You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/08/23 19:37:51 UTC

[02/25] TAJO-906: Runtime code generation for evaluating expression trees.

http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/ASMContentHandler.java
----------------------------------------------------------------------
diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/ASMContentHandler.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/ASMContentHandler.java
new file mode 100644
index 0000000..c8c14c9
--- /dev/null
+++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/ASMContentHandler.java
@@ -0,0 +1,1317 @@
+/***
+ * 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.tajo.org.objectweb.asm.xml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.tajo.org.objectweb.asm.AnnotationVisitor;
+import org.apache.tajo.org.objectweb.asm.Label;
+import org.apache.tajo.org.objectweb.asm.Type;
+import org.apache.tajo.org.objectweb.asm.ClassVisitor;
+import org.apache.tajo.org.objectweb.asm.FieldVisitor;
+import org.apache.tajo.org.objectweb.asm.Handle;
+import org.apache.tajo.org.objectweb.asm.MethodVisitor;
+import org.apache.tajo.org.objectweb.asm.Opcodes;
+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 SAXClassAdapter
+ * @see 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 org.apache.tajo.org.objectweb.asm.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/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("*/parameterAnnotation", new AnnotationParameterRule());
+        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;
+            }
+            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));
+        }
+    }
+
+    /**
+     * 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"));
+                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 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 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;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/Processor.java
----------------------------------------------------------------------
diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/Processor.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/Processor.java
new file mode 100644
index 0000000..7deedba
--- /dev/null
+++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/xml/Processor.java
@@ -0,0 +1,1044 @@
+/***
+ * 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.tajo.org.objectweb.asm.xml;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.tajo.org.objectweb.asm.ClassWriter;
+import org.apache.tajo.org.objectweb.asm.ClassReader;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Processor is a command line tool that can be used for bytecode waving
+ * directed by XSL transformation.
+ * <p>
+ * In order to use a concrete XSLT engine, system property
+ * <tt>javax.xml.transform.TransformerFactory</tt> must be set to one of the
+ * following values.
+ * 
+ * <blockquote>
+ * <table border="1" cellspacing="0" cellpadding="3">
+ * <tr>
+ * <td>jd.xslt</td>
+ * <td>jd.xml.xslt.trax.TransformerFactoryImpl</td>
+ * </tr>
+ * 
+ * <tr>
+ * <td>Saxon</td>
+ * <td>net.sf.saxon.TransformerFactoryImpl</td>
+ * </tr>
+ * 
+ * <tr>
+ * <td>Caucho</td>
+ * <td>com.caucho.xsl.Xsl</td>
+ * </tr>
+ * 
+ * <tr>
+ * <td>Xalan interpeter</td>
+ * <td>org.apache.xalan.processor.TransformerFactory</td>
+ * </tr>
+ * 
+ * <tr>
+ * <td>Xalan xsltc</td>
+ * <td>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * 
+ * @author Eugene Kuleshov
+ */
+public class Processor {
+
+    public static final int BYTECODE = 1;
+
+    public static final int MULTI_XML = 2;
+
+    public static final int SINGLE_XML = 3;
+
+    private static final String SINGLE_XML_NAME = "classes.xml";
+
+    private final int inRepresentation;
+
+    private final int outRepresentation;
+
+    private final InputStream input;
+
+    private final OutputStream output;
+
+    private final Source xslt;
+
+    private int n = 0;
+
+    public Processor(final int inRepresenation, final int outRepresentation,
+            final InputStream input, final OutputStream output,
+            final Source xslt) {
+        this.inRepresentation = inRepresenation;
+        this.outRepresentation = outRepresentation;
+        this.input = input;
+        this.output = output;
+        this.xslt = xslt;
+    }
+
+    public int process() throws TransformerException, IOException, SAXException {
+        ZipInputStream zis = new ZipInputStream(input);
+        final ZipOutputStream zos = new ZipOutputStream(output);
+        final OutputStreamWriter osw = new OutputStreamWriter(zos);
+
+        Thread.currentThread().setContextClassLoader(
+                getClass().getClassLoader());
+
+        TransformerFactory tf = TransformerFactory.newInstance();
+        if (!tf.getFeature(SAXSource.FEATURE)
+                || !tf.getFeature(SAXResult.FEATURE)) {
+            return 0;
+        }
+
+        SAXTransformerFactory saxtf = (SAXTransformerFactory) tf;
+        Templates templates = null;
+        if (xslt != null) {
+            templates = saxtf.newTemplates(xslt);
+        }
+
+        // configuring outHandlerFactory
+        // ///////////////////////////////////////////////////////
+
+        EntryElement entryElement = getEntryElement(zos);
+
+        ContentHandler outDocHandler = null;
+        switch (outRepresentation) {
+        case BYTECODE:
+            outDocHandler = new OutputSlicingHandler(
+                    new ASMContentHandlerFactory(zos), entryElement, false);
+            break;
+
+        case MULTI_XML:
+            outDocHandler = new OutputSlicingHandler(new SAXWriterFactory(osw,
+                    true), entryElement, true);
+            break;
+
+        case SINGLE_XML:
+            ZipEntry outputEntry = new ZipEntry(SINGLE_XML_NAME);
+            zos.putNextEntry(outputEntry);
+            outDocHandler = new SAXWriter(osw, false);
+            break;
+
+        }
+
+        // configuring inputDocHandlerFactory
+        // /////////////////////////////////////////////////
+        ContentHandler inDocHandler;
+        if (templates == null) {
+            inDocHandler = outDocHandler;
+        } else {
+            inDocHandler = new InputSlicingHandler("class", outDocHandler,
+                    new TransformerHandlerFactory(saxtf, templates,
+                            outDocHandler));
+        }
+        ContentHandlerFactory inDocHandlerFactory = new SubdocumentHandlerFactory(
+                inDocHandler);
+
+        if (inDocHandler != null && inRepresentation != SINGLE_XML) {
+            inDocHandler.startDocument();
+            inDocHandler.startElement("", "classes", "classes",
+                    new AttributesImpl());
+        }
+
+        int i = 0;
+        ZipEntry ze;
+        while ((ze = zis.getNextEntry()) != null) {
+            update(ze.getName(), n++);
+            if (isClassEntry(ze)) {
+                processEntry(zis, ze, inDocHandlerFactory);
+            } else {
+                OutputStream os = entryElement.openEntry(getName(ze));
+                copyEntry(zis, os);
+                entryElement.closeEntry();
+            }
+
+            i++;
+        }
+
+        if (inDocHandler != null && inRepresentation != SINGLE_XML) {
+            inDocHandler.endElement("", "classes", "classes");
+            inDocHandler.endDocument();
+        }
+
+        if (outRepresentation == SINGLE_XML) {
+            zos.closeEntry();
+        }
+        zos.flush();
+        zos.close();
+
+        return i;
+    }
+
+    private void copyEntry(final InputStream is, final OutputStream os)
+            throws IOException {
+        if (outRepresentation == SINGLE_XML) {
+            return;
+        }
+
+        byte[] buff = new byte[2048];
+        int i;
+        while ((i = is.read(buff)) != -1) {
+            os.write(buff, 0, i);
+        }
+    }
+
+    private boolean isClassEntry(final ZipEntry ze) {
+        String name = ze.getName();
+        return inRepresentation == SINGLE_XML && name.equals(SINGLE_XML_NAME)
+                || name.endsWith(".class") || name.endsWith(".class.xml");
+    }
+
+    private void processEntry(final ZipInputStream zis, final ZipEntry ze,
+            final ContentHandlerFactory handlerFactory) {
+        ContentHandler handler = handlerFactory.createContentHandler();
+        try {
+
+            // if (CODE2ASM.equals(command)) { // read bytecode and process it
+            // // with TraceClassVisitor
+            // ClassReader cr = new ClassReader(readEntry(zis, ze));
+            // cr.accept(new TraceClassVisitor(null, new PrintWriter(os)),
+            // false);
+            // }
+
+            boolean singleInputDocument = inRepresentation == SINGLE_XML;
+            if (inRepresentation == BYTECODE) { // read bytecode and process it
+                // with handler
+                ClassReader cr = new ClassReader(readEntry(zis, ze));
+                cr.accept(new SAXClassAdapter(handler, singleInputDocument), 0);
+
+            } else { // read XML and process it with handler
+                XMLReader reader = XMLReaderFactory.createXMLReader();
+                reader.setContentHandler(handler);
+                reader.parse(new InputSource(
+                        singleInputDocument ? (InputStream) new ProtectedInputStream(
+                                zis) : new ByteArrayInputStream(readEntry(zis,
+                                ze))));
+
+            }
+        } catch (Exception ex) {
+            update(ze.getName(), 0);
+            update(ex, 0);
+        }
+    }
+
+    private EntryElement getEntryElement(final ZipOutputStream zos) {
+        if (outRepresentation == SINGLE_XML) {
+            return new SingleDocElement(zos);
+        }
+        return new ZipEntryElement(zos);
+    }
+
+    // private ContentHandlerFactory getHandlerFactory(
+    // OutputStream os,
+    // SAXTransformerFactory saxtf,
+    // Templates templates)
+    // {
+    // ContentHandlerFactory factory = null;
+    // if (templates == null) {
+    // if (outputRepresentation == BYTECODE) { // factory used to write
+    // // bytecode
+    // factory = new ASMContentHandlerFactory(os, computeMax);
+    // } else { // factory used to write XML
+    // factory = new SAXWriterFactory(os, true);
+    // }
+    // } else {
+    // if (outputRepresentation == BYTECODE) { // factory used to transform
+    // // and then write bytecode
+    // factory = new ASMTransformerHandlerFactory(saxtf,
+    // templates,
+    // os,
+    // computeMax);
+    // } else { // factory used to transformand then write XML
+    // factory = new TransformerHandlerFactory(saxtf,
+    // templates,
+    // os,
+    // outputRepresentation == SINGLE_XML);
+    // }
+    // }
+    // return factory;
+    // }
+
+    private String getName(final ZipEntry ze) {
+        String name = ze.getName();
+        if (isClassEntry(ze)) {
+            if (inRepresentation != BYTECODE && outRepresentation == BYTECODE) {
+                name = name.substring(0, name.length() - 4); // .class.xml to
+                // .class
+            } else if (inRepresentation == BYTECODE
+                    && outRepresentation != BYTECODE) {
+                name += ".xml"; // .class to .class.xml
+            }
+            // } else if( CODE2ASM.equals( command)) {
+            // name = name.substring( 0, name.length()-6).concat( ".asm");
+        }
+        return name;
+    }
+
+    private static byte[] readEntry(final InputStream zis, final ZipEntry ze)
+            throws IOException {
+        long size = ze.getSize();
+        if (size > -1) {
+            byte[] buff = new byte[(int) size];
+            int k = 0;
+            int n;
+            while ((n = zis.read(buff, k, buff.length - k)) > 0) {
+                k += n;
+            }
+            return buff;
+        }
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        byte[] buff = new byte[4096];
+        int i;
+        while ((i = zis.read(buff)) != -1) {
+            bos.write(buff, 0, i);
+        }
+        return bos.toByteArray();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
+     */
+    protected void update(final Object arg, final int n) {
+        if (arg instanceof Throwable) {
+            ((Throwable) arg).printStackTrace();
+        } else {
+            if (n % 100 == 0) {
+                System.err.println(n + " " + arg);
+            }
+        }
+    }
+
+    public static void main(final String[] args) throws Exception {
+        if (args.length < 2) {
+            showUsage();
+            return;
+        }
+
+        int inRepresentation = getRepresentation(args[0]);
+        int outRepresentation = getRepresentation(args[1]);
+
+        InputStream is = System.in;
+        OutputStream os = new BufferedOutputStream(System.out);
+
+        Source xslt = null;
+        // boolean computeMax = true;
+
+        for (int i = 2; i < args.length; i++) {
+            if ("-in".equals(args[i])) {
+                is = new FileInputStream(args[++i]);
+
+            } else if ("-out".equals(args[i])) {
+                os = new BufferedOutputStream(new FileOutputStream(args[++i]));
+
+            } else if ("-xslt".equals(args[i])) {
+                xslt = new StreamSource(new FileInputStream(args[++i]));
+
+                // } else if( "-computemax".equals( args[ i].toLowerCase())) {
+                // computeMax = true;
+
+            } else {
+                showUsage();
+                return;
+
+            }
+        }
+
+        if (inRepresentation == 0 || outRepresentation == 0) {
+            showUsage();
+            return;
+        }
+
+        Processor m = new Processor(inRepresentation, outRepresentation, is,
+                os, xslt);
+
+        long l1 = System.currentTimeMillis();
+        int n = m.process();
+        long l2 = System.currentTimeMillis();
+        System.err.println(n);
+        System.err.println((l2 - l1) + "ms  " + 1000f * n / (l2 - l1)
+                + " resources/sec");
+    }
+
+    private static int getRepresentation(final String s) {
+        if ("code".equals(s)) {
+            return BYTECODE;
+        } else if ("xml".equals(s)) {
+            return MULTI_XML;
+        } else if ("singlexml".equals(s)) {
+            return SINGLE_XML;
+        }
+        return 0;
+    }
+
+    private static void showUsage() {
+        System.err
+                .println("Usage: Main <in format> <out format> [-in <input jar>] [-out <output jar>] [-xslt <xslt fiel>]");
+        System.err
+                .println("  when -in or -out is omitted sysin and sysout would be used");
+        System.err
+                .println("  <in format> and <out format> - code | xml | singlexml");
+    }
+
+    /**
+     * IputStream wrapper class used to protect input streams from being closed
+     * by some stupid XML parsers.
+     */
+    private static final class ProtectedInputStream extends InputStream {
+        private final InputStream is;
+
+        ProtectedInputStream(final InputStream is) {
+            this.is = is;
+        }
+
+        @Override
+        public final void close() throws IOException {
+        }
+
+        @Override
+        public final int read() throws IOException {
+            return is.read();
+        }
+
+        @Override
+        public final int read(final byte[] b, final int off, final int len)
+                throws IOException {
+            return is.read(b, off, len);
+        }
+
+        @Override
+        public final int available() throws IOException {
+            return is.available();
+        }
+    }
+
+    /**
+     * A {@link ContentHandlerFactory ContentHandlerFactory} is used to create
+     * {@link org.xml.sax.ContentHandler ContentHandler} instances for concrete
+     * context.
+     */
+    private static interface ContentHandlerFactory {
+
+        /**
+         * Creates an instance of the content handler.
+         * 
+         * @return content handler
+         */
+        ContentHandler createContentHandler();
+
+    }
+
+    /**
+     * SAXWriterFactory
+     */
+    private static final class SAXWriterFactory implements
+            ContentHandlerFactory {
+        private final Writer w;
+
+        private final boolean optimizeEmptyElements;
+
+        SAXWriterFactory(final Writer w, final boolean optimizeEmptyElements) {
+            this.w = w;
+            this.optimizeEmptyElements = optimizeEmptyElements;
+        }
+
+        public final ContentHandler createContentHandler() {
+            return new SAXWriter(w, optimizeEmptyElements);
+        }
+
+    }
+
+    /**
+     * ASMContentHandlerFactory
+     */
+    private static final class ASMContentHandlerFactory implements
+            ContentHandlerFactory {
+        final OutputStream os;
+
+        ASMContentHandlerFactory(final OutputStream os) {
+            this.os = os;
+        }
+
+        public final ContentHandler createContentHandler() {
+            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+            return new ASMContentHandler(cw) {
+                @Override
+                public void endDocument() throws SAXException {
+                    try {
+                        os.write(cw.toByteArray());
+                    } catch (IOException e) {
+                        throw new SAXException(e);
+                    }
+                }
+            };
+        }
+
+    }
+
+    /**
+     * TransformerHandlerFactory
+     */
+    private static final class TransformerHandlerFactory implements
+            ContentHandlerFactory {
+        private SAXTransformerFactory saxtf;
+
+        private final Templates templates;
+
+        private ContentHandler outputHandler;
+
+        TransformerHandlerFactory(final SAXTransformerFactory saxtf,
+                final Templates templates, final ContentHandler outputHandler) {
+            this.saxtf = saxtf;
+            this.templates = templates;
+            this.outputHandler = outputHandler;
+        }
+
+        public final ContentHandler createContentHandler() {
+            try {
+                TransformerHandler handler = saxtf
+                        .newTransformerHandler(templates);
+                handler.setResult(new SAXResult(outputHandler));
+                return handler;
+            } catch (TransformerConfigurationException ex) {
+                throw new RuntimeException(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * SubdocumentHandlerFactory
+     */
+    private static final class SubdocumentHandlerFactory implements
+            ContentHandlerFactory {
+        private final ContentHandler subdocumentHandler;
+
+        SubdocumentHandlerFactory(final ContentHandler subdocumentHandler) {
+            this.subdocumentHandler = subdocumentHandler;
+        }
+
+        public final ContentHandler createContentHandler() {
+            return subdocumentHandler;
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} and
+     * {@link org.xml.sax.ext.LexicalHandler LexicalHandler} that serializes XML
+     * from SAX 2.0 events into {@link java.io.Writer Writer}.
+     * 
+     * <i><blockquote> This implementation does not support namespaces, entity
+     * definitions (uncluding DTD), CDATA and text elements. </blockquote></i>
+     */
+    private static final class SAXWriter extends DefaultHandler implements
+            LexicalHandler {
+        private static final char[] OFF = "                                                                                                        "
+                .toCharArray();
+
+        private Writer w;
+
+        private final boolean optimizeEmptyElements;
+
+        private boolean openElement = false;
+
+        private int ident = 0;
+
+        /**
+         * Creates <code>SAXWriter</code>.
+         * 
+         * @param w
+         *            writer
+         * @param optimizeEmptyElements
+         *            if set to <code>true</code>, short XML syntax will be used
+         *            for empty elements
+         */
+        SAXWriter(final Writer w, final boolean optimizeEmptyElements) {
+            this.w = w;
+            this.optimizeEmptyElements = optimizeEmptyElements;
+        }
+
+        @Override
+        public final void startElement(final String ns, final String localName,
+                final String qName, final Attributes atts) throws SAXException {
+            try {
+                closeElement();
+
+                writeIdent();
+                w.write('<' + qName);
+                if (atts != null && atts.getLength() > 0) {
+                    writeAttributes(atts);
+                }
+
+                if (optimizeEmptyElements) {
+                    openElement = true;
+                } else {
+                    w.write(">\n");
+                }
+                ident += 2;
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        @Override
+        public final void endElement(final String ns, final String localName,
+                final String qName) throws SAXException {
+            ident -= 2;
+            try {
+                if (openElement) {
+                    w.write("/>\n");
+                    openElement = false;
+                } else {
+                    writeIdent();
+                    w.write("</" + qName + ">\n");
+                }
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        @Override
+        public final void endDocument() throws SAXException {
+            try {
+                w.flush();
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void comment(final char[] ch, final int off, final int len)
+                throws SAXException {
+            try {
+                closeElement();
+
+                writeIdent();
+                w.write("<!-- ");
+                w.write(ch, off, len);
+                w.write(" -->\n");
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void startDTD(final String arg0, final String arg1,
+                final String arg2) throws SAXException {
+        }
+
+        public final void endDTD() throws SAXException {
+        }
+
+        public final void startEntity(final String arg0) throws SAXException {
+        }
+
+        public final void endEntity(final String arg0) throws SAXException {
+        }
+
+        public final void startCDATA() throws SAXException {
+        }
+
+        public final void endCDATA() throws SAXException {
+        }
+
+        private final void writeAttributes(final Attributes atts)
+                throws IOException {
+            StringBuffer sb = new StringBuffer();
+            int len = atts.getLength();
+            for (int i = 0; i < len; i++) {
+                sb.append(' ').append(atts.getLocalName(i)).append("=\"")
+                        .append(esc(atts.getValue(i))).append('\"');
+            }
+            w.write(sb.toString());
+        }
+
+        /**
+         * Encode string with escaping.
+         * 
+         * @param str
+         *            string to encode.
+         * @return encoded string
+         */
+        private static final String esc(final String str) {
+            StringBuffer sb = new StringBuffer(str.length());
+            for (int i = 0; i < str.length(); i++) {
+                char ch = str.charAt(i);
+                switch (ch) {
+                case '&':
+                    sb.append("&amp;");
+                    break;
+
+                case '<':
+                    sb.append("&lt;");
+                    break;
+
+                case '>':
+                    sb.append("&gt;");
+                    break;
+
+                case '\"':
+                    sb.append("&quot;");
+                    break;
+
+                default:
+                    if (ch > 0x7f) {
+                        sb.append("&#").append(Integer.toString(ch))
+                                .append(';');
+                    } else {
+                        sb.append(ch);
+                    }
+
+                }
+            }
+            return sb.toString();
+        }
+
+        private final void writeIdent() throws IOException {
+            int n = ident;
+            while (n > 0) {
+                if (n > OFF.length) {
+                    w.write(OFF);
+                    n -= OFF.length;
+                } else {
+                    w.write(OFF, 0, n);
+                    n = 0;
+                }
+            }
+        }
+
+        private final void closeElement() throws IOException {
+            if (openElement) {
+                w.write(">\n");
+            }
+            openElement = false;
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML
+     * documents into smaller chunks. Each chunk is processed by the nested
+     * {@link org.xml.sax.ContentHandler ContentHandler} obtained from
+     * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is
+     * useful for running XSLT engine against large XML document that will
+     * hardly fit into the memory all together.
+     * <p>
+     * TODO use complete path for subdocumentRoot
+     */
+    private static final class InputSlicingHandler extends DefaultHandler {
+        private String subdocumentRoot;
+
+        private final ContentHandler rootHandler;
+
+        private ContentHandlerFactory subdocumentHandlerFactory;
+
+        private boolean subdocument = false;
+
+        private ContentHandler subdocumentHandler;
+
+        /**
+         * Constructs a new {@link InputSlicingHandler SubdocumentHandler}
+         * object.
+         * 
+         * @param subdocumentRoot
+         *            name/path to the root element of the subdocument
+         * @param rootHandler
+         *            content handler for the entire document (subdocument
+         *            envelope).
+         * @param subdocumentHandlerFactory
+         *            a {@link ContentHandlerFactory ContentHandlerFactory} used
+         *            to create {@link ContentHandler ContentHandler} instances
+         *            for subdocuments.
+         */
+        InputSlicingHandler(final String subdocumentRoot,
+                final ContentHandler rootHandler,
+                final ContentHandlerFactory subdocumentHandlerFactory) {
+            this.subdocumentRoot = subdocumentRoot;
+            this.rootHandler = rootHandler;
+            this.subdocumentHandlerFactory = subdocumentHandlerFactory;
+        }
+
+        @Override
+        public final void startElement(final String namespaceURI,
+                final String localName, final String qName,
+                final Attributes list) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.startElement(namespaceURI, localName, qName,
+                        list);
+            } else if (localName.equals(subdocumentRoot)) {
+                subdocumentHandler = subdocumentHandlerFactory
+                        .createContentHandler();
+                subdocumentHandler.startDocument();
+                subdocumentHandler.startElement(namespaceURI, localName, qName,
+                        list);
+                subdocument = true;
+            } else if (rootHandler != null) {
+                rootHandler.startElement(namespaceURI, localName, qName, list);
+            }
+        }
+
+        @Override
+        public final void endElement(final String namespaceURI,
+                final String localName, final String qName) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.endElement(namespaceURI, localName, qName);
+                if (localName.equals(subdocumentRoot)) {
+                    subdocumentHandler.endDocument();
+                    subdocument = false;
+                }
+            } else if (rootHandler != null) {
+                rootHandler.endElement(namespaceURI, localName, qName);
+            }
+        }
+
+        @Override
+        public final void startDocument() throws SAXException {
+            if (rootHandler != null) {
+                rootHandler.startDocument();
+            }
+        }
+
+        @Override
+        public final void endDocument() throws SAXException {
+            if (rootHandler != null) {
+                rootHandler.endDocument();
+
+            }
+        }
+
+        @Override
+        public final void characters(final char[] buff, final int offset,
+                final int size) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.characters(buff, offset, size);
+            } else if (rootHandler != null) {
+                rootHandler.characters(buff, offset, size);
+            }
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML
+     * documents into smaller chunks. Each chunk is processed by the nested
+     * {@link org.xml.sax.ContentHandler ContentHandler} obtained from
+     * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is
+     * useful for running XSLT engine against large XML document that will
+     * hardly fit into the memory all together.
+     * 
+     * <p>
+     * TODO use complete path for subdocumentRoot
+     */
+    private static final class OutputSlicingHandler extends DefaultHandler {
+        private final String subdocumentRoot;
+
+        private ContentHandlerFactory subdocumentHandlerFactory;
+
+        private final EntryElement entryElement;
+
+        private boolean isXml;
+
+        private boolean subdocument = false;
+
+        private ContentHandler subdocumentHandler;
+
+        /**
+         * Constructs a new {@link OutputSlicingHandler SubdocumentHandler}
+         * object.
+         * 
+         * @param subdocumentHandlerFactory
+         *            a {@link ContentHandlerFactory ContentHandlerFactory} used
+         *            to create {@link ContentHandler ContentHandler} instances
+         *            for subdocuments.
+         * @param entryElement
+         *            TODO.
+         * @param isXml
+         *            TODO.
+         */
+        OutputSlicingHandler(
+                final ContentHandlerFactory subdocumentHandlerFactory,
+                final EntryElement entryElement, final boolean isXml) {
+            this.subdocumentRoot = "class";
+            this.subdocumentHandlerFactory = subdocumentHandlerFactory;
+            this.entryElement = entryElement;
+            this.isXml = isXml;
+        }
+
+        @Override
+        public final void startElement(final String namespaceURI,
+                final String localName, final String qName,
+                final Attributes list) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.startElement(namespaceURI, localName, qName,
+                        list);
+            } else if (localName.equals(subdocumentRoot)) {
+                String name = list.getValue("name");
+                if (name == null || name.length() == 0) {
+                    throw new SAXException(
+                            "Class element without name attribute.");
+                }
+                try {
+                    entryElement.openEntry(isXml ? name + ".class.xml" : name
+                            + ".class");
+                } catch (IOException ex) {
+                    throw new SAXException(ex.toString(), ex);
+                }
+                subdocumentHandler = subdocumentHandlerFactory
+                        .createContentHandler();
+                subdocumentHandler.startDocument();
+                subdocumentHandler.startElement(namespaceURI, localName, qName,
+                        list);
+                subdocument = true;
+            }
+        }
+
+        @Override
+        public final void endElement(final String namespaceURI,
+                final String localName, final String qName) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.endElement(namespaceURI, localName, qName);
+                if (localName.equals(subdocumentRoot)) {
+                    subdocumentHandler.endDocument();
+                    subdocument = false;
+                    try {
+                        entryElement.closeEntry();
+                    } catch (IOException ex) {
+                        throw new SAXException(ex.toString(), ex);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public final void startDocument() throws SAXException {
+        }
+
+        @Override
+        public final void endDocument() throws SAXException {
+        }
+
+        @Override
+        public final void characters(final char[] buff, final int offset,
+                final int size) throws SAXException {
+            if (subdocument) {
+                subdocumentHandler.characters(buff, offset, size);
+            }
+        }
+
+    }
+
+    private static interface EntryElement {
+
+        OutputStream openEntry(String name) throws IOException;
+
+        void closeEntry() throws IOException;
+
+    }
+
+    private static final class SingleDocElement implements EntryElement {
+        private final OutputStream os;
+
+        SingleDocElement(final OutputStream os) {
+            this.os = os;
+        }
+
+        public OutputStream openEntry(final String name) throws IOException {
+            return os;
+        }
+
+        public void closeEntry() throws IOException {
+            os.flush();
+        }
+
+    }
+
+    private static final class ZipEntryElement implements EntryElement {
+        private ZipOutputStream zos;
+
+        ZipEntryElement(final ZipOutputStream zos) {
+            this.zos = zos;
+        }
+
+        public OutputStream openEntry(final String name) throws IOException {
+            ZipEntry entry = new ZipEntry(name);
+            zos.putNextEntry(entry);
+            return zos;
+        }
+
+        public void closeEntry() throws IOException {
+            zos.flush();
+            zos.closeEntry();
+        }
+
+    }
+
+}