001: /*
002: * Copyright (c) 2001-2007, Jean Tessier
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * * Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in the
014: * documentation and/or other materials provided with the distribution.
015: *
016: * * Neither the name of Jean Tessier nor the names of his contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032:
033: package com.jeantessier.classreader;
034:
035: import java.io.*;
036: import java.util.*;
037:
038: import org.apache.oro.text.perl.*;
039:
040: public class XMLPrinter extends Printer {
041: public static final String DEFAULT_ENCODING = "utf-8";
042: public static final String DEFAULT_DTD_PREFIX = "http://depfind.sourceforge.net/dtd";
043:
044: private static final BitFormat format = new BitFormat(16);
045: private static final Perl5Util perl = new Perl5Util();
046:
047: private boolean top = true;
048:
049: public XMLPrinter(PrintWriter out) {
050: this (out, DEFAULT_ENCODING, DEFAULT_DTD_PREFIX);
051: }
052:
053: public XMLPrinter(PrintWriter out, String encoding, String dtdPrefix) {
054: super (out);
055:
056: appendHeader(encoding, dtdPrefix);
057: }
058:
059: private void appendHeader(String encoding, String dtdPrefix) {
060: append("<?xml version=\"1.0\" encoding=\"").append(encoding)
061: .append("\" ?>").eol();
062: eol();
063: append("<!DOCTYPE classfiles SYSTEM \"").append(dtdPrefix)
064: .append("/classfile.dtd\">").eol();
065: eol();
066: }
067:
068: public void visitClassfiles(Collection<Classfile> classfiles) {
069: indent().append("<classfiles>").eol();
070: raiseIndent();
071:
072: super .visitClassfiles(classfiles);
073:
074: lowerIndent();
075: indent().append("</classfiles>").eol();
076: }
077:
078: public void visitClassfile(Classfile classfile) {
079: indent().append("<classfile magic-number=\"").append(
080: classfile.getMagicNumber()).append(
081: "\" minor-version=\"").append(
082: classfile.getMinorVersion()).append(
083: "\" major-version=\"").append(
084: classfile.getMajorVersion())
085: .append("\" access-flag=\"").append(
086: format.format(classfile.getAccessFlag()))
087: .append("\">").eol();
088: raiseIndent();
089:
090: top = true;
091: classfile.getConstantPool().accept(this );
092: top = false;
093:
094: if (classfile.isPublic())
095: indent().append("<public/>").eol();
096: if (classfile.isFinal())
097: indent().append("<final/>").eol();
098: if (classfile.isSuper())
099: indent().append("<super/>").eol();
100: if (classfile.isInterface())
101: indent().append("<is-interface/>").eol();
102: if (classfile.isAbstract())
103: indent().append("<abstract/>").eol();
104:
105: indent();
106: append("<this-class>");
107: classfile.getRawClass().accept(this );
108: append("</this-class>").eol();
109:
110: indent();
111: append("<superclass>");
112: if (classfile.getSuperclassIndex() != 0) {
113: classfile.getRawSuperclass().accept(this );
114: }
115: append("</superclass>").eol();
116:
117: if (!classfile.getAllInterfaces().isEmpty()) {
118: indent().append("<interfaces>").eol();
119: raiseIndent();
120: for (Class_info class_info : classfile.getAllInterfaces()) {
121: indent();
122: append("<interface>");
123: class_info.accept(this );
124: append("</interface>").eol();
125: }
126: lowerIndent();
127: indent().append("</interfaces>").eol();
128: }
129:
130: if (!classfile.getAllFields().isEmpty()) {
131: indent().append("<fields>").eol();
132: raiseIndent();
133: for (Field_info field : classfile.getAllFields()) {
134: field.accept(this );
135: }
136: lowerIndent();
137: indent().append("</fields>").eol();
138: }
139:
140: if (!classfile.getAllMethods().isEmpty()) {
141: indent().append("<methods>").eol();
142: raiseIndent();
143: for (Method_info method : classfile.getAllMethods()) {
144: method.accept(this );
145: }
146: lowerIndent();
147: indent().append("</methods>").eol();
148: }
149:
150: if (!classfile.getAttributes().isEmpty()) {
151: indent().append("<attributes>").eol();
152: raiseIndent();
153: for (Attribute_info attribute : classfile.getAttributes()) {
154: attribute.accept(this );
155: }
156: lowerIndent();
157: indent().append("</attributes>").eol();
158: }
159:
160: lowerIndent();
161: indent().append("</classfile>").eol();
162: }
163:
164: public void visitConstantPool(ConstantPool constantPool) {
165: resetCount();
166:
167: indent().append("<constant-pool>").eol();
168: raiseIndent();
169:
170: for (ConstantPoolEntry entry : constantPool) {
171: if (entry != null) {
172: entry.accept(this );
173: }
174: incrementCount();
175: }
176:
177: lowerIndent();
178: indent().append("</constant-pool>").eol();
179: }
180:
181: public void visitClass_info(Class_info entry) {
182: if (top) {
183: top = false;
184: indent();
185: append("<class id=\"").append(currentCount()).append("\">");
186: // entry.getRawName().accept(this);
187: append(entry.getName());
188: append("</class>").eol();
189: top = true;
190: } else {
191: // entry.getRawName().accept(this);
192: append(entry.getName());
193: }
194: }
195:
196: public void visitFieldRef_info(FieldRef_info entry) {
197: Class_info c = entry.getRawClass();
198: NameAndType_info nat = entry.getRawNameAndType();
199:
200: if (top) {
201: top = false;
202: indent();
203: append("<field-ref-info id=\"").append(currentCount())
204: .append("\">");
205: append("<class>");
206: c.accept(this );
207: append("</class>");
208: append("<type>");
209: nat.getRawType().accept(this );
210: append("</type>");
211: append("<name>");
212: nat.getRawName().accept(this );
213: append("</name>");
214: append("</field-ref-info>").eol();
215: top = true;
216: } else {
217: append(SignatureHelper.getType(nat.getType()));
218: append(" ");
219: append(entry.getFullSignature());
220: }
221: }
222:
223: public void visitMethodRef_info(MethodRef_info entry) {
224: Class_info c = entry.getRawClass();
225: NameAndType_info nat = entry.getRawNameAndType();
226:
227: if (top) {
228: top = false;
229: indent();
230: append("<method-ref-info id=\"").append(currentCount())
231: .append("\">");
232: append("<class>");
233: c.accept(this );
234: append("</class>");
235: append("<name>");
236: nat.getRawName().accept(this );
237: append("</name>");
238: append("<type>");
239: nat.getRawType().accept(this );
240: append("</type>");
241: append("</method-ref-info>").eol();
242: top = true;
243: } else {
244: if (!entry.isConstructor() && !entry.isStaticInitializer()) {
245: append(SignatureHelper.getReturnType(nat.getType()))
246: .append(" ");
247: }
248: append(entry.getFullSignature());
249: }
250: }
251:
252: public void visitInterfaceMethodRef_info(
253: InterfaceMethodRef_info entry) {
254: Class_info c = entry.getRawClass();
255: NameAndType_info nat = entry.getRawNameAndType();
256:
257: if (top) {
258: top = false;
259: indent();
260: append("<interface-method-ref-info id=\"").append(
261: currentCount()).append("\">");
262: append("<class>");
263: c.accept(this );
264: append("</class>");
265: append("<name>");
266: nat.getRawName().accept(this );
267: append("</name>");
268: append("<type>");
269: nat.getRawType().accept(this );
270: append("</type>");
271: append("</interface-method-ref-info>").eol();
272: top = true;
273: } else {
274: append(SignatureHelper.getReturnType(nat.getType()));
275: append(" ");
276: append(entry.getFullSignature());
277: }
278: }
279:
280: public void visitString_info(String_info entry) {
281: if (top) {
282: top = false;
283: indent();
284: append("<string-info id=\"").append(currentCount()).append(
285: "\">");
286: entry.getRawValue().accept(this );
287: append("</string-info>").eol();
288: top = true;
289: } else {
290: entry.getRawValue().accept(this );
291: }
292: }
293:
294: public void visitInteger_info(Integer_info entry) {
295: if (top) {
296: top = false;
297: indent();
298: append("<integer-info id=\"").append(currentCount())
299: .append("\">");
300: append(entry.getValue());
301: append("</integer-info>").eol();
302: top = true;
303: } else {
304: append(entry.getValue());
305: }
306: }
307:
308: public void visitFloat_info(Float_info entry) {
309: if (top) {
310: top = false;
311: indent();
312: append("<float-info id=\"").append(currentCount()).append(
313: "\">");
314: append(entry.getValue());
315: append("</float-info>").eol();
316: top = true;
317: } else {
318: append(entry.getValue());
319: }
320: }
321:
322: public void visitLong_info(Long_info entry) {
323: if (top) {
324: top = false;
325: indent();
326: append("<long-info id=\"").append(currentCount()).append(
327: "\">");
328: append(entry.getValue());
329: append("</long-info>").eol();
330: top = true;
331: } else {
332: append(entry.getValue());
333: }
334: }
335:
336: public void visitDouble_info(Double_info entry) {
337: if (top) {
338: top = false;
339: indent();
340: append("<double-info id=\"").append(currentCount()).append(
341: "\">");
342: append(entry.getValue());
343: append("</double-info>").eol();
344: top = true;
345: } else {
346: append(entry.getValue());
347: }
348: }
349:
350: public void visitNameAndType_info(NameAndType_info entry) {
351: if (top) {
352: top = false;
353: indent();
354: append("<name-and-type-info id=\"").append(currentCount())
355: .append("\">");
356: append("<name>");
357: entry.getRawName().accept(this );
358: append("</name>");
359: append("<type>");
360: entry.getRawType().accept(this );
361: append("</type>");
362: append("</name-and-type-info>").eol();
363: top = true;
364: } else {
365: entry.getRawName().accept(this );
366: append(" ");
367: entry.getRawType().accept(this );
368: }
369: }
370:
371: public void visitUTF8_info(UTF8_info entry) {
372: if (top) {
373: top = false;
374: indent().append("<utf8-info id=\"").append(currentCount())
375: .append("\">");
376: append(escapeXMLCharacters(entry.getValue()));
377: append("</utf8-info>").eol();
378: top = true;
379: } else {
380: append(escapeXMLCharacters(entry.getValue()));
381: }
382: }
383:
384: public void visitField_info(Field_info entry) {
385: indent().append("<field-info access-flag=\"").append(
386: format.format(entry.getAccessFlag())).append("\">")
387: .eol();
388: raiseIndent();
389:
390: if (entry.isPublic())
391: indent().append("<public/>").eol();
392: if (entry.isProtected())
393: indent().append("<protected/>").eol();
394: if (entry.isPrivate())
395: indent().append("<private/>").eol();
396: if (entry.isStatic())
397: indent().append("<static/>").eol();
398: if (entry.isFinal())
399: indent().append("<final/>").eol();
400: if (entry.isVolatile())
401: indent().append("<volatile/>").eol();
402: if (entry.isTransient())
403: indent().append("<transient/>").eol();
404:
405: indent();
406: append("<name>");
407: entry.getRawName().accept(this );
408: append("</name>").eol();
409:
410: indent().append("<type>").append(entry.getType()).append(
411: "</type>").eol();
412:
413: if (!entry.getAttributes().isEmpty()) {
414: indent().append("<attributes>").eol();
415: raiseIndent();
416: super .visitField_info(entry);
417: lowerIndent();
418: indent().append("</attributes>").eol();
419: }
420:
421: lowerIndent();
422: indent().append("</field-info>").eol();
423: }
424:
425: public void visitMethod_info(Method_info entry) {
426: indent().append("<method-info access-flag=\"").append(
427: format.format(entry.getAccessFlag())).append("\">")
428: .eol();
429: raiseIndent();
430:
431: if (entry.isPublic())
432: indent().append("<public/>").eol();
433: if (entry.isProtected())
434: indent().append("<protected/>").eol();
435: if (entry.isPrivate())
436: indent().append("<private/>").eol();
437: if (entry.isStatic())
438: indent().append("<static/>").eol();
439: if (entry.isFinal())
440: indent().append("<final/>").eol();
441: if (entry.isSynchronized())
442: indent().append("<synchronized/>").eol();
443: if (entry.isNative())
444: indent().append("<native/>").eol();
445: if (entry.isAbstract())
446: indent().append("<abstract/>").eol();
447: if (entry.isStrict())
448: indent().append("<strict/>").eol();
449:
450: indent();
451: append("<name>");
452: entry.getRawName().accept(this );
453: append("</name>").eol();
454:
455: if (!entry.getName().equals("<init>")
456: && !entry.getName().equals("<clinit>")) {
457: indent().append("<return-type>").append(
458: (entry.getReturnType() != null) ? entry
459: .getReturnType() : "void").append(
460: "</return-type>").eol();
461: }
462: indent().append("<signature>").append(entry.getSignature())
463: .append("</signature>").eol();
464:
465: if (!entry.getAttributes().isEmpty()) {
466: indent().append("<attributes>").eol();
467: raiseIndent();
468: super .visitMethod_info(entry);
469: lowerIndent();
470: indent().append("</attributes>").eol();
471: }
472:
473: lowerIndent();
474: indent().append("</method-info>").eol();
475: }
476:
477: public void visitConstantValue_attribute(
478: ConstantValue_attribute attribute) {
479: indent().append("<constant-value-attribute>");
480:
481: attribute.getRawValue().accept(this );
482:
483: append("</constant-value-attribute>").eol();
484: }
485:
486: public void visitCode_attribute(Code_attribute attribute) {
487: indent().append("<code-attribute>").eol();
488: raiseIndent();
489:
490: indent().append("<length>").append(attribute.getCode().length)
491: .append("</length>").eol();
492:
493: indent().append("<instructions>").eol();
494: raiseIndent();
495: Iterator i = attribute.iterator();
496: while (i.hasNext()) {
497: Instruction instr = (Instruction) i.next();
498: indent();
499: append("<instruction pc=\"").append(instr.getStart())
500: .append("\" length=\"").append(instr.getLength())
501: .append("\"");
502: switch (instr.getOpcode()) {
503: case 0x12: // ldc
504: case 0x13: // ldc_w
505: case 0x14: // ldc2_w
506: case 0xb2: // getstatic
507: case 0xb3: // putstatic
508: case 0xb4: // getfield
509: case 0xb5: // putfield
510: case 0xb6: // invokevirtual
511: case 0xb7: // invokespecial
512: case 0xb8: // invokestatic
513: case 0xb9: // invokeinterface
514: case 0xbb: // new
515: case 0xbd: // anewarray
516: case 0xc0: // checkcast
517: case 0xc1: // instanceof
518: case 0xc5: // multianewarray
519: append(" index=\"").append(instr.getIndex()).append(
520: "\">");
521: append(instr);
522: append(" ");
523: instr.getIndexedConstantPoolEntry().accept(this );
524: break;
525: default:
526: append(">");
527: append(instr);
528: break;
529: }
530: append("</instruction>").eol();
531: }
532: lowerIndent();
533: indent().append("</instructions>").eol();
534:
535: if (!attribute.getExceptionHandlers().isEmpty()) {
536: indent().append("<exception-handlers>").eol();
537: raiseIndent();
538: for (ExceptionHandler exceptionHandler : attribute
539: .getExceptionHandlers()) {
540: exceptionHandler.accept(this );
541: }
542: lowerIndent();
543: indent().append("</exception-handlers>").eol();
544: }
545:
546: if (!attribute.getAttributes().isEmpty()) {
547: indent().append("<attributes>").eol();
548: raiseIndent();
549: for (Attribute_info attribute_info : attribute
550: .getAttributes()) {
551: attribute_info.accept(this );
552: }
553: lowerIndent();
554: indent().append("</attributes>").eol();
555: }
556:
557: lowerIndent();
558: indent().append("</code-attribute>").eol();
559: }
560:
561: public void visitExceptions_attribute(Exceptions_attribute attribute) {
562: indent().append("<exceptions-attribute>").eol();
563: raiseIndent();
564:
565: for (Class_info exception : attribute.getExceptions()) {
566: indent();
567: append("<exception>");
568: exception.accept(this );
569: append("</exception>").eol();
570: }
571:
572: lowerIndent();
573: indent().append("</exceptions-attribute>").eol();
574: }
575:
576: public void visitInnerClasses_attribute(
577: InnerClasses_attribute attribute) {
578: indent().append("<inner-classes-attribute>").eol();
579: raiseIndent();
580:
581: for (InnerClass innerClass : attribute.getInnerClasses()) {
582: innerClass.accept(this );
583: }
584:
585: lowerIndent();
586: indent().append("</inner-classes-attribute>").eol();
587: }
588:
589: public void visitSynthetic_attribute(Synthetic_attribute attribute) {
590: indent().append("<synthetic-attribute/>").eol();
591: }
592:
593: public void visitSourceFile_attribute(SourceFile_attribute attribute) {
594: indent().append("<source-file-attribute>").append(
595: attribute.getSourceFile()).append(
596: "</source-file-attribute>").eol();
597: }
598:
599: public void visitLineNumberTable_attribute(
600: LineNumberTable_attribute attribute) {
601: indent().append("<line-number-table-attribute>").eol();
602: raiseIndent();
603:
604: for (LineNumber lineNumber : attribute.getLineNumbers()) {
605: lineNumber.accept(this );
606: }
607:
608: lowerIndent();
609: indent().append("</line-number-table-attribute>").eol();
610: }
611:
612: public void visitLocalVariableTable_attribute(
613: LocalVariableTable_attribute attribute) {
614: indent().append("<local-variable-table-attribute>").eol();
615: raiseIndent();
616:
617: for (LocalVariable localVariable : attribute
618: .getLocalVariables()) {
619: localVariable.accept(this );
620: }
621:
622: lowerIndent();
623: indent().append("</local-variable-table-attribute>").eol();
624: }
625:
626: public void visitDeprecated_attribute(Deprecated_attribute attribute) {
627: indent().append("<deprecated-attribute/>").eol();
628: }
629:
630: public void visitExceptionHandler(ExceptionHandler helper) {
631: indent();
632: append("<exception-handler>");
633: append("<start-pc>").append(helper.getStartPC()).append(
634: "</start-pc>");
635: append("<end-pc>").append(helper.getEndPC())
636: .append("</end-pc>");
637: append("<handler-pc>").append(helper.getHandlerPC()).append(
638: "</handler-pc>");
639:
640: append("<catch-type>");
641: if (helper.getCatchTypeIndex() != 0) {
642: helper.getRawCatchType().accept(this );
643: }
644: append("</catch-type>");
645:
646: append("</exception-handler>").eol();
647: }
648:
649: public void visitInnerClass(InnerClass helper) {
650: indent().append("<inner-class access-flag=\"").append(
651: format.format(helper.getAccessFlag())).append("\">")
652: .eol();
653: raiseIndent();
654:
655: if (helper.isPublic())
656: indent().append("<public/>").eol();
657: if (helper.isProtected())
658: indent().append("<protected/>").eol();
659: if (helper.isPrivate())
660: indent().append("<private/>").eol();
661: if (helper.isStatic())
662: indent().append("<static/>").eol();
663: if (helper.isFinal())
664: indent().append("<final/>").eol();
665: if (helper.isInterface())
666: indent().append("<is-interface/>").eol();
667: if (helper.isAbstract())
668: indent().append("<abstract/>").eol();
669:
670: indent();
671: append("<inner-class-info>");
672: if (helper.getInnerClassInfoIndex() != 0) {
673: helper.getRawInnerClassInfo().accept(this );
674: }
675: append("</inner-class-info>").eol();
676:
677: indent();
678: append("<outer-class-info>");
679: if (helper.getOuterClassInfoIndex() != 0) {
680: helper.getRawOuterClassInfo().accept(this );
681: }
682: append("</outer-class-info>").eol();
683:
684: indent();
685: append("<inner-name>");
686: if (helper.getInnerNameIndex() != 0) {
687: helper.getRawInnerName().accept(this );
688: }
689: append("</inner-name>").eol();
690:
691: lowerIndent();
692: indent().append("</inner-class>").eol();
693: }
694:
695: public void visitLineNumber(LineNumber helper) {
696: indent();
697: append("<line-number>");
698: append("<start-pc>").append(helper.getStartPC()).append(
699: "</start-pc>");
700: append("<line>").append(helper.getLineNumber()).append(
701: "</line>");
702: append("</line-number>").eol();
703: }
704:
705: public void visitLocalVariable(LocalVariable helper) {
706: indent();
707: append("<local-variable pc=\"").append(helper.getStartPC())
708: .append("\" length=\"").append(helper.getLength())
709: .append("\">");
710: append("<name>");
711: helper.getRawName().accept(this );
712: append("</name>");
713:
714: append("<type>").append(
715: SignatureHelper.getType(helper.getDescriptor()))
716: .append("</type>");
717: append("</local-variable>").eol();
718: }
719:
720: private String escapeXMLCharacters(String text) {
721: String result = text;
722:
723: result = perl.substitute("s/&/&/g", result);
724: result = perl.substitute("s/</</g", result);
725: result = perl.substitute("s/>/>/g", result);
726:
727: return result;
728: }
729: }
|