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(); + } + + } + +}
