3 * Copyright (c) 2004, Eugene Kuleshov
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
30 package org.objectweb.asm.xml;
32 import java.io.IOException;
33 import java.io.OutputStream;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
40 import org.objectweb.asm.AnnotationVisitor;
41 import org.objectweb.asm.ClassVisitor;
42 import org.objectweb.asm.ClassWriter;
43 import org.objectweb.asm.FieldVisitor;
44 import org.objectweb.asm.MethodVisitor;
45 import org.objectweb.asm.Opcodes;
46 import org.objectweb.asm.Label;
47 import org.objectweb.asm.Type;
49 import org.xml.sax.Attributes;
50 import org.xml.sax.SAXException;
51 import org.xml.sax.helpers.DefaultHandler;
54 * A {@link org.xml.sax.ContentHandler ContentHandler} that transforms XML
55 * document into Java class file. This class can be feeded by any kind of SAX
56 * 2.0 event producers, e.g. XML parser, XSLT or XPath engines, or custom code.
58 * @see org.objectweb.asm.xml.SAXClassAdapter
59 * @see org.objectweb.asm.xml.Processor
61 * @author Eugene Kuleshov
63 public class ASMContentHandler extends DefaultHandler implements Opcodes {
65 * Stack of the intermediate processing contexts.
67 private List stack = new ArrayList();
70 * Complete name of the current element.
72 private String match = "";
75 * <tt>true</tt> if the maximum stack size and number of local variables
76 * must be automatically computed.
78 protected boolean computeMax;
81 * Output stream to write result bytecode.
83 protected OutputStream os;
86 * Current instance of the {@link ClassWriter ClassWriter} used to write
89 protected ClassWriter cw;
92 * Map of the active {@link Label Label} instances for current method.
96 private static final String BASE = "class";
98 private final RuleSet RULES = new RuleSet();
100 RULES.add(BASE, new ClassRule());
101 RULES.add(BASE + "/interfaces/interface", new InterfaceRule());
102 RULES.add(BASE + "/interfaces", new InterfacesRule());
103 RULES.add(BASE + "/outerclass", new OuterClassRule());
104 RULES.add(BASE + "/innerclass", new InnerClassRule());
105 RULES.add(BASE + "/source", new SourceRule());
106 RULES.add(BASE + "/field", new FieldRule());
108 RULES.add(BASE + "/method", new MethodRule());
109 RULES.add(BASE + "/method/exceptions/exception", new ExceptionRule());
110 RULES.add(BASE + "/method/exceptions", new ExceptionsRule());
112 RULES.add(BASE + "/method/annotationDefault",
113 new AnnotationDefaultRule());
115 RULES.add(BASE + "/method/code/*", new OpcodesRule()); // opcodes
117 RULES.add(BASE + "/method/code/TABLESWITCH", new TableSwitchRule());
118 RULES.add(BASE + "/method/code/TABLESWITCH/label",
119 new TableSwitchLabelRule());
120 RULES.add(BASE + "/method/code/LOOKUPSWITCH", new LookupSwitchRule());
121 RULES.add(BASE + "/method/code/LOOKUPSWITCH/label",
122 new LookupSwitchLabelRule());
124 RULES.add(BASE + "/method/code/Label", new LabelRule());
125 RULES.add(BASE + "/method/code/TryCatch", new TryCatchRule());
126 RULES.add(BASE + "/method/code/LineNumber", new LineNumberRule());
127 RULES.add(BASE + "/method/code/LocalVar", new LocalVarRule());
128 RULES.add(BASE + "/method/code/Max", new MaxRule());
130 RULES.add("*/annotation", new AnnotationRule());
131 RULES.add("*/parameterAnnotation", new AnnotationParameterRule());
132 RULES.add("*/annotationValue", new AnnotationValueRule());
133 RULES.add("*/annotationValueAnnotation",
134 new AnnotationValueAnnotationRule());
135 RULES.add("*/annotationValueEnum", new AnnotationValueEnumRule());
136 RULES.add("*/annotationValueArray", new AnnotationValueArrayRule());
139 private static interface OpcodeGroup {
140 public static final int INSN = 0;
141 public static final int INSN_INT = 1;
142 public static final int INSN_VAR = 2;
143 public static final int INSN_TYPE = 3;
144 public static final int INSN_FIELD = 4;
145 public static final int INSN_METHOD = 5;
146 public static final int INSN_JUMP = 6;
147 public static final int INSN_LDC = 7;
148 public static final int INSN_IINC = 8;
149 public static final int INSN_MULTIANEWARRAY = 9;
153 * Map of the opcode names to opcode and opcode group
155 static final Map OPCODES = new HashMap();
157 OPCODES.put("NOP", new Opcode(NOP, OpcodeGroup.INSN));
158 OPCODES.put("ACONST_NULL", new Opcode(ACONST_NULL, OpcodeGroup.INSN));
159 OPCODES.put("ICONST_M1", new Opcode(ICONST_M1, OpcodeGroup.INSN));
160 OPCODES.put("ICONST_0", new Opcode(ICONST_0, OpcodeGroup.INSN));
161 OPCODES.put("ICONST_1", new Opcode(ICONST_1, OpcodeGroup.INSN));
162 OPCODES.put("ICONST_2", new Opcode(ICONST_2, OpcodeGroup.INSN));
163 OPCODES.put("ICONST_3", new Opcode(ICONST_3, OpcodeGroup.INSN));
164 OPCODES.put("ICONST_4", new Opcode(ICONST_4, OpcodeGroup.INSN));
165 OPCODES.put("ICONST_5", new Opcode(ICONST_5, OpcodeGroup.INSN));
166 OPCODES.put("LCONST_0", new Opcode(LCONST_0, OpcodeGroup.INSN));
167 OPCODES.put("LCONST_1", new Opcode(LCONST_1, OpcodeGroup.INSN));
168 OPCODES.put("FCONST_0", new Opcode(FCONST_0, OpcodeGroup.INSN));
169 OPCODES.put("FCONST_1", new Opcode(FCONST_1, OpcodeGroup.INSN));
170 OPCODES.put("FCONST_2", new Opcode(FCONST_2, OpcodeGroup.INSN));
171 OPCODES.put("DCONST_0", new Opcode(DCONST_0, OpcodeGroup.INSN));
172 OPCODES.put("DCONST_1", new Opcode(DCONST_1, OpcodeGroup.INSN));
173 OPCODES.put("BIPUSH", new Opcode(BIPUSH, OpcodeGroup.INSN_INT));
174 OPCODES.put("SIPUSH", new Opcode(SIPUSH, OpcodeGroup.INSN_INT));
175 OPCODES.put("LDC", new Opcode(LDC, OpcodeGroup.INSN_LDC));
176 OPCODES.put("ILOAD", new Opcode(ILOAD, OpcodeGroup.INSN_VAR));
177 OPCODES.put("LLOAD", new Opcode(LLOAD, OpcodeGroup.INSN_VAR));
178 OPCODES.put("FLOAD", new Opcode(FLOAD, OpcodeGroup.INSN_VAR));
179 OPCODES.put("DLOAD", new Opcode(DLOAD, OpcodeGroup.INSN_VAR));
180 OPCODES.put("ALOAD", new Opcode(ALOAD, OpcodeGroup.INSN_VAR));
181 OPCODES.put("IALOAD", new Opcode(IALOAD, OpcodeGroup.INSN));
182 OPCODES.put("LALOAD", new Opcode(LALOAD, OpcodeGroup.INSN));
183 OPCODES.put("FALOAD", new Opcode(FALOAD, OpcodeGroup.INSN));
184 OPCODES.put("DALOAD", new Opcode(DALOAD, OpcodeGroup.INSN));
185 OPCODES.put("AALOAD", new Opcode(AALOAD, OpcodeGroup.INSN));
186 OPCODES.put("BALOAD", new Opcode(BALOAD, OpcodeGroup.INSN));
187 OPCODES.put("CALOAD", new Opcode(CALOAD, OpcodeGroup.INSN));
188 OPCODES.put("SALOAD", new Opcode(SALOAD, OpcodeGroup.INSN));
189 OPCODES.put("ISTORE", new Opcode(ISTORE, OpcodeGroup.INSN_VAR));
190 OPCODES.put("LSTORE", new Opcode(LSTORE, OpcodeGroup.INSN_VAR));
191 OPCODES.put("FSTORE", new Opcode(FSTORE, OpcodeGroup.INSN_VAR));
192 OPCODES.put("DSTORE", new Opcode(DSTORE, OpcodeGroup.INSN_VAR));
193 OPCODES.put("ASTORE", new Opcode(ASTORE, OpcodeGroup.INSN_VAR));
194 OPCODES.put("IASTORE", new Opcode(IASTORE, OpcodeGroup.INSN));
195 OPCODES.put("LASTORE", new Opcode(LASTORE, OpcodeGroup.INSN));
196 OPCODES.put("FASTORE", new Opcode(FASTORE, OpcodeGroup.INSN));
197 OPCODES.put("DASTORE", new Opcode(DASTORE, OpcodeGroup.INSN));
198 OPCODES.put("AASTORE", new Opcode(AASTORE, OpcodeGroup.INSN));
199 OPCODES.put("BASTORE", new Opcode(BASTORE, OpcodeGroup.INSN));
200 OPCODES.put("CASTORE", new Opcode(CASTORE, OpcodeGroup.INSN));
201 OPCODES.put("SASTORE", new Opcode(SASTORE, OpcodeGroup.INSN));
202 OPCODES.put("POP", new Opcode(POP, OpcodeGroup.INSN));
203 OPCODES.put("POP2", new Opcode(POP2, OpcodeGroup.INSN));
204 OPCODES.put("DUP", new Opcode(DUP, OpcodeGroup.INSN));
205 OPCODES.put("DUP_X1", new Opcode(DUP_X1, OpcodeGroup.INSN));
206 OPCODES.put("DUP_X2", new Opcode(DUP_X2, OpcodeGroup.INSN));
207 OPCODES.put("DUP2", new Opcode(DUP2, OpcodeGroup.INSN));
208 OPCODES.put("DUP2_X1", new Opcode(DUP2_X1, OpcodeGroup.INSN));
209 OPCODES.put("DUP2_X2", new Opcode(DUP2_X2, OpcodeGroup.INSN));
210 OPCODES.put("SWAP", new Opcode(SWAP, OpcodeGroup.INSN));
211 OPCODES.put("IADD", new Opcode(IADD, OpcodeGroup.INSN));
212 OPCODES.put("LADD", new Opcode(LADD, OpcodeGroup.INSN));
213 OPCODES.put("FADD", new Opcode(FADD, OpcodeGroup.INSN));
214 OPCODES.put("DADD", new Opcode(DADD, OpcodeGroup.INSN));
215 OPCODES.put("ISUB", new Opcode(ISUB, OpcodeGroup.INSN));
216 OPCODES.put("LSUB", new Opcode(LSUB, OpcodeGroup.INSN));
217 OPCODES.put("FSUB", new Opcode(FSUB, OpcodeGroup.INSN));
218 OPCODES.put("DSUB", new Opcode(DSUB, OpcodeGroup.INSN));
219 OPCODES.put("IMUL", new Opcode(IMUL, OpcodeGroup.INSN));
220 OPCODES.put("LMUL", new Opcode(LMUL, OpcodeGroup.INSN));
221 OPCODES.put("FMUL", new Opcode(FMUL, OpcodeGroup.INSN));
222 OPCODES.put("DMUL", new Opcode(DMUL, OpcodeGroup.INSN));
223 OPCODES.put("IDIV", new Opcode(IDIV, OpcodeGroup.INSN));
224 OPCODES.put("LDIV", new Opcode(LDIV, OpcodeGroup.INSN));
225 OPCODES.put("FDIV", new Opcode(FDIV, OpcodeGroup.INSN));
226 OPCODES.put("DDIV", new Opcode(DDIV, OpcodeGroup.INSN));
227 OPCODES.put("IREM", new Opcode(IREM, OpcodeGroup.INSN));
228 OPCODES.put("LREM", new Opcode(LREM, OpcodeGroup.INSN));
229 OPCODES.put("FREM", new Opcode(FREM, OpcodeGroup.INSN));
230 OPCODES.put("DREM", new Opcode(DREM, OpcodeGroup.INSN));
231 OPCODES.put("INEG", new Opcode(INEG, OpcodeGroup.INSN));
232 OPCODES.put("LNEG", new Opcode(LNEG, OpcodeGroup.INSN));
233 OPCODES.put("FNEG", new Opcode(FNEG, OpcodeGroup.INSN));
234 OPCODES.put("DNEG", new Opcode(DNEG, OpcodeGroup.INSN));
235 OPCODES.put("ISHL", new Opcode(ISHL, OpcodeGroup.INSN));
236 OPCODES.put("LSHL", new Opcode(LSHL, OpcodeGroup.INSN));
237 OPCODES.put("ISHR", new Opcode(ISHR, OpcodeGroup.INSN));
238 OPCODES.put("LSHR", new Opcode(LSHR, OpcodeGroup.INSN));
239 OPCODES.put("IUSHR", new Opcode(IUSHR, OpcodeGroup.INSN));
240 OPCODES.put("LUSHR", new Opcode(LUSHR, OpcodeGroup.INSN));
241 OPCODES.put("IAND", new Opcode(IAND, OpcodeGroup.INSN));
242 OPCODES.put("LAND", new Opcode(LAND, OpcodeGroup.INSN));
243 OPCODES.put("IOR", new Opcode(IOR, OpcodeGroup.INSN));
244 OPCODES.put("LOR", new Opcode(LOR, OpcodeGroup.INSN));
245 OPCODES.put("IXOR", new Opcode(IXOR, OpcodeGroup.INSN));
246 OPCODES.put("LXOR", new Opcode(LXOR, OpcodeGroup.INSN));
247 OPCODES.put("IINC", new Opcode(IINC, OpcodeGroup.INSN_IINC));
248 OPCODES.put("I2L", new Opcode(I2L, OpcodeGroup.INSN));
249 OPCODES.put("I2F", new Opcode(I2F, OpcodeGroup.INSN));
250 OPCODES.put("I2D", new Opcode(I2D, OpcodeGroup.INSN));
251 OPCODES.put("L2I", new Opcode(L2I, OpcodeGroup.INSN));
252 OPCODES.put("L2F", new Opcode(L2F, OpcodeGroup.INSN));
253 OPCODES.put("L2D", new Opcode(L2D, OpcodeGroup.INSN));
254 OPCODES.put("F2I", new Opcode(F2I, OpcodeGroup.INSN));
255 OPCODES.put("F2L", new Opcode(F2L, OpcodeGroup.INSN));
256 OPCODES.put("F2D", new Opcode(F2D, OpcodeGroup.INSN));
257 OPCODES.put("D2I", new Opcode(D2I, OpcodeGroup.INSN));
258 OPCODES.put("D2L", new Opcode(D2L, OpcodeGroup.INSN));
259 OPCODES.put("D2F", new Opcode(D2F, OpcodeGroup.INSN));
260 OPCODES.put("I2B", new Opcode(I2B, OpcodeGroup.INSN));
261 OPCODES.put("I2C", new Opcode(I2C, OpcodeGroup.INSN));
262 OPCODES.put("I2S", new Opcode(I2S, OpcodeGroup.INSN));
263 OPCODES.put("LCMP", new Opcode(LCMP, OpcodeGroup.INSN));
264 OPCODES.put("FCMPL", new Opcode(FCMPL, OpcodeGroup.INSN));
265 OPCODES.put("FCMPG", new Opcode(FCMPG, OpcodeGroup.INSN));
266 OPCODES.put("DCMPL", new Opcode(DCMPL, OpcodeGroup.INSN));
267 OPCODES.put("DCMPG", new Opcode(DCMPG, OpcodeGroup.INSN));
268 OPCODES.put("IFEQ", new Opcode(IFEQ, OpcodeGroup.INSN_JUMP));
269 OPCODES.put("IFNE", new Opcode(IFNE, OpcodeGroup.INSN_JUMP));
270 OPCODES.put("IFLT", new Opcode(IFLT, OpcodeGroup.INSN_JUMP));
271 OPCODES.put("IFGE", new Opcode(IFGE, OpcodeGroup.INSN_JUMP));
272 OPCODES.put("IFGT", new Opcode(IFGT, OpcodeGroup.INSN_JUMP));
273 OPCODES.put("IFLE", new Opcode(IFLE, OpcodeGroup.INSN_JUMP));
274 OPCODES.put("IF_ICMPEQ", new Opcode(IF_ICMPEQ, OpcodeGroup.INSN_JUMP));
275 OPCODES.put("IF_ICMPNE", new Opcode(IF_ICMPNE, OpcodeGroup.INSN_JUMP));
276 OPCODES.put("IF_ICMPLT", new Opcode(IF_ICMPLT, OpcodeGroup.INSN_JUMP));
277 OPCODES.put("IF_ICMPGE", new Opcode(IF_ICMPGE, OpcodeGroup.INSN_JUMP));
278 OPCODES.put("IF_ICMPGT", new Opcode(IF_ICMPGT, OpcodeGroup.INSN_JUMP));
279 OPCODES.put("IF_ICMPLE", new Opcode(IF_ICMPLE, OpcodeGroup.INSN_JUMP));
280 OPCODES.put("IF_ACMPEQ", new Opcode(IF_ACMPEQ, OpcodeGroup.INSN_JUMP));
281 OPCODES.put("IF_ACMPNE", new Opcode(IF_ACMPNE, OpcodeGroup.INSN_JUMP));
282 OPCODES.put("GOTO", new Opcode(GOTO, OpcodeGroup.INSN_JUMP));
283 OPCODES.put("JSR", new Opcode(JSR, OpcodeGroup.INSN_JUMP));
284 OPCODES.put("RET", new Opcode(RET, OpcodeGroup.INSN_VAR));
285 // OPCODES.put( "TABLESWITCH", new Opcode( TABLESWITCH,
286 // "visiTableSwitchInsn"));
287 // OPCODES.put( "LOOKUPSWITCH", new Opcode( LOOKUPSWITCH,
288 // "visitLookupSwitch"));
289 OPCODES.put("IRETURN", new Opcode(IRETURN, OpcodeGroup.INSN));
290 OPCODES.put("LRETURN", new Opcode(LRETURN, OpcodeGroup.INSN));
291 OPCODES.put("FRETURN", new Opcode(FRETURN, OpcodeGroup.INSN));
292 OPCODES.put("DRETURN", new Opcode(DRETURN, OpcodeGroup.INSN));
293 OPCODES.put("ARETURN", new Opcode(ARETURN, OpcodeGroup.INSN));
294 OPCODES.put("RETURN", new Opcode(RETURN, OpcodeGroup.INSN));
295 OPCODES.put("GETSTATIC", new Opcode(GETSTATIC, OpcodeGroup.INSN_FIELD));
296 OPCODES.put("PUTSTATIC", new Opcode(PUTSTATIC, OpcodeGroup.INSN_FIELD));
297 OPCODES.put("GETFIELD", new Opcode(GETFIELD, OpcodeGroup.INSN_FIELD));
298 OPCODES.put("PUTFIELD", new Opcode(PUTFIELD, OpcodeGroup.INSN_FIELD));
299 OPCODES.put("INVOKEVIRTUAL", new Opcode(INVOKEVIRTUAL,
300 OpcodeGroup.INSN_METHOD));
301 OPCODES.put("INVOKESPECIAL", new Opcode(INVOKESPECIAL,
302 OpcodeGroup.INSN_METHOD));
303 OPCODES.put("INVOKESTATIC", new Opcode(INVOKESTATIC,
304 OpcodeGroup.INSN_METHOD));
305 OPCODES.put("INVOKEINTERFACE", new Opcode(INVOKEINTERFACE,
306 OpcodeGroup.INSN_METHOD));
307 OPCODES.put("NEW", new Opcode(NEW, OpcodeGroup.INSN_TYPE));
308 OPCODES.put("NEWARRAY", new Opcode(NEWARRAY, OpcodeGroup.INSN_INT));
309 OPCODES.put("ANEWARRAY", new Opcode(ANEWARRAY, OpcodeGroup.INSN_TYPE));
310 OPCODES.put("ARRAYLENGTH", new Opcode(ARRAYLENGTH, OpcodeGroup.INSN));
311 OPCODES.put("ATHROW", new Opcode(ATHROW, OpcodeGroup.INSN));
312 OPCODES.put("CHECKCAST", new Opcode(CHECKCAST, OpcodeGroup.INSN_TYPE));
313 OPCODES.put("INSTANCEOF", new Opcode(INSTANCEOF, OpcodeGroup.INSN_TYPE));
314 OPCODES.put("MONITORENTER", new Opcode(MONITORENTER, OpcodeGroup.INSN));
315 OPCODES.put("MONITOREXIT", new Opcode(MONITOREXIT, OpcodeGroup.INSN));
316 OPCODES.put("MULTIANEWARRAY", new Opcode(MULTIANEWARRAY,
317 OpcodeGroup.INSN_MULTIANEWARRAY));
318 OPCODES.put("IFNULL", new Opcode(IFNULL, OpcodeGroup.INSN_JUMP));
319 OPCODES.put("IFNONNULL", new Opcode(IFNONNULL, OpcodeGroup.INSN_JUMP));
323 * Constructs a new {@link ASMContentHandler ASMContentHandler} object.
325 * @param os output stream to write generated class.
326 * @param computeMax <tt>true</tt> if the maximum stack size and the
327 * maximum number of local variables must be automatically computed.
328 * This value is passed to {@link ClassWriter ClassWriter} instance.
330 public ASMContentHandler(OutputStream os, boolean computeMax) {
332 this.computeMax = computeMax;
336 * Returns the bytecode of the class that was build with underneath class
339 * @return the bytecode of the class that was build with underneath class
340 * writer or null if there are no classwriter created.
342 public byte[] toByteArray() {
343 return cw == null ? null : cw.toByteArray();
347 * Process notification of the start of an XML element being reached.
349 * @param ns - The Namespace URI, or the empty string if the element has no
350 * Namespace URI or if Namespace processing is not being performed.
351 * @param localName - The local name (without prefix), or the empty string
352 * if Namespace processing is not being performed.
353 * @param qName - The qualified name (with prefix), or the empty string if
354 * qualified names are not available.
355 * @param list - The attributes attached to the element. If there are no
356 * attributes, it shall be an empty Attributes object.
357 * @exception SAXException if a parsing error is to be reported
359 public final void startElement(
363 Attributes list) throws SAXException
365 // the actual element name is either in localName or qName, depending
366 // on whether the parser is namespace aware
367 String name = localName;
368 if (name == null || name.length() < 1) {
372 // Compute the current matching rule
373 StringBuffer sb = new StringBuffer(match);
374 if (match.length() > 0) {
378 match = sb.toString();
380 // Fire "begin" events for all relevant rules
381 Rule r = (Rule) RULES.match(match);
387 * Process notification of the end of an XML element being reached.
389 * @param ns - The Namespace URI, or the empty string if the element has no
390 * Namespace URI or if Namespace processing is not being performed.
391 * @param localName - The local name (without prefix), or the empty string
392 * if Namespace processing is not being performed.
393 * @param qName - The qualified XML 1.0 name (with prefix), or the empty
394 * string if qualified names are not available.
396 * @exception SAXException if a parsing error is to be reported
398 public final void endElement(String ns, String localName, String qName)
401 // the actual element name is either in localName or qName, depending
402 // on whether the parser is namespace aware
403 String name = localName;
404 if (name == null || name.length() < 1) {
408 // Fire "end" events for all relevant rules in reverse order
409 Rule r = (Rule) RULES.match(match);
413 // Recover the previous match expression
414 int slash = match.lastIndexOf('/');
416 match = match.substring(0, slash);
423 * Process notification of the end of a document and write generated
424 * bytecode into output stream.
426 * @exception SAXException if parsing or writing error is to be reported.
428 public final void endDocument() throws SAXException {
430 os.write(cw.toByteArray());
431 } catch (IOException ex) {
432 throw new SAXException(ex.toString(), ex);
437 * Return the top object on the stack without removing it. If there are no
438 * objects on the stack, return <code>null</code>.
440 * @return the top object on the stack without removing it.
442 final Object peek() {
443 return stack.size() == 0 ? null : stack.get(stack.size() - 1);
447 * Return the n'th object down the stack, where 0 is the top element and
448 * [getCount()-1] is the bottom element. If the specified index is out of
449 * range, return <code>null</code>.
451 * @param n Index of the desired element, where 0 is the top of the stack, 1
452 * is the next element down, and so on.
453 * @return the n'th object down the stack.
455 final Object peek(int n) {
456 return stack.size() < (n + 1) ? null : stack.get(n);
460 * Pop the top object off of the stack, and return it. If there are no
461 * objects on the stack, return <code>null</code>.
463 * @return the top object off of the stack.
466 return stack.size() == 0 ? null : stack.remove(stack.size() - 1);
470 * Push a new object onto the top of the object stack.
472 * @param object The new object
474 final void push(Object object) {
478 private static final class RuleSet {
479 private Map rules = new HashMap();
481 private List lpatterns = new ArrayList();
483 private List rpatterns = new ArrayList();
485 public void add(String path, Object rule) {
486 String pattern = path;
487 if (path.startsWith("*/")) {
488 pattern = path.substring(1);
489 lpatterns.add(pattern);
490 } else if (path.endsWith("/*")) {
491 pattern = path.substring(0, path.length() - 1);
492 rpatterns.add(pattern);
494 rules.put(pattern, rule);
497 public Object match(String path) {
498 if (rules.containsKey(path)) {
499 return rules.get(path);
502 int n = path.lastIndexOf('/');
503 for (Iterator it = lpatterns.iterator(); it.hasNext();) {
504 String pattern = (String) it.next();
505 if (path.substring(n).endsWith(pattern)) {
506 return rules.get(pattern);
510 for (Iterator it = rpatterns.iterator(); it.hasNext();) {
511 String pattern = (String) it.next();
512 if (path.startsWith(pattern)) {
513 return rules.get(pattern);
525 protected abstract class Rule {
527 public void begin(String name, Attributes attrs) {
530 public void end(String name) {
533 protected final Object getValue(String desc, String val) {
536 if (desc.equals("Ljava/lang/String;")) {
538 } else if ("Ljava/lang/Integer;".equals(desc)
539 || "I".equals(desc) || "S".equals(desc)
540 || "B".equals(desc) || "C".equals(desc)
543 value = new Integer(val);
545 } else if ("Ljava/lang/Short;".equals(desc)) {
546 value = new Short(val);
548 } else if ("Ljava/lang/Byte;".equals(desc)) {
549 value = new Byte(val);
551 } else if ("Ljava/lang/Character;".equals(desc)) {
552 value = new Character(decode(val).charAt(0));
554 } else if ("Ljava/lang/Boolean;".equals(desc)) {
555 value = Boolean.valueOf(val);
557 // } else if ("Ljava/lang/Integer;".equals(desc)
558 // || desc.equals("I"))
560 // value = new Integer(val);
561 // } else if ("Ljava/lang/Character;".equals(desc)
562 // || desc.equals("C"))
564 // value = new Character(decode(val).charAt(0));
565 // } else if ("Ljava/lang/Short;".equals(desc) ||
568 // value = Short.valueOf(val);
569 // } else if ("Ljava/lang/Byte;".equals(desc) ||
572 // value = Byte.valueOf(val);
574 } else if ("Ljava/lang/Long;".equals(desc) || desc.equals("J"))
576 value = new Long(val);
577 } else if ("Ljava/lang/Float;".equals(desc) || desc.equals("F"))
579 value = new Float(val);
580 } else if ("Ljava/lang/Double;".equals(desc)
583 value = new Double(val);
584 } else if (Type.getDescriptor(Type.class).equals(desc)) {
585 value = Type.getType(val);
587 // } else if ("[I".equals(desc)) {
588 // value = new int[0]; // TODO
589 // } else if ("[C".equals(desc)) {
590 // value = new char[0]; // TODO
591 // } else if ("[Z".equals(desc)) {
592 // value = new boolean[0]; // TODO
593 // } else if ("[S".equals(desc)) {
594 // value = new short[0]; // TODO
595 // } else if ("[B".equals(desc)) {
596 // value = new byte[0]; // TODO
597 // } else if ("[J".equals(desc)) {
598 // value = new long[0]; // TODO
599 // } else if ("[F".equals(desc)) {
600 // value = new float[0]; // TODO
601 // } else if ("[D".equals(desc)) {
602 // value = new double[0]; // TODO
605 throw new RuntimeException("Invalid value:" + val
606 + " desc:" + desc + " ctx:" + this);
612 private final String decode(String val) {
613 StringBuffer sb = new StringBuffer(val.length());
616 while (n < val.length()) {
617 char c = val.charAt(n);
625 sb.append((char) Integer.parseInt(val.substring(n,
635 } catch (RuntimeException ex) {
636 System.err.println(val + "\n" + ex.toString());
637 ex.printStackTrace();
640 return sb.toString();
643 protected final Label getLabel(Object label) {
644 Label lbl = (Label) labels.get(label);
647 labels.put(label, lbl);
652 // TODO verify move to stack
653 protected final MethodVisitor getCodeVisitor() {
654 return (MethodVisitor) peek();
657 protected final int getAccess(String s) {
659 if (s.indexOf("public") != -1)
660 access |= Opcodes.ACC_PUBLIC;
661 if (s.indexOf("private") != -1)
662 access |= Opcodes.ACC_PRIVATE;
663 if (s.indexOf("protected") != -1)
664 access |= Opcodes.ACC_PROTECTED;
665 if (s.indexOf("static") != -1)
666 access |= Opcodes.ACC_STATIC;
667 if (s.indexOf("final") != -1)
668 access |= Opcodes.ACC_FINAL;
669 if (s.indexOf("super") != -1)
670 access |= Opcodes.ACC_SUPER;
671 if (s.indexOf("synchronized") != -1)
672 access |= Opcodes.ACC_SYNCHRONIZED;
673 if (s.indexOf("volatile") != -1)
674 access |= Opcodes.ACC_VOLATILE;
675 if (s.indexOf("bridge") != -1)
676 access |= Opcodes.ACC_BRIDGE;
677 if (s.indexOf("varargs") != -1)
678 access |= Opcodes.ACC_VARARGS;
679 if (s.indexOf("transient") != -1)
680 access |= Opcodes.ACC_TRANSIENT;
681 if (s.indexOf("native") != -1)
682 access |= Opcodes.ACC_NATIVE;
683 if (s.indexOf("interface") != -1)
684 access |= Opcodes.ACC_INTERFACE;
685 if (s.indexOf("abstract") != -1)
686 access |= Opcodes.ACC_ABSTRACT;
687 if (s.indexOf("strict") != -1)
688 access |= Opcodes.ACC_STRICT;
689 if (s.indexOf("synthetic") != -1)
690 access |= Opcodes.ACC_SYNTHETIC;
691 if (s.indexOf("annotation") != -1)
692 access |= Opcodes.ACC_ANNOTATION;
693 if (s.indexOf("enum") != -1)
694 access |= Opcodes.ACC_ENUM;
695 if (s.indexOf("deprecated") != -1)
696 access |= Opcodes.ACC_DEPRECATED;
705 private final class ClassRule extends Rule {
707 public final void begin(String name, Attributes attrs) {
708 int major = Integer.parseInt(attrs.getValue("major"));
709 int minor = Integer.parseInt(attrs.getValue("minor"));
710 cw = new ClassWriter(computeMax);
711 Map vals = new HashMap();
712 vals.put("version", new Integer(minor << 16 | major));
713 vals.put("access", attrs.getValue("access"));
714 vals.put("name", attrs.getValue("name"));
715 vals.put("parent", attrs.getValue("parent"));
716 vals.put("source", attrs.getValue("source"));
717 vals.put("signature", attrs.getValue("signature"));
718 vals.put("interfaces", new ArrayList());
720 // values will be extracted in InterfacesRule.end();
725 private final class SourceRule extends Rule {
727 public void begin(String name, Attributes attrs) {
728 String file = attrs.getValue("file");
729 String debug = attrs.getValue("debug");
730 cw.visitSource(file, debug);
738 private final class InterfaceRule extends Rule {
740 public final void begin(String name, Attributes attrs) {
741 ((List) ((Map) peek()).get("interfaces")).add(attrs.getValue("name"));
749 private final class InterfacesRule extends Rule {
751 public final void end(String element) {
752 Map vals = (Map) pop();
753 int version = ((Integer) vals.get("version")).intValue();
754 int access = getAccess((String) vals.get("access"));
755 String name = (String) vals.get("name");
756 String signature = (String) vals.get("signature");
757 String parent = (String) vals.get("parent");
758 List infs = (List) vals.get("interfaces");
759 String[] interfaces = (String[]) infs.toArray(new String[infs.size()]);
760 cw.visit(version, access, name, signature, parent, interfaces);
769 private final class OuterClassRule extends Rule {
771 public final void begin(String element, Attributes attrs) {
772 String owner = attrs.getValue("owner");
773 String name = attrs.getValue("name");
774 String desc = attrs.getValue("desc");
775 cw.visitOuterClass(owner, name, desc);
783 private final class InnerClassRule extends Rule {
785 public final void begin(String element, Attributes attrs) {
786 int access = getAccess(attrs.getValue("access"));
787 String name = attrs.getValue("name");
788 String outerName = attrs.getValue("outerName");
789 String innerName = attrs.getValue("innerName");
790 cw.visitInnerClass(name, outerName, innerName, access);
798 private final class FieldRule extends Rule {
800 public final void begin(String element, Attributes attrs) {
801 int access = getAccess(attrs.getValue("access"));
802 String name = attrs.getValue("name");
803 String signature = attrs.getValue("signature");
804 String desc = attrs.getValue("desc");
805 Object value = getValue(desc, attrs.getValue("value"));
806 push(cw.visitField(access, name, desc, signature, value));
809 public void end(String name) {
810 ((FieldVisitor) pop()).visitEnd();
818 private final class MethodRule extends Rule {
820 public final void begin(String name, Attributes attrs) {
821 labels = new HashMap();
822 Map vals = new HashMap();
823 vals.put("access", attrs.getValue("access"));
824 vals.put("name", attrs.getValue("name"));
825 vals.put("desc", attrs.getValue("desc"));
826 vals.put("signature", attrs.getValue("signature"));
827 vals.put("exceptions", new ArrayList());
829 // values will be extracted in ExceptionsRule.end();
832 public final void end(String name) {
833 ((MethodVisitor) pop()).visitEnd();
842 private final class ExceptionRule extends Rule {
844 public final void begin(String name, Attributes attrs) {
845 ((List) ((Map) peek()).get("exceptions")).add(attrs.getValue("name"));
853 private final class ExceptionsRule extends Rule {
855 public final void end(String element) {
856 Map vals = (Map) pop();
857 int access = getAccess((String) vals.get("access"));
858 String name = (String) vals.get("name");
859 String desc = (String) vals.get("desc");
860 String signature = (String) vals.get("signature");
861 List excs = (List) vals.get("exceptions");
862 String[] exceptions = (String[]) excs.toArray(new String[excs.size()]);
864 push(cw.visitMethod(access, name, desc, signature, exceptions));
872 private class TableSwitchRule extends Rule {
874 public final void begin(String name, Attributes attrs) {
875 Map vals = new HashMap();
876 vals.put("min", attrs.getValue("min"));
877 vals.put("max", attrs.getValue("max"));
878 vals.put("dflt", attrs.getValue("dflt"));
879 vals.put("labels", new ArrayList());
883 public final void end(String name) {
884 Map vals = (Map) pop();
885 int min = Integer.parseInt((String) vals.get("min"));
886 int max = Integer.parseInt((String) vals.get("max"));
887 Label dflt = getLabel(vals.get("dflt"));
888 List lbls = (List) vals.get("labels");
889 Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]);
890 getCodeVisitor().visitTableSwitchInsn(min, max, dflt, labels);
896 * TableSwitchLabelRule
898 private final class TableSwitchLabelRule extends Rule {
900 public final void begin(String name, Attributes attrs) {
901 ((List) ((Map) peek()).get("labels")).add(getLabel(attrs.getValue("name")));
909 private final class LookupSwitchRule extends Rule {
911 public final void begin(String name, Attributes attrs) {
912 Map vals = new HashMap();
913 vals.put("dflt", attrs.getValue("dflt"));
914 vals.put("labels", new ArrayList());
915 vals.put("keys", new ArrayList());
919 public final void end(String name) {
920 Map vals = (Map) pop();
921 Label dflt = getLabel(vals.get("dflt"));
922 List keyList = (List) vals.get("keys");
923 List lbls = (List) vals.get("labels");
924 Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]);
925 int[] keys = new int[keyList.size()];
926 for (int i = 0; i < keys.length; i++) {
927 keys[i] = Integer.parseInt((String) keyList.get(i));
929 getCodeVisitor().visitLookupSwitchInsn(dflt, keys, labels);
935 * LookupSwitchLabelRule
937 private final class LookupSwitchLabelRule extends Rule {
939 public final void begin(String name, Attributes attrs) {
940 Map vals = (Map) peek();
941 ((List) vals.get("labels")).add(getLabel(attrs.getValue("name")));
942 ((List) vals.get("keys")).add(attrs.getValue("key"));
950 private final class LabelRule extends Rule {
952 public final void begin(String name, Attributes attrs) {
953 getCodeVisitor().visitLabel(getLabel(attrs.getValue("name")));
961 private final class TryCatchRule extends Rule {
963 public final void begin(String name, Attributes attrs) {
964 Label start = getLabel(attrs.getValue("start"));
965 Label end = getLabel(attrs.getValue("end"));
966 Label handler = getLabel(attrs.getValue("handler"));
967 String type = attrs.getValue("type");
968 getCodeVisitor().visitTryCatchBlock(start, end, handler, type);
976 private final class LineNumberRule extends Rule {
978 public final void begin(String name, Attributes attrs) {
979 int line = Integer.parseInt(attrs.getValue("line"));
980 Label start = getLabel(attrs.getValue("start"));
981 getCodeVisitor().visitLineNumber(line, start);
989 private final class LocalVarRule extends Rule {
991 public final void begin(String element, Attributes attrs) {
992 String name = attrs.getValue("name");
993 String desc = attrs.getValue("desc");
994 String signature = attrs.getValue("signature");
995 Label start = getLabel(attrs.getValue("start"));
996 Label end = getLabel(attrs.getValue("end"));
997 int var = Integer.parseInt(attrs.getValue("var"));
998 getCodeVisitor().visitLocalVariable(name,
1011 private final class OpcodesRule extends Rule {
1013 // public boolean match( String match, String element) {
1014 // return match.startsWith( path) && OPCODES.containsKey( element);
1017 public final void begin(String element, Attributes attrs) {
1018 Opcode o = ((Opcode) OPCODES.get(element));
1023 case OpcodeGroup.INSN:
1024 getCodeVisitor().visitInsn(o.opcode);
1027 case OpcodeGroup.INSN_FIELD:
1028 getCodeVisitor().visitFieldInsn(o.opcode,
1029 attrs.getValue("owner"),
1030 attrs.getValue("name"),
1031 attrs.getValue("desc"));
1034 case OpcodeGroup.INSN_INT:
1035 getCodeVisitor().visitIntInsn(o.opcode,
1036 Integer.parseInt(attrs.getValue("value")));
1039 case OpcodeGroup.INSN_JUMP:
1040 getCodeVisitor().visitJumpInsn(o.opcode,
1041 getLabel(attrs.getValue("label")));
1044 case OpcodeGroup.INSN_METHOD:
1045 getCodeVisitor().visitMethodInsn(o.opcode,
1046 attrs.getValue("owner"),
1047 attrs.getValue("name"),
1048 attrs.getValue("desc"));
1051 case OpcodeGroup.INSN_TYPE:
1052 getCodeVisitor().visitTypeInsn(o.opcode,
1053 attrs.getValue("desc"));
1056 case OpcodeGroup.INSN_VAR:
1057 getCodeVisitor().visitVarInsn(o.opcode,
1058 Integer.parseInt(attrs.getValue("var")));
1061 case OpcodeGroup.INSN_IINC:
1062 getCodeVisitor().visitIincInsn(Integer.parseInt(attrs.getValue("var")),
1063 Integer.parseInt(attrs.getValue("inc")));
1066 case OpcodeGroup.INSN_LDC:
1067 getCodeVisitor().visitLdcInsn(getValue(attrs.getValue("desc"),
1068 attrs.getValue("cst")));
1071 case OpcodeGroup.INSN_MULTIANEWARRAY:
1072 getCodeVisitor().visitMultiANewArrayInsn(attrs.getValue("desc"),
1073 Integer.parseInt(attrs.getValue("dims")));
1077 throw new RuntimeException("Invalid element: " + element
1087 private final class MaxRule extends Rule {
1089 public final void begin(String element, Attributes attrs) {
1090 int maxStack = Integer.parseInt(attrs.getValue("maxStack"));
1091 int maxLocals = Integer.parseInt(attrs.getValue("maxLocals"));
1092 getCodeVisitor().visitMaxs(maxStack, maxLocals);
1097 private final class AnnotationRule extends Rule {
1099 public void begin(String name, Attributes attrs) {
1100 String desc = attrs.getValue("desc");
1101 boolean visible = Boolean.valueOf(attrs.getValue("visible"))
1105 if (v instanceof ClassVisitor) {
1106 push(((ClassVisitor) v).visitAnnotation(desc, visible));
1107 } else if (v instanceof FieldVisitor) {
1108 push(((FieldVisitor) v).visitAnnotation(desc, visible));
1109 } else if (v instanceof MethodVisitor) {
1110 push(((MethodVisitor) v).visitAnnotation(desc, visible));
1114 public void end(String name) {
1115 ((AnnotationVisitor) pop()).visitEnd();
1120 private final class AnnotationParameterRule extends Rule {
1122 public void begin(String name, Attributes attrs) {
1123 int parameter = Integer.parseInt(attrs.getValue("parameter"));
1124 String desc = attrs.getValue("desc");
1125 boolean visible = Boolean.valueOf(attrs.getValue("visible"))
1128 push(((MethodVisitor) peek()).visitParameterAnnotation(parameter,
1133 public void end(String name) {
1134 ((AnnotationVisitor) pop()).visitEnd();
1139 private final class AnnotationValueRule extends Rule {
1141 public void begin(String nm, Attributes attrs) {
1142 String name = attrs.getValue("name");
1143 String desc = attrs.getValue("desc");
1144 String value = attrs.getValue("value");
1145 ((AnnotationVisitor) peek()).visit(name, getValue(desc, value));
1150 private final class AnnotationValueEnumRule extends Rule {
1152 public void begin(String nm, Attributes attrs) {
1153 String name = attrs.getValue("name");
1154 String desc = attrs.getValue("desc");
1155 String value = attrs.getValue("value");
1156 ((AnnotationVisitor) peek()).visitEnum(name, desc, value);
1161 private final class AnnotationValueAnnotationRule extends Rule {
1163 public void begin(String nm, Attributes attrs) {
1164 String name = attrs.getValue("name");
1165 String desc = attrs.getValue("desc");
1166 push(((AnnotationVisitor) peek()).visitAnnotation(name, desc));
1169 public void end(String name) {
1170 ((AnnotationVisitor) pop()).visitEnd();
1175 private final class AnnotationValueArrayRule extends Rule {
1177 public void begin(String nm, Attributes attrs) {
1178 String name = attrs.getValue("name");
1179 push(((AnnotationVisitor) peek()).visitArray(name));
1182 public void end(String name) {
1183 ((AnnotationVisitor) pop()).visitEnd();
1188 private final class AnnotationDefaultRule extends Rule {
1190 public void begin(String nm, Attributes attrs) {
1191 push(((MethodVisitor) peek()).visitAnnotationDefault());
1194 public void end(String name) {
1195 ((AnnotationVisitor) pop()).visitEnd();
1203 private final static class Opcode {
1208 public Opcode(int opcode, int type) {
1209 this.opcode = opcode;