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("&");
+ break;
+
+ case '<':
+ sb.append("<");
+ break;
+
+ case '>':
+ sb.append(">");
+ break;
+
+ case '\"':
+ sb.append(""");
+ 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();
+ }
+
+ }
+
+}