You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by ka...@apache.org on 2014/03/20 20:23:08 UTC

[03/23] FIXED - TAP5-2214: Make tapestry5 java8 compatible - apply source from AMS 5.0 final source release and change package name to org.apache.tapestry5.internal.plastic.asm. Also made a single change to ASM source, promoted visibility of AnnotationNo

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1acd9d22/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
index 0d7fe3f..859a8d1 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
@@ -1,7 +1,6 @@
 /***
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
- * Copyright (c) 2011 Google
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,12 +29,18 @@
  */
 package org.apache.tapestry5.internal.plastic.asm.util;
 
-import org.apache.tapestry5.internal.plastic.asm.*;
+import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Attribute;
+import org.apache.tapestry5.internal.plastic.asm.Handle;
+import org.apache.tapestry5.internal.plastic.asm.Label;
+import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
 
 /**
  * A {@link MethodVisitor} that prints the methods it visits with a
  * {@link Printer}.
- *
+ * 
  * @author Eric Bruneton
  */
 public final class TraceMethodVisitor extends MethodVisitor {
@@ -47,15 +52,19 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     public TraceMethodVisitor(final MethodVisitor mv, final Printer p) {
-        super(Opcodes.ASM4, mv);
+        super(Opcodes.ASM5, mv);
         this.p = p;
     }
 
     @Override
-    public AnnotationVisitor visitAnnotation(
-        final String desc,
-        final boolean visible)
-    {
+    public void visitParameter(String name, int access) {
+        p.visitParameter(name, access);
+        super.visitParameter(name, access);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String desc,
+            final boolean visible) {
         Printer p = this.p.visitMethodAnnotation(desc, visible);
         AnnotationVisitor av = mv == null ? null : mv.visitAnnotation(desc,
                 visible);
@@ -63,6 +72,16 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
+    public AnnotationVisitor visitTypeAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        Printer p = this.p.visitMethodTypeAnnotation(typeRef, typePath, desc,
+                visible);
+        AnnotationVisitor av = mv == null ? null : mv.visitTypeAnnotation(
+                typeRef, typePath, desc, visible);
+        return new TraceAnnotationVisitor(av, p);
+    }
+
+    @Override
     public void visitAttribute(final Attribute attr) {
         p.visitMethodAttribute(attr);
         super.visitAttribute(attr);
@@ -76,17 +95,11 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
-    public AnnotationVisitor visitParameterAnnotation(
-        final int parameter,
-        final String desc,
-        final boolean visible)
-    {
-        Printer p = this.p.visitParameterAnnotation(parameter,
-                desc,
-                visible);
-        AnnotationVisitor av = mv == null
-                ? null
-                : mv.visitParameterAnnotation(parameter, desc, visible);
+    public AnnotationVisitor visitParameterAnnotation(final int parameter,
+            final String desc, final boolean visible) {
+        Printer p = this.p.visitParameterAnnotation(parameter, desc, visible);
+        AnnotationVisitor av = mv == null ? null : mv.visitParameterAnnotation(
+                parameter, desc, visible);
         return new TraceAnnotationVisitor(av, p);
     }
 
@@ -97,13 +110,8 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
-    public void visitFrame(
-        final int type,
-        final int nLocal,
-        final Object[] local,
-        final int nStack,
-        final Object[] stack)
-    {
+    public void visitFrame(final int type, final int nLocal,
+            final Object[] local, final int nStack, final Object[] stack) {
         p.visitFrame(type, nLocal, local, nStack, stack);
         super.visitFrame(type, nLocal, local, nStack, stack);
     }
@@ -133,34 +141,42 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
-    public void visitFieldInsn(
-        final int opcode,
-        final String owner,
-        final String name,
-        final String desc)
-    {
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
         p.visitFieldInsn(opcode, owner, name, desc);
         super.visitFieldInsn(opcode, owner, name, desc);
     }
 
+    @Deprecated
     @Override
-    public void visitMethodInsn(
-        final int opcode,
-        final String owner,
-        final String name,
-        final String desc)
-    {
+    public void visitMethodInsn(int opcode, String owner, String name,
+            String desc) {
+        if (api >= Opcodes.ASM5) {
+            super.visitMethodInsn(opcode, owner, name, desc);
+            return;
+        }
         p.visitMethodInsn(opcode, owner, name, desc);
-        super.visitMethodInsn(opcode, owner, name, desc);
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name,
+            String desc, boolean itf) {
+        if (api < Opcodes.ASM5) {
+            super.visitMethodInsn(opcode, owner, name, desc, itf);
+            return;
+        }
+        p.visitMethodInsn(opcode, owner, name, desc, itf);
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc, itf);
+        }
     }
 
     @Override
-    public void visitInvokeDynamicInsn(
-        String name,
-        String desc,
-        Handle bsm,
-        Object... bsmArgs)
-    {
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
         p.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
         super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
     }
@@ -190,22 +206,15 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
-    public void visitTableSwitchInsn(
-        final int min,
-        final int max,
-        final Label dflt,
-        final Label... labels)
-    {
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
         p.visitTableSwitchInsn(min, max, dflt, labels);
         super.visitTableSwitchInsn(min, max, dflt, labels);
     }
 
     @Override
-    public void visitLookupSwitchInsn(
-        final Label dflt,
-        final int[] keys,
-        final Label[] labels)
-    {
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
         p.visitLookupSwitchInsn(dflt, keys, labels);
         super.visitLookupSwitchInsn(dflt, keys, labels);
     }
@@ -217,30 +226,53 @@ public final class TraceMethodVisitor extends MethodVisitor {
     }
 
     @Override
-    public void visitTryCatchBlock(
-        final Label start,
-        final Label end,
-        final Label handler,
-        final String type)
-    {
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        Printer p = this.p
+                .visitInsnAnnotation(typeRef, typePath, desc, visible);
+        AnnotationVisitor av = mv == null ? null : mv.visitInsnAnnotation(
+                typeRef, typePath, desc, visible);
+        return new TraceAnnotationVisitor(av, p);
+    }
+
+    @Override
+    public void visitTryCatchBlock(final Label start, final Label end,
+            final Label handler, final String type) {
         p.visitTryCatchBlock(start, end, handler, type);
         super.visitTryCatchBlock(start, end, handler, type);
     }
 
     @Override
-    public void visitLocalVariable(
-        final String name,
-        final String desc,
-        final String signature,
-        final Label start,
-        final Label end,
-        final int index)
-    {
+    public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        Printer p = this.p.visitTryCatchAnnotation(typeRef, typePath, desc,
+                visible);
+        AnnotationVisitor av = mv == null ? null : mv.visitTryCatchAnnotation(
+                typeRef, typePath, desc, visible);
+        return new TraceAnnotationVisitor(av, p);
+    }
+
+    @Override
+    public void visitLocalVariable(final String name, final String desc,
+            final String signature, final Label start, final Label end,
+            final int index) {
         p.visitLocalVariable(name, desc, signature, start, end, index);
         super.visitLocalVariable(name, desc, signature, start, end, index);
     }
 
     @Override
+    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+            TypePath typePath, Label[] start, Label[] end, int[] index,
+            String desc, boolean visible) {
+        Printer p = this.p.visitLocalVariableAnnotation(typeRef, typePath,
+                start, end, index, desc, visible);
+        AnnotationVisitor av = mv == null ? null : mv
+                .visitLocalVariableAnnotation(typeRef, typePath, start, end,
+                        index, desc, visible);
+        return new TraceAnnotationVisitor(av, p);
+    }
+
+    @Override
     public void visitLineNumber(final int line, final Label start) {
         p.visitLineNumber(line, start);
         super.visitLineNumber(line, start);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1acd9d22/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
index 3b0a178..1e2c9a3 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
@@ -33,14 +33,13 @@ import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.signature.SignatureVisitor;
 
 /**
- * A {@link org.apache.tapestry5.internal.plastic.asm.signature.SignatureVisitor} that prints a disassembled view of the signature
+ * A {@link SignatureVisitor} that prints a disassembled view of the signature
  * it visits.
- *
+ * 
  * @author Eugene Kuleshov
  * @author Eric Bruneton
  */
-public final class TraceSignatureVisitor extends SignatureVisitor
-{
+public final class TraceSignatureVisitor extends SignatureVisitor {
 
     private final StringBuffer declaration;
 
@@ -76,13 +75,13 @@ public final class TraceSignatureVisitor extends SignatureVisitor
     private String separator = "";
 
     public TraceSignatureVisitor(final int access) {
-        super(Opcodes.ASM4);
+        super(Opcodes.ASM5);
         isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
         this.declaration = new StringBuffer();
     }
 
     private TraceSignatureVisitor(final StringBuffer buf) {
-        super(Opcodes.ASM4);
+        super(Opcodes.ASM5);
         this.declaration = buf;
     }
 
@@ -118,8 +117,7 @@ public final class TraceSignatureVisitor extends SignatureVisitor
 
     @Override
     public SignatureVisitor visitInterface() {
-        separator = seenInterface ? ", " : isInterface
-                ? " extends "
+        separator = seenInterface ? ", " : isInterface ? " extends "
                 : " implements ";
         seenInterface = true;
         startType();
@@ -166,34 +164,34 @@ public final class TraceSignatureVisitor extends SignatureVisitor
     @Override
     public void visitBaseType(final char descriptor) {
         switch (descriptor) {
-            case 'V':
-                declaration.append("void");
-                break;
-            case 'B':
-                declaration.append("byte");
-                break;
-            case 'J':
-                declaration.append("long");
-                break;
-            case 'Z':
-                declaration.append("boolean");
-                break;
-            case 'I':
-                declaration.append("int");
-                break;
-            case 'S':
-                declaration.append("short");
-                break;
-            case 'C':
-                declaration.append("char");
-                break;
-            case 'F':
-                declaration.append("float");
-                break;
-            // case 'D':
-            default:
-                declaration.append("double");
-                break;
+        case 'V':
+            declaration.append("void");
+            break;
+        case 'B':
+            declaration.append("byte");
+            break;
+        case 'J':
+            declaration.append("long");
+            break;
+        case 'Z':
+            declaration.append("boolean");
+            break;
+        case 'I':
+            declaration.append("int");
+            break;
+        case 'S':
+            declaration.append("short");
+            break;
+        case 'C':
+            declaration.append("char");
+            break;
+        case 'F':
+            declaration.append("float");
+            break;
+        // case 'D':
+        default:
+            declaration.append("double");
+            break;
         }
         endType();
     }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1acd9d22/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/ASMContentHandler.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/ASMContentHandler.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/ASMContentHandler.java
new file mode 100644
index 0000000..88dab5e
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/ASMContentHandler.java
@@ -0,0 +1,1453 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004-2011, Eugene Kuleshov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.apache.tapestry5.internal.plastic.asm.xml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
+import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
+import org.apache.tapestry5.internal.plastic.asm.FieldVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Handle;
+import org.apache.tapestry5.internal.plastic.asm.Label;
+import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.Type;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A {@link org.xml.sax.ContentHandler ContentHandler} that transforms XML
+ * document into Java class file. This class can be feeded by any kind of SAX
+ * 2.0 event producers, e.g. XML parser, XSLT or XPath engines, or custom code.
+ * 
+ * @see org.apache.tapestry5.internal.plastic.asm.xml.SAXClassAdapter
+ * @see org.apache.tapestry5.internal.plastic.asm.xml.Processor
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ASMContentHandler extends DefaultHandler implements Opcodes {
+
+    /**
+     * Stack of the intermediate processing contexts.
+     */
+    private final ArrayList<Object> stack = new ArrayList<Object>();
+
+    /**
+     * Complete name of the current element.
+     */
+    String match = "";
+
+    /**
+     * Current instance of the {@link ClassVisitor ClassVisitor} used to visit
+     * classfile bytecode.
+     */
+    protected ClassVisitor cv;
+
+    /**
+     * Map of the active {@link Label Label} instances for current method.
+     */
+    protected Map<Object, Label> labels;
+
+    private static final String BASE = "class";
+
+    private final RuleSet RULES = new RuleSet();
+    {
+        RULES.add(BASE, new ClassRule());
+        RULES.add(BASE + "/interfaces/interface", new InterfaceRule());
+        RULES.add(BASE + "/interfaces", new InterfacesRule());
+        RULES.add(BASE + "/outerclass", new OuterClassRule());
+        RULES.add(BASE + "/innerclass", new InnerClassRule());
+        RULES.add(BASE + "/source", new SourceRule());
+        RULES.add(BASE + "/field", new FieldRule());
+
+        RULES.add(BASE + "/method", new MethodRule());
+        RULES.add(BASE + "/method/exceptions/exception", new ExceptionRule());
+        RULES.add(BASE + "/method/exceptions", new ExceptionsRule());
+
+        RULES.add(BASE + "/method/parameter", new MethodParameterRule());
+        RULES.add(BASE + "/method/annotationDefault",
+                new AnnotationDefaultRule());
+
+        RULES.add(BASE + "/method/code/*", new OpcodesRule()); // opcodes
+
+        RULES.add(BASE + "/method/code/frame", new FrameRule());
+        RULES.add(BASE + "/method/code/frame/local", new FrameTypeRule());
+        RULES.add(BASE + "/method/code/frame/stack", new FrameTypeRule());
+
+        RULES.add(BASE + "/method/code/TABLESWITCH", new TableSwitchRule());
+        RULES.add(BASE + "/method/code/TABLESWITCH/label",
+                new TableSwitchLabelRule());
+        RULES.add(BASE + "/method/code/LOOKUPSWITCH", new LookupSwitchRule());
+        RULES.add(BASE + "/method/code/LOOKUPSWITCH/label",
+                new LookupSwitchLabelRule());
+
+        RULES.add(BASE + "/method/code/INVOKEDYNAMIC", new InvokeDynamicRule());
+        RULES.add(BASE + "/method/code/INVOKEDYNAMIC/bsmArg",
+                new InvokeDynamicBsmArgumentsRule());
+
+        RULES.add(BASE + "/method/code/Label", new LabelRule());
+        RULES.add(BASE + "/method/code/TryCatch", new TryCatchRule());
+        RULES.add(BASE + "/method/code/LineNumber", new LineNumberRule());
+        RULES.add(BASE + "/method/code/LocalVar", new LocalVarRule());
+        RULES.add(BASE + "/method/code/Max", new MaxRule());
+
+        RULES.add("*/annotation", new AnnotationRule());
+        RULES.add("*/typeAnnotation", new TypeAnnotationRule());
+        RULES.add("*/parameterAnnotation", new AnnotationParameterRule());
+        RULES.add("*/insnAnnotation", new InsnAnnotationRule());
+        RULES.add("*/tryCatchAnnotation", new TryCatchAnnotationRule());
+        RULES.add("*/localVariableAnnotation",
+                new LocalVariableAnnotationRule());
+        RULES.add("*/annotationValue", new AnnotationValueRule());
+        RULES.add("*/annotationValueAnnotation",
+                new AnnotationValueAnnotationRule());
+        RULES.add("*/annotationValueEnum", new AnnotationValueEnumRule());
+        RULES.add("*/annotationValueArray", new AnnotationValueArrayRule());
+    }
+
+    private static interface OpcodeGroup {
+        public static final int INSN = 0;
+        public static final int INSN_INT = 1;
+        public static final int INSN_VAR = 2;
+        public static final int INSN_TYPE = 3;
+        public static final int INSN_FIELD = 4;
+        public static final int INSN_METHOD = 5;
+        public static final int INSN_JUMP = 6;
+        public static final int INSN_LDC = 7;
+        public static final int INSN_IINC = 8;
+        public static final int INSN_MULTIANEWARRAY = 9;
+    }
+
+    /**
+     * Map of the opcode names to opcode and opcode group
+     */
+    static final HashMap<String, Opcode> OPCODES = new HashMap<String, Opcode>();
+    static {
+        addOpcode("NOP", NOP, OpcodeGroup.INSN);
+        addOpcode("ACONST_NULL", ACONST_NULL, OpcodeGroup.INSN);
+        addOpcode("ICONST_M1", ICONST_M1, OpcodeGroup.INSN);
+        addOpcode("ICONST_0", ICONST_0, OpcodeGroup.INSN);
+        addOpcode("ICONST_1", ICONST_1, OpcodeGroup.INSN);
+        addOpcode("ICONST_2", ICONST_2, OpcodeGroup.INSN);
+        addOpcode("ICONST_3", ICONST_3, OpcodeGroup.INSN);
+        addOpcode("ICONST_4", ICONST_4, OpcodeGroup.INSN);
+        addOpcode("ICONST_5", ICONST_5, OpcodeGroup.INSN);
+        addOpcode("LCONST_0", LCONST_0, OpcodeGroup.INSN);
+        addOpcode("LCONST_1", LCONST_1, OpcodeGroup.INSN);
+        addOpcode("FCONST_0", FCONST_0, OpcodeGroup.INSN);
+        addOpcode("FCONST_1", FCONST_1, OpcodeGroup.INSN);
+        addOpcode("FCONST_2", FCONST_2, OpcodeGroup.INSN);
+        addOpcode("DCONST_0", DCONST_0, OpcodeGroup.INSN);
+        addOpcode("DCONST_1", DCONST_1, OpcodeGroup.INSN);
+        addOpcode("BIPUSH", BIPUSH, OpcodeGroup.INSN_INT);
+        addOpcode("SIPUSH", SIPUSH, OpcodeGroup.INSN_INT);
+        addOpcode("LDC", LDC, OpcodeGroup.INSN_LDC);
+        addOpcode("ILOAD", ILOAD, OpcodeGroup.INSN_VAR);
+        addOpcode("LLOAD", LLOAD, OpcodeGroup.INSN_VAR);
+        addOpcode("FLOAD", FLOAD, OpcodeGroup.INSN_VAR);
+        addOpcode("DLOAD", DLOAD, OpcodeGroup.INSN_VAR);
+        addOpcode("ALOAD", ALOAD, OpcodeGroup.INSN_VAR);
+        addOpcode("IALOAD", IALOAD, OpcodeGroup.INSN);
+        addOpcode("LALOAD", LALOAD, OpcodeGroup.INSN);
+        addOpcode("FALOAD", FALOAD, OpcodeGroup.INSN);
+        addOpcode("DALOAD", DALOAD, OpcodeGroup.INSN);
+        addOpcode("AALOAD", AALOAD, OpcodeGroup.INSN);
+        addOpcode("BALOAD", BALOAD, OpcodeGroup.INSN);
+        addOpcode("CALOAD", CALOAD, OpcodeGroup.INSN);
+        addOpcode("SALOAD", SALOAD, OpcodeGroup.INSN);
+        addOpcode("ISTORE", ISTORE, OpcodeGroup.INSN_VAR);
+        addOpcode("LSTORE", LSTORE, OpcodeGroup.INSN_VAR);
+        addOpcode("FSTORE", FSTORE, OpcodeGroup.INSN_VAR);
+        addOpcode("DSTORE", DSTORE, OpcodeGroup.INSN_VAR);
+        addOpcode("ASTORE", ASTORE, OpcodeGroup.INSN_VAR);
+        addOpcode("IASTORE", IASTORE, OpcodeGroup.INSN);
+        addOpcode("LASTORE", LASTORE, OpcodeGroup.INSN);
+        addOpcode("FASTORE", FASTORE, OpcodeGroup.INSN);
+        addOpcode("DASTORE", DASTORE, OpcodeGroup.INSN);
+        addOpcode("AASTORE", AASTORE, OpcodeGroup.INSN);
+        addOpcode("BASTORE", BASTORE, OpcodeGroup.INSN);
+        addOpcode("CASTORE", CASTORE, OpcodeGroup.INSN);
+        addOpcode("SASTORE", SASTORE, OpcodeGroup.INSN);
+        addOpcode("POP", POP, OpcodeGroup.INSN);
+        addOpcode("POP2", POP2, OpcodeGroup.INSN);
+        addOpcode("DUP", DUP, OpcodeGroup.INSN);
+        addOpcode("DUP_X1", DUP_X1, OpcodeGroup.INSN);
+        addOpcode("DUP_X2", DUP_X2, OpcodeGroup.INSN);
+        addOpcode("DUP2", DUP2, OpcodeGroup.INSN);
+        addOpcode("DUP2_X1", DUP2_X1, OpcodeGroup.INSN);
+        addOpcode("DUP2_X2", DUP2_X2, OpcodeGroup.INSN);
+        addOpcode("SWAP", SWAP, OpcodeGroup.INSN);
+        addOpcode("IADD", IADD, OpcodeGroup.INSN);
+        addOpcode("LADD", LADD, OpcodeGroup.INSN);
+        addOpcode("FADD", FADD, OpcodeGroup.INSN);
+        addOpcode("DADD", DADD, OpcodeGroup.INSN);
+        addOpcode("ISUB", ISUB, OpcodeGroup.INSN);
+        addOpcode("LSUB", LSUB, OpcodeGroup.INSN);
+        addOpcode("FSUB", FSUB, OpcodeGroup.INSN);
+        addOpcode("DSUB", DSUB, OpcodeGroup.INSN);
+        addOpcode("IMUL", IMUL, OpcodeGroup.INSN);
+        addOpcode("LMUL", LMUL, OpcodeGroup.INSN);
+        addOpcode("FMUL", FMUL, OpcodeGroup.INSN);
+        addOpcode("DMUL", DMUL, OpcodeGroup.INSN);
+        addOpcode("IDIV", IDIV, OpcodeGroup.INSN);
+        addOpcode("LDIV", LDIV, OpcodeGroup.INSN);
+        addOpcode("FDIV", FDIV, OpcodeGroup.INSN);
+        addOpcode("DDIV", DDIV, OpcodeGroup.INSN);
+        addOpcode("IREM", IREM, OpcodeGroup.INSN);
+        addOpcode("LREM", LREM, OpcodeGroup.INSN);
+        addOpcode("FREM", FREM, OpcodeGroup.INSN);
+        addOpcode("DREM", DREM, OpcodeGroup.INSN);
+        addOpcode("INEG", INEG, OpcodeGroup.INSN);
+        addOpcode("LNEG", LNEG, OpcodeGroup.INSN);
+        addOpcode("FNEG", FNEG, OpcodeGroup.INSN);
+        addOpcode("DNEG", DNEG, OpcodeGroup.INSN);
+        addOpcode("ISHL", ISHL, OpcodeGroup.INSN);
+        addOpcode("LSHL", LSHL, OpcodeGroup.INSN);
+        addOpcode("ISHR", ISHR, OpcodeGroup.INSN);
+        addOpcode("LSHR", LSHR, OpcodeGroup.INSN);
+        addOpcode("IUSHR", IUSHR, OpcodeGroup.INSN);
+        addOpcode("LUSHR", LUSHR, OpcodeGroup.INSN);
+        addOpcode("IAND", IAND, OpcodeGroup.INSN);
+        addOpcode("LAND", LAND, OpcodeGroup.INSN);
+        addOpcode("IOR", IOR, OpcodeGroup.INSN);
+        addOpcode("LOR", LOR, OpcodeGroup.INSN);
+        addOpcode("IXOR", IXOR, OpcodeGroup.INSN);
+        addOpcode("LXOR", LXOR, OpcodeGroup.INSN);
+        addOpcode("IINC", IINC, OpcodeGroup.INSN_IINC);
+        addOpcode("I2L", I2L, OpcodeGroup.INSN);
+        addOpcode("I2F", I2F, OpcodeGroup.INSN);
+        addOpcode("I2D", I2D, OpcodeGroup.INSN);
+        addOpcode("L2I", L2I, OpcodeGroup.INSN);
+        addOpcode("L2F", L2F, OpcodeGroup.INSN);
+        addOpcode("L2D", L2D, OpcodeGroup.INSN);
+        addOpcode("F2I", F2I, OpcodeGroup.INSN);
+        addOpcode("F2L", F2L, OpcodeGroup.INSN);
+        addOpcode("F2D", F2D, OpcodeGroup.INSN);
+        addOpcode("D2I", D2I, OpcodeGroup.INSN);
+        addOpcode("D2L", D2L, OpcodeGroup.INSN);
+        addOpcode("D2F", D2F, OpcodeGroup.INSN);
+        addOpcode("I2B", I2B, OpcodeGroup.INSN);
+        addOpcode("I2C", I2C, OpcodeGroup.INSN);
+        addOpcode("I2S", I2S, OpcodeGroup.INSN);
+        addOpcode("LCMP", LCMP, OpcodeGroup.INSN);
+        addOpcode("FCMPL", FCMPL, OpcodeGroup.INSN);
+        addOpcode("FCMPG", FCMPG, OpcodeGroup.INSN);
+        addOpcode("DCMPL", DCMPL, OpcodeGroup.INSN);
+        addOpcode("DCMPG", DCMPG, OpcodeGroup.INSN);
+        addOpcode("IFEQ", IFEQ, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFNE", IFNE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFLT", IFLT, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFGE", IFGE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFGT", IFGT, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFLE", IFLE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPEQ", IF_ICMPEQ, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPNE", IF_ICMPNE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPLT", IF_ICMPLT, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPGE", IF_ICMPGE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPGT", IF_ICMPGT, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ICMPLE", IF_ICMPLE, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ACMPEQ", IF_ACMPEQ, OpcodeGroup.INSN_JUMP);
+        addOpcode("IF_ACMPNE", IF_ACMPNE, OpcodeGroup.INSN_JUMP);
+        addOpcode("GOTO", GOTO, OpcodeGroup.INSN_JUMP);
+        addOpcode("JSR", JSR, OpcodeGroup.INSN_JUMP);
+        addOpcode("RET", RET, OpcodeGroup.INSN_VAR);
+        addOpcode("IRETURN", IRETURN, OpcodeGroup.INSN);
+        addOpcode("LRETURN", LRETURN, OpcodeGroup.INSN);
+        addOpcode("FRETURN", FRETURN, OpcodeGroup.INSN);
+        addOpcode("DRETURN", DRETURN, OpcodeGroup.INSN);
+        addOpcode("ARETURN", ARETURN, OpcodeGroup.INSN);
+        addOpcode("RETURN", RETURN, OpcodeGroup.INSN);
+        addOpcode("GETSTATIC", GETSTATIC, OpcodeGroup.INSN_FIELD);
+        addOpcode("PUTSTATIC", PUTSTATIC, OpcodeGroup.INSN_FIELD);
+        addOpcode("GETFIELD", GETFIELD, OpcodeGroup.INSN_FIELD);
+        addOpcode("PUTFIELD", PUTFIELD, OpcodeGroup.INSN_FIELD);
+        addOpcode("INVOKEVIRTUAL", INVOKEVIRTUAL, OpcodeGroup.INSN_METHOD);
+        addOpcode("INVOKESPECIAL", INVOKESPECIAL, OpcodeGroup.INSN_METHOD);
+        addOpcode("INVOKESTATIC", INVOKESTATIC, OpcodeGroup.INSN_METHOD);
+        addOpcode("INVOKEINTERFACE", INVOKEINTERFACE, OpcodeGroup.INSN_METHOD);
+        addOpcode("NEW", NEW, OpcodeGroup.INSN_TYPE);
+        addOpcode("NEWARRAY", NEWARRAY, OpcodeGroup.INSN_INT);
+        addOpcode("ANEWARRAY", ANEWARRAY, OpcodeGroup.INSN_TYPE);
+        addOpcode("ARRAYLENGTH", ARRAYLENGTH, OpcodeGroup.INSN);
+        addOpcode("ATHROW", ATHROW, OpcodeGroup.INSN);
+        addOpcode("CHECKCAST", CHECKCAST, OpcodeGroup.INSN_TYPE);
+        addOpcode("INSTANCEOF", INSTANCEOF, OpcodeGroup.INSN_TYPE);
+        addOpcode("MONITORENTER", MONITORENTER, OpcodeGroup.INSN);
+        addOpcode("MONITOREXIT", MONITOREXIT, OpcodeGroup.INSN);
+        addOpcode("MULTIANEWARRAY", MULTIANEWARRAY,
+                OpcodeGroup.INSN_MULTIANEWARRAY);
+        addOpcode("IFNULL", IFNULL, OpcodeGroup.INSN_JUMP);
+        addOpcode("IFNONNULL", IFNONNULL, OpcodeGroup.INSN_JUMP);
+    }
+
+    private static void addOpcode(String operStr, int oper, int group) {
+        OPCODES.put(operStr, new Opcode(oper, group));
+    }
+
+    static final HashMap<String, Integer> TYPES = new HashMap<String, Integer>();
+    static {
+        String[] types = SAXCodeAdapter.TYPES;
+        for (int i = 0; i < types.length; i++) {
+            TYPES.put(types[i], new Integer(i));
+        }
+    }
+
+    /**
+     * Constructs a new {@link ASMContentHandler ASMContentHandler} object.
+     * 
+     * @param cv
+     *            class visitor that will be called to reconstruct the classfile
+     *            using the XML stream.
+     */
+    public ASMContentHandler(final ClassVisitor cv) {
+        this.cv = cv;
+    }
+
+    /**
+     * Process notification of the start of an XML element being reached.
+     * 
+     * @param ns
+     *            - The Namespace URI, or the empty string if the element has no
+     *            Namespace URI or if Namespace processing is not being
+     *            performed.
+     * @param lName
+     *            - The local name (without prefix), or the empty string if
+     *            Namespace processing is not being performed.
+     * @param qName
+     *            - The qualified name (with prefix), or the empty string if
+     *            qualified names are not available.
+     * @param list
+     *            - The attributes attached to the element. If there are no
+     *            attributes, it shall be an empty Attributes object.
+     * @exception SAXException
+     *                if a parsing error is to be reported
+     */
+    @Override
+    public final void startElement(final String ns, final String lName,
+            final String qName, final Attributes list) throws SAXException {
+        // the actual element name is either in lName or qName, depending
+        // on whether the parser is namespace aware
+        String name = lName == null || lName.length() == 0 ? qName : lName;
+
+        // Compute the current matching rule
+        StringBuffer sb = new StringBuffer(match);
+        if (match.length() > 0) {
+            sb.append('/');
+        }
+        sb.append(name);
+        match = sb.toString();
+
+        // Fire "begin" events for all relevant rules
+        Rule r = (Rule) RULES.match(match);
+        if (r != null) {
+            r.begin(name, list);
+        }
+    }
+
+    /**
+     * Process notification of the end of an XML element being reached.
+     * 
+     * @param ns
+     *            - The Namespace URI, or the empty string if the element has no
+     *            Namespace URI or if Namespace processing is not being
+     *            performed.
+     * @param lName
+     *            - The local name (without prefix), or the empty string if
+     *            Namespace processing is not being performed.
+     * @param qName
+     *            - The qualified XML 1.0 name (with prefix), or the empty
+     *            string if qualified names are not available.
+     * 
+     * @exception SAXException
+     *                if a parsing error is to be reported
+     */
+    @Override
+    public final void endElement(final String ns, final String lName,
+            final String qName) throws SAXException {
+        // the actual element name is either in lName or qName, depending
+        // on whether the parser is namespace aware
+        String name = lName == null || lName.length() == 0 ? qName : lName;
+
+        // Fire "end" events for all relevant rules in reverse order
+        Rule r = (Rule) RULES.match(match);
+        if (r != null) {
+            r.end(name);
+        }
+
+        // Recover the previous match expression
+        int slash = match.lastIndexOf('/');
+        if (slash >= 0) {
+            match = match.substring(0, slash);
+        } else {
+            match = "";
+        }
+    }
+
+    /**
+     * Return the top object on the stack without removing it. If there are no
+     * objects on the stack, return <code>null</code>.
+     * 
+     * @return the top object on the stack without removing it.
+     */
+    final Object peek() {
+        int size = stack.size();
+        return size == 0 ? null : stack.get(size - 1);
+    }
+
+    /**
+     * Pop the top object off of the stack, and return it. If there are no
+     * objects on the stack, return <code>null</code>.
+     * 
+     * @return the top object off of the stack.
+     */
+    final Object pop() {
+        int size = stack.size();
+        return size == 0 ? null : stack.remove(size - 1);
+    }
+
+    /**
+     * Push a new object onto the top of the object stack.
+     * 
+     * @param object
+     *            The new object
+     */
+    final void push(final Object object) {
+        stack.add(object);
+    }
+
+    static final class RuleSet {
+
+        private final HashMap<String, Object> rules = new HashMap<String, Object>();
+
+        private final ArrayList<String> lpatterns = new ArrayList<String>();
+
+        private final ArrayList<String> rpatterns = new ArrayList<String>();
+
+        public void add(final String path, final Object rule) {
+            String pattern = path;
+            if (path.startsWith("*/")) {
+                pattern = path.substring(1);
+                lpatterns.add(pattern);
+            } else if (path.endsWith("/*")) {
+                pattern = path.substring(0, path.length() - 1);
+                rpatterns.add(pattern);
+            }
+            rules.put(pattern, rule);
+        }
+
+        public Object match(final String path) {
+            if (rules.containsKey(path)) {
+                return rules.get(path);
+            }
+
+            int n = path.lastIndexOf('/');
+            for (Iterator<String> it = lpatterns.iterator(); it.hasNext();) {
+                String pattern = it.next();
+                if (path.substring(n).endsWith(pattern)) {
+                    return rules.get(pattern);
+                }
+            }
+
+            for (Iterator<String> it = rpatterns.iterator(); it.hasNext();) {
+                String pattern = it.next();
+                if (path.startsWith(pattern)) {
+                    return rules.get(pattern);
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Rule
+     */
+    protected abstract class Rule {
+
+        public void begin(final String name, final Attributes attrs)
+                throws SAXException {
+        }
+
+        public void end(final String name) {
+        }
+
+        protected final Object getValue(final String desc, final String val)
+                throws SAXException {
+            Object value = null;
+            if (val != null) {
+                if ("Ljava/lang/String;".equals(desc)) {
+                    value = decode(val);
+                } else if ("Ljava/lang/Integer;".equals(desc)
+                        || "I".equals(desc) || "S".equals(desc)
+                        || "B".equals(desc) || "C".equals(desc)
+                        || "Z".equals(desc)) {
+                    value = new Integer(val);
+
+                } else if ("Ljava/lang/Short;".equals(desc)) {
+                    value = new Short(val);
+
+                } else if ("Ljava/lang/Byte;".equals(desc)) {
+                    value = new Byte(val);
+
+                } else if ("Ljava/lang/Character;".equals(desc)) {
+                    value = new Character(decode(val).charAt(0));
+
+                } else if ("Ljava/lang/Boolean;".equals(desc)) {
+                    value = Boolean.valueOf(val);
+
+                } else if ("Ljava/lang/Long;".equals(desc) || "J".equals(desc)) {
+                    value = new Long(val);
+                } else if ("Ljava/lang/Float;".equals(desc) || "F".equals(desc)) {
+                    value = new Float(val);
+                } else if ("Ljava/lang/Double;".equals(desc)
+                        || "D".equals(desc)) {
+                    value = new Double(val);
+                } else if (Type.getDescriptor(Type.class).equals(desc)) {
+                    value = Type.getType(val);
+
+                } else if (Type.getDescriptor(Handle.class).equals(desc)) {
+                    value = decodeHandle(val);
+
+                } else {
+                    // TODO use of default toString().
+                    throw new SAXException("Invalid value:" + val + " desc:"
+                            + desc + " ctx:" + this);
+                }
+            }
+            return value;
+        }
+
+        Handle decodeHandle(final String val) throws SAXException {
+            try {
+                int dotIndex = val.indexOf('.');
+                int descIndex = val.indexOf('(', dotIndex + 1);
+                int tagIndex = val.lastIndexOf('(');
+
+                int tag = Integer.parseInt(val.substring(tagIndex + 1,
+                        val.length() - 1));
+                String owner = val.substring(0, dotIndex);
+                String name = val.substring(dotIndex + 1, descIndex);
+                String desc = val.substring(descIndex, tagIndex - 1);
+                return new Handle(tag, owner, name, desc);
+
+            } catch (RuntimeException e) {
+                throw new SAXException("Malformed handle " + val, e);
+            }
+        }
+
+        private final String decode(final String val) throws SAXException {
+            StringBuffer sb = new StringBuffer(val.length());
+            try {
+                int n = 0;
+                while (n < val.length()) {
+                    char c = val.charAt(n);
+                    if (c == '\\') {
+                        n++;
+                        c = val.charAt(n);
+                        if (c == '\\') {
+                            sb.append('\\');
+                        } else {
+                            n++; // skip 'u'
+                            sb.append((char) Integer.parseInt(
+                                    val.substring(n, n + 4), 16));
+                            n += 3;
+                        }
+                    } else {
+                        sb.append(c);
+                    }
+                    n++;
+                }
+
+            } catch (RuntimeException ex) {
+                throw new SAXException(ex);
+            }
+            return sb.toString();
+        }
+
+        protected final Label getLabel(final Object label) {
+            Label lbl = labels.get(label);
+            if (lbl == null) {
+                lbl = new Label();
+                labels.put(label, lbl);
+            }
+            return lbl;
+        }
+
+        // TODO verify move to stack
+        protected final MethodVisitor getCodeVisitor() {
+            return (MethodVisitor) peek();
+        }
+
+        protected final int getAccess(final String s) {
+            int access = 0;
+            if (s.indexOf("public") != -1) {
+                access |= ACC_PUBLIC;
+            }
+            if (s.indexOf("private") != -1) {
+                access |= ACC_PRIVATE;
+            }
+            if (s.indexOf("protected") != -1) {
+                access |= ACC_PROTECTED;
+            }
+            if (s.indexOf("static") != -1) {
+                access |= ACC_STATIC;
+            }
+            if (s.indexOf("final") != -1) {
+                access |= ACC_FINAL;
+            }
+            if (s.indexOf("super") != -1) {
+                access |= ACC_SUPER;
+            }
+            if (s.indexOf("synchronized") != -1) {
+                access |= ACC_SYNCHRONIZED;
+            }
+            if (s.indexOf("volatile") != -1) {
+                access |= ACC_VOLATILE;
+            }
+            if (s.indexOf("bridge") != -1) {
+                access |= ACC_BRIDGE;
+            }
+            if (s.indexOf("varargs") != -1) {
+                access |= ACC_VARARGS;
+            }
+            if (s.indexOf("transient") != -1) {
+                access |= ACC_TRANSIENT;
+            }
+            if (s.indexOf("native") != -1) {
+                access |= ACC_NATIVE;
+            }
+            if (s.indexOf("interface") != -1) {
+                access |= ACC_INTERFACE;
+            }
+            if (s.indexOf("abstract") != -1) {
+                access |= ACC_ABSTRACT;
+            }
+            if (s.indexOf("strict") != -1) {
+                access |= ACC_STRICT;
+            }
+            if (s.indexOf("synthetic") != -1) {
+                access |= ACC_SYNTHETIC;
+            }
+            if (s.indexOf("annotation") != -1) {
+                access |= ACC_ANNOTATION;
+            }
+            if (s.indexOf("enum") != -1) {
+                access |= ACC_ENUM;
+            }
+            if (s.indexOf("deprecated") != -1) {
+                access |= ACC_DEPRECATED;
+            }
+            if (s.indexOf("mandated") != -1) {
+                access |= ACC_MANDATED;
+            }
+            return access;
+        }
+    }
+
+    /**
+     * ClassRule
+     */
+    final class ClassRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            int major = Integer.parseInt(attrs.getValue("major"));
+            int minor = Integer.parseInt(attrs.getValue("minor"));
+            HashMap<String, Object> vals = new HashMap<String, Object>();
+            vals.put("version", new Integer(minor << 16 | major));
+            vals.put("access", attrs.getValue("access"));
+            vals.put("name", attrs.getValue("name"));
+            vals.put("parent", attrs.getValue("parent"));
+            vals.put("source", attrs.getValue("source"));
+            vals.put("signature", attrs.getValue("signature"));
+            vals.put("interfaces", new ArrayList<String>());
+            push(vals);
+            // values will be extracted in InterfacesRule.end();
+        }
+    }
+
+    final class SourceRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String file = attrs.getValue("file");
+            String debug = attrs.getValue("debug");
+            cv.visitSource(file, debug);
+        }
+    }
+
+    /**
+     * InterfaceRule
+     */
+    final class InterfaceRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            ((ArrayList<String>) ((HashMap<?, ?>) peek()).get("interfaces"))
+                    .add(attrs.getValue("name"));
+        }
+    }
+
+    /**
+     * InterfacesRule
+     */
+    final class InterfacesRule extends Rule {
+
+        @Override
+        public final void end(final String element) {
+            HashMap<?, ?> vals = (HashMap<?, ?>) pop();
+            int version = ((Integer) vals.get("version")).intValue();
+            int access = getAccess((String) vals.get("access"));
+            String name = (String) vals.get("name");
+            String signature = (String) vals.get("signature");
+            String parent = (String) vals.get("parent");
+            ArrayList<?> infs = (ArrayList<?>) vals.get("interfaces");
+            String[] interfaces = infs.toArray(new String[infs.size()]);
+            cv.visit(version, access, name, signature, parent, interfaces);
+            push(cv);
+        }
+    }
+
+    /**
+     * OuterClassRule
+     */
+    final class OuterClassRule extends Rule {
+
+        @Override
+        public final void begin(final String element, final Attributes attrs) {
+            String owner = attrs.getValue("owner");
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            cv.visitOuterClass(owner, name, desc);
+        }
+    }
+
+    /**
+     * InnerClassRule
+     */
+    final class InnerClassRule extends Rule {
+
+        @Override
+        public final void begin(final String element, final Attributes attrs) {
+            int access = getAccess(attrs.getValue("access"));
+            String name = attrs.getValue("name");
+            String outerName = attrs.getValue("outerName");
+            String innerName = attrs.getValue("innerName");
+            cv.visitInnerClass(name, outerName, innerName, access);
+        }
+    }
+
+    /**
+     * FieldRule
+     */
+    final class FieldRule extends Rule {
+
+        @Override
+        public final void begin(final String element, final Attributes attrs)
+                throws SAXException {
+            int access = getAccess(attrs.getValue("access"));
+            String name = attrs.getValue("name");
+            String signature = attrs.getValue("signature");
+            String desc = attrs.getValue("desc");
+            Object value = getValue(desc, attrs.getValue("value"));
+            push(cv.visitField(access, name, desc, signature, value));
+        }
+
+        @Override
+        public void end(final String name) {
+            ((FieldVisitor) pop()).visitEnd();
+        }
+    }
+
+    /**
+     * MethodRule
+     */
+    final class MethodRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            labels = new HashMap<Object, Label>();
+            HashMap<String, Object> vals = new HashMap<String, Object>();
+            vals.put("access", attrs.getValue("access"));
+            vals.put("name", attrs.getValue("name"));
+            vals.put("desc", attrs.getValue("desc"));
+            vals.put("signature", attrs.getValue("signature"));
+            vals.put("exceptions", new ArrayList<String>());
+            push(vals);
+            // values will be extracted in ExceptionsRule.end();
+        }
+
+        @Override
+        public final void end(final String name) {
+            ((MethodVisitor) pop()).visitEnd();
+            labels = null;
+        }
+    }
+
+    /**
+     * ExceptionRule
+     */
+    final class ExceptionRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            ((ArrayList<String>) ((HashMap<?, ?>) peek()).get("exceptions"))
+                    .add(attrs.getValue("name"));
+        }
+    }
+
+    /**
+     * ExceptionsRule
+     */
+    final class ExceptionsRule extends Rule {
+
+        @Override
+        public final void end(final String element) {
+            HashMap<?, ?> vals = (HashMap<?, ?>) pop();
+            int access = getAccess((String) vals.get("access"));
+            String name = (String) vals.get("name");
+            String desc = (String) vals.get("desc");
+            String signature = (String) vals.get("signature");
+            ArrayList<?> excs = (ArrayList<?>) vals.get("exceptions");
+            String[] exceptions = excs.toArray(new String[excs.size()]);
+
+            push(cv.visitMethod(access, name, desc, signature, exceptions));
+        }
+    }
+
+    /**
+     * MethodParameterRule
+     */
+    final class MethodParameterRule extends Rule {
+        @Override
+        public void begin(final String nm, final Attributes attrs) {
+            String name = attrs.getValue("name");
+            int access = getAccess(attrs.getValue("access"));
+            getCodeVisitor().visitParameter(name, access);
+        }
+    }
+
+    /**
+     * TableSwitchRule
+     */
+    final class TableSwitchRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            HashMap<String, Object> vals = new HashMap<String, Object>();
+            vals.put("min", attrs.getValue("min"));
+            vals.put("max", attrs.getValue("max"));
+            vals.put("dflt", attrs.getValue("dflt"));
+            vals.put("labels", new ArrayList<String>());
+            push(vals);
+        }
+
+        @Override
+        public final void end(final String name) {
+            HashMap<?, ?> vals = (HashMap<?, ?>) pop();
+            int min = Integer.parseInt((String) vals.get("min"));
+            int max = Integer.parseInt((String) vals.get("max"));
+            Label dflt = getLabel(vals.get("dflt"));
+            ArrayList<?> lbls = (ArrayList<?>) vals.get("labels");
+            Label[] labels = lbls.toArray(new Label[lbls.size()]);
+            getCodeVisitor().visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    /**
+     * TableSwitchLabelRule
+     */
+    final class TableSwitchLabelRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            ((ArrayList<Label>) ((HashMap<?, ?>) peek()).get("labels"))
+                    .add(getLabel(attrs.getValue("name")));
+        }
+    }
+
+    /**
+     * LookupSwitchRule
+     */
+    final class LookupSwitchRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            HashMap<String, Object> vals = new HashMap<String, Object>();
+            vals.put("dflt", attrs.getValue("dflt"));
+            vals.put("labels", new ArrayList<Label>());
+            vals.put("keys", new ArrayList<String>());
+            push(vals);
+        }
+
+        @Override
+        public final void end(final String name) {
+            HashMap<?, ?> vals = (HashMap<?, ?>) pop();
+            Label dflt = getLabel(vals.get("dflt"));
+            ArrayList<String> keyList = (ArrayList<String>) vals.get("keys");
+            ArrayList<?> lbls = (ArrayList<?>) vals.get("labels");
+            Label[] labels = lbls.toArray(new Label[lbls.size()]);
+            int[] keys = new int[keyList.size()];
+            for (int i = 0; i < keys.length; i++) {
+                keys[i] = Integer.parseInt(keyList.get(i));
+            }
+            getCodeVisitor().visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    /**
+     * LookupSwitchLabelRule
+     */
+    final class LookupSwitchLabelRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            HashMap<?, ?> vals = (HashMap<?, ?>) peek();
+            ((ArrayList<Label>) vals.get("labels")).add(getLabel(attrs
+                    .getValue("name")));
+            ((ArrayList<String>) vals.get("keys")).add(attrs.getValue("key"));
+        }
+    }
+
+    /**
+     * FrameRule
+     */
+    final class FrameRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            HashMap<String, Object> typeLists = new HashMap<String, Object>();
+            typeLists.put("local", new ArrayList<Object>());
+            typeLists.put("stack", new ArrayList<Object>());
+            push(attrs.getValue("type"));
+            push(attrs.getValue("count") == null ? "0" : attrs
+                    .getValue("count"));
+            push(typeLists);
+        }
+
+        @Override
+        public void end(final String name) {
+            HashMap<?, ?> typeLists = (HashMap<?, ?>) pop();
+            ArrayList<?> locals = (ArrayList<?>) typeLists.get("local");
+            int nLocal = locals.size();
+            Object[] local = locals.toArray();
+            ArrayList<?> stacks = (ArrayList<?>) typeLists.get("stack");
+            int nStack = stacks.size();
+            Object[] stack = stacks.toArray();
+            String count = (String) pop();
+            String type = (String) pop();
+            if ("NEW".equals(type)) {
+                getCodeVisitor()
+                        .visitFrame(F_NEW, nLocal, local, nStack, stack);
+            } else if ("FULL".equals(type)) {
+                getCodeVisitor().visitFrame(F_FULL, nLocal, local, nStack,
+                        stack);
+            } else if ("APPEND".equals(type)) {
+                getCodeVisitor().visitFrame(F_APPEND, nLocal, local, 0, null);
+            } else if ("CHOP".equals(type)) {
+                getCodeVisitor().visitFrame(F_CHOP, Integer.parseInt(count),
+                        null, 0, null);
+            } else if ("SAME".equals(type)) {
+                getCodeVisitor().visitFrame(F_SAME, 0, null, 0, null);
+            } else if ("SAME1".equals(type)) {
+                getCodeVisitor().visitFrame(F_SAME1, 0, null, nStack, stack);
+            }
+        }
+    }
+
+    final class FrameTypeRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            ArrayList<Object> types = (ArrayList<Object>) ((HashMap<?, ?>) peek())
+                    .get(name);
+            String type = attrs.getValue("type");
+            if ("uninitialized".equals(type)) {
+                types.add(getLabel(attrs.getValue("label")));
+            } else {
+                Integer t = TYPES.get(type);
+                if (t == null) {
+                    types.add(type);
+                } else {
+                    types.add(t);
+                }
+            }
+        }
+    }
+
+    /**
+     * LabelRule
+     */
+    final class LabelRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            getCodeVisitor().visitLabel(getLabel(attrs.getValue("name")));
+        }
+    }
+
+    /**
+     * TryCatchRule
+     */
+    final class TryCatchRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            Label start = getLabel(attrs.getValue("start"));
+            Label end = getLabel(attrs.getValue("end"));
+            Label handler = getLabel(attrs.getValue("handler"));
+            String type = attrs.getValue("type");
+            getCodeVisitor().visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    /**
+     * LineNumberRule
+     */
+    final class LineNumberRule extends Rule {
+
+        @Override
+        public final void begin(final String name, final Attributes attrs) {
+            int line = Integer.parseInt(attrs.getValue("line"));
+            Label start = getLabel(attrs.getValue("start"));
+            getCodeVisitor().visitLineNumber(line, start);
+        }
+    }
+
+    /**
+     * LocalVarRule
+     */
+    final class LocalVarRule extends Rule {
+
+        @Override
+        public final void begin(final String element, final Attributes attrs) {
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            String signature = attrs.getValue("signature");
+            Label start = getLabel(attrs.getValue("start"));
+            Label end = getLabel(attrs.getValue("end"));
+            int var = Integer.parseInt(attrs.getValue("var"));
+            getCodeVisitor().visitLocalVariable(name, desc, signature, start,
+                    end, var);
+        }
+    }
+
+    /**
+     * InvokeDynamicRule
+     */
+    final class InvokeDynamicRule extends Rule {
+        @Override
+        public final void begin(final String element, final Attributes attrs)
+                throws SAXException {
+            push(attrs.getValue("name"));
+            push(attrs.getValue("desc"));
+            push(decodeHandle(attrs.getValue("bsm")));
+            push(new ArrayList<Object>());
+        }
+
+        @Override
+        public final void end(final String element) {
+            ArrayList<?> bsmArgs = (ArrayList<?>) pop();
+            Handle bsm = (Handle) pop();
+            String desc = (String) pop();
+            String name = (String) pop();
+            getCodeVisitor().visitInvokeDynamicInsn(name, desc, bsm,
+                    bsmArgs.toArray());
+        }
+    }
+
+    /**
+     * InvokeDynamicBsmArgumentsRule
+     */
+    final class InvokeDynamicBsmArgumentsRule extends Rule {
+        @Override
+        public final void begin(final String element, final Attributes attrs)
+                throws SAXException {
+            ArrayList<Object> bsmArgs = (ArrayList<Object>) peek();
+            bsmArgs.add(getValue(attrs.getValue("desc"), attrs.getValue("cst")));
+        }
+    }
+
+    /**
+     * OpcodesRule
+     */
+    final class OpcodesRule extends Rule {
+
+        // public boolean match( String match, String element) {
+        // return match.startsWith( path) && OPCODES.containsKey( element);
+        // }
+
+        @Override
+        public final void begin(final String element, final Attributes attrs)
+                throws SAXException {
+            Opcode o = OPCODES.get(element);
+            if (o == null) {
+                throw new SAXException("Invalid element: " + element + " at "
+                        + match);
+            }
+
+            switch (o.type) {
+            case OpcodeGroup.INSN:
+                getCodeVisitor().visitInsn(o.opcode);
+                break;
+
+            case OpcodeGroup.INSN_FIELD:
+                getCodeVisitor().visitFieldInsn(o.opcode,
+                        attrs.getValue("owner"), attrs.getValue("name"),
+                        attrs.getValue("desc"));
+                break;
+
+            case OpcodeGroup.INSN_INT:
+                getCodeVisitor().visitIntInsn(o.opcode,
+                        Integer.parseInt(attrs.getValue("value")));
+                break;
+
+            case OpcodeGroup.INSN_JUMP:
+                getCodeVisitor().visitJumpInsn(o.opcode,
+                        getLabel(attrs.getValue("label")));
+                break;
+
+            case OpcodeGroup.INSN_METHOD:
+                getCodeVisitor().visitMethodInsn(o.opcode,
+                        attrs.getValue("owner"), attrs.getValue("name"),
+                        attrs.getValue("desc"),
+                        attrs.getValue("itf").equals("true"));
+                break;
+
+            case OpcodeGroup.INSN_TYPE:
+                getCodeVisitor()
+                        .visitTypeInsn(o.opcode, attrs.getValue("desc"));
+                break;
+
+            case OpcodeGroup.INSN_VAR:
+                getCodeVisitor().visitVarInsn(o.opcode,
+                        Integer.parseInt(attrs.getValue("var")));
+                break;
+
+            case OpcodeGroup.INSN_IINC:
+                getCodeVisitor().visitIincInsn(
+                        Integer.parseInt(attrs.getValue("var")),
+                        Integer.parseInt(attrs.getValue("inc")));
+                break;
+
+            case OpcodeGroup.INSN_LDC:
+                getCodeVisitor()
+                        .visitLdcInsn(
+                                getValue(attrs.getValue("desc"),
+                                        attrs.getValue("cst")));
+                break;
+
+            case OpcodeGroup.INSN_MULTIANEWARRAY:
+                getCodeVisitor().visitMultiANewArrayInsn(
+                        attrs.getValue("desc"),
+                        Integer.parseInt(attrs.getValue("dims")));
+                break;
+
+            default:
+                throw new Error("Internal error");
+
+            }
+        }
+    }
+
+    /**
+     * MaxRule
+     */
+    final class MaxRule extends Rule {
+
+        @Override
+        public final void begin(final String element, final Attributes attrs) {
+            int maxStack = Integer.parseInt(attrs.getValue("maxStack"));
+            int maxLocals = Integer.parseInt(attrs.getValue("maxLocals"));
+            getCodeVisitor().visitMaxs(maxStack, maxLocals);
+        }
+    }
+
+    final class AnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+
+            Object v = peek();
+            if (v instanceof ClassVisitor) {
+                push(((ClassVisitor) v).visitAnnotation(desc, visible));
+            } else if (v instanceof FieldVisitor) {
+                push(((FieldVisitor) v).visitAnnotation(desc, visible));
+            } else if (v instanceof MethodVisitor) {
+                push(((MethodVisitor) v).visitAnnotation(desc, visible));
+            }
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class TypeAnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+            int typeRef = Integer.parseInt(attrs.getValue("typeRef"));
+            TypePath typePath = TypePath.fromString(attrs.getValue("typePath"));
+
+            Object v = peek();
+            if (v instanceof ClassVisitor) {
+                push(((ClassVisitor) v).visitTypeAnnotation(typeRef, typePath,
+                        desc, visible));
+            } else if (v instanceof FieldVisitor) {
+                push(((FieldVisitor) v).visitTypeAnnotation(typeRef, typePath,
+                        desc, visible));
+            } else if (v instanceof MethodVisitor) {
+                push(((MethodVisitor) v).visitTypeAnnotation(typeRef, typePath,
+                        desc, visible));
+            }
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class AnnotationParameterRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            int parameter = Integer.parseInt(attrs.getValue("parameter"));
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+
+            push(((MethodVisitor) peek()).visitParameterAnnotation(parameter,
+                    desc, visible));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class InsnAnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+            int typeRef = Integer.parseInt(attrs.getValue("typeRef"));
+            TypePath typePath = TypePath.fromString(attrs.getValue("typePath"));
+            push(((MethodVisitor) peek()).visitInsnAnnotation(typeRef,
+                    typePath, desc, visible));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class TryCatchAnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+            int typeRef = Integer.parseInt(attrs.getValue("typeRef"));
+            TypePath typePath = TypePath.fromString(attrs.getValue("typePath"));
+            push(((MethodVisitor) peek()).visitTryCatchAnnotation(typeRef,
+                    typePath, desc, visible));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class LocalVariableAnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String name, final Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+            int typeRef = Integer.parseInt(attrs.getValue("typeRef"));
+            TypePath typePath = TypePath.fromString(attrs.getValue("typePath"));
+            String[] s = attrs.getValue("start").split(" ");
+            Label[] start = new Label[s.length];
+            for (int i = 0; i < start.length; ++i) {
+                start[i] = getLabel(s[i]);
+            }
+            String[] e = attrs.getValue("end").split(" ");
+            Label[] end = new Label[e.length];
+            for (int i = 0; i < end.length; ++i) {
+                end[i] = getLabel(e[i]);
+            }
+            String[] v = attrs.getValue("index").split(" ");
+            int[] index = new int[v.length];
+            for (int i = 0; i < index.length; ++i) {
+                index[i] = Integer.parseInt(v[i]);
+            }
+            push(((MethodVisitor) peek()).visitLocalVariableAnnotation(typeRef,
+                    typePath, start, end, index, desc, visible));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class AnnotationValueRule extends Rule {
+
+        @Override
+        public void begin(final String nm, final Attributes attrs)
+                throws SAXException {
+            AnnotationVisitor av = (AnnotationVisitor) peek();
+            if (av != null) {
+                av.visit(
+                        attrs.getValue("name"),
+                        getValue(attrs.getValue("desc"),
+                                attrs.getValue("value")));
+            }
+        }
+    }
+
+    final class AnnotationValueEnumRule extends Rule {
+
+        @Override
+        public void begin(final String nm, final Attributes attrs) {
+            AnnotationVisitor av = (AnnotationVisitor) peek();
+            if (av != null) {
+                av.visitEnum(attrs.getValue("name"), attrs.getValue("desc"),
+                        attrs.getValue("value"));
+            }
+        }
+    }
+
+    final class AnnotationValueAnnotationRule extends Rule {
+
+        @Override
+        public void begin(final String nm, final Attributes attrs) {
+            AnnotationVisitor av = (AnnotationVisitor) peek();
+            push(av == null ? null : av.visitAnnotation(attrs.getValue("name"),
+                    attrs.getValue("desc")));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class AnnotationValueArrayRule extends Rule {
+
+        @Override
+        public void begin(final String nm, final Attributes attrs) {
+            AnnotationVisitor av = (AnnotationVisitor) peek();
+            push(av == null ? null : av.visitArray(attrs.getValue("name")));
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    final class AnnotationDefaultRule extends Rule {
+
+        @Override
+        public void begin(final String nm, final Attributes attrs) {
+            MethodVisitor av = (MethodVisitor) peek();
+            push(av == null ? null : av.visitAnnotationDefault());
+        }
+
+        @Override
+        public void end(final String name) {
+            AnnotationVisitor av = (AnnotationVisitor) pop();
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+    }
+
+    /**
+     * Opcode
+     */
+    static final class Opcode {
+
+        public final int opcode;
+
+        public final int type;
+
+        Opcode(final int opcode, final int type) {
+            this.opcode = opcode;
+            this.type = type;
+        }
+    }
+}