package org.parboiled.transform.method;

import com.google.common.collect.ImmutableMap;
import org.objectweb.asm.Type;
import org.parboiled.annotations.Cached;
import org.parboiled.annotations.DontExtend;
import org.parboiled.annotations.DontLabel;
import org.parboiled.annotations.DontSkipActionsInPredicates;
import org.parboiled.annotations.ExplicitActionsOnly;
import org.parboiled.annotations.MemoMismatches;
import org.parboiled.annotations.SkipActionsInPredicates;
import org.parboiled.annotations.SkipNode;
import org.parboiled.annotations.SuppressNode;
import org.parboiled.annotations.SuppressSubnodes;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;

/**
 * Enumeration associating rule method annotations to their ASM descriptor
 *
 * @see Type#getDescriptor()
 * @see org.parboiled.annotations
 */
public enum RuleAnnotation
{
    EXPLICIT_ACTIONS_ONLY(ExplicitActionsOnly.class),
    CACHED(Cached.class),
    SUPPRESS_NODE(SuppressNode.class),
    SUPPRESS_SUBNODES(SuppressSubnodes.class),
    SKIP_NODE(SkipNode.class),
    MEMO_MISMATCHES(MemoMismatches.class),
    SKIP_ACTIONS_IN_PREDICATES(SkipActionsInPredicates.class),
    DONT_SKIP_ACTIONS_IN_PREDICATES(DontSkipActionsInPredicates.class),
    DONT_LABEL(DontLabel.class),
    DONT_EXTEND(DontExtend.class)
    ;

    private static final Map<String, RuleAnnotation>
        REVERSE_MAP;

    static {
        final ImmutableMap.Builder<String, RuleAnnotation> builder
            = ImmutableMap.builder();

        for (final RuleAnnotation entry: values())
            builder.put(entry.descriptor, entry);

        REVERSE_MAP = builder.build();
    }


    private final String descriptor;

    RuleAnnotation(final Class<? extends Annotation> c)
    {
        descriptor = Type.getType(c).getDescriptor();
    }

    /**
     * Record an enumeration value into a set if the descriptor is known
     *
     * @param set the set to record into
     * @param desc the descriptor
     * @return true if the descriptor is known
     */
    public static boolean recordDesc(final Set<RuleAnnotation> set,
        final String desc)
    {
        final RuleAnnotation annotation = REVERSE_MAP.get(desc);
        if (annotation == null)
            return false;
        set.add(annotation);
        return true;
    }
}

@@ -605,16 +605,59 @@
 ubnodes;
+%0Aimport org.parboiled.transform.RuleMethod;
 %0A%0Aimport
@@ -690,16 +690,42 @@
 tation;%0A
+import java.util.EnumSet;%0A
 import j
@@ -1388,16 +1388,640 @@
 %0A    ;%0A%0A
+    /**%0A     * @see RuleMethod#moveFlagsTo(RuleMethod)%0A     */%0A    private static final Set%3CRuleAnnotation%3E FLAGS_COPY = EnumSet.of(%0A        CACHED, DONT_LABEL, SUPPRESS_NODE, SUPPRESS_SUBNODES,%0A        SKIP_NODE, MEMO_MISMATCHES%0A    );%0A%0A    /**%0A     * @see RuleMethod#moveFlagsTo(RuleMethod)%0A     */%0A    private static final Set%3CRuleAnnotation%3E FLAGS_CLEAR = EnumSet.of(%0A        CACHED, SUPPRESS_NODE, SUPPRESS_SUBNODES, SKIP_NODE,%0A        MEMO_MISMATCHES%0A    );%0A%0A    /**%0A     * @see RuleMethod#moveFlagsTo(RuleMethod)%0A     */%0A    private static final Set%3CRuleAnnotation%3E FLAGS_SET = EnumSet.of(%0A        DONT_LABEL%0A    );%0A%0A
     priv
@@ -2987,16 +2987,16 @@
 ation);%0A
-
         
@@ -3013,11 +3013,489 @@
 e;%0A    %7D
+%0A%0A    /**%0A     * @see RuleMethod#moveFlagsTo(RuleMethod)%0A     *%0A     * @param from set to move flags from%0A     * @param  to set to move flags to%0A     */%0A    public static void moveTo(final Set%3CRuleAnnotation%3E from,%0A        final Set%3CRuleAnnotation%3E to)%0A    %7B%0A        final Set%3CRuleAnnotation%3E transferred = EnumSet.copyOf(from);%0A        transferred.retainAll(FLAGS_COPY);%0A        to.addAll(transferred);%0A        from.addAll(FLAGS_SET);%0A        from.removeAll(FLAGS_CLEAR);%0A    %7D
 %0A%7D%0A

add method to move flags from one RuleMethod to another