KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jdepend > framework > ClassFileParser


1 package jdepend.framework;
2
3 import java.io.*;
4 import java.util.*;
5
6 /**
7  * The <code>ClassFileParser</code> class is responsible for
8  * parsing a Java class file to create a <code>JavaClass</code>
9  * instance.
10  *
11  * @author <b>Mike Clark</b>
12  * @author Clarkware Consulting, Inc.
13  */

14
15 public class ClassFileParser extends AbstractParser {
16
17     public static final int JAVA_MAGIC = 0xCAFEBABE;
18     public static final int CONSTANT_UTF8 = 1;
19     public static final int CONSTANT_UNICODE = 2;
20     public static final int CONSTANT_INTEGER = 3;
21     public static final int CONSTANT_FLOAT = 4;
22     public static final int CONSTANT_LONG = 5;
23     public static final int CONSTANT_DOUBLE = 6;
24     public static final int CONSTANT_CLASS = 7;
25     public static final int CONSTANT_STRING = 8;
26     public static final int CONSTANT_FIELD = 9;
27     public static final int CONSTANT_METHOD = 10;
28     public static final int CONSTANT_INTERFACEMETHOD = 11;
29     public static final int CONSTANT_NAMEANDTYPE = 12;
30     public static final char CLASS_DESCRIPTOR = 'L';
31     public static final int ACC_INTERFACE = 0x200;
32     public static final int ACC_ABSTRACT = 0x400;
33     
34     private String JavaDoc fileName;
35     private String JavaDoc className;
36     private String JavaDoc superClassName;
37     private String JavaDoc interfaceNames[];
38     private boolean isAbstract;
39     private JavaClass jClass;
40     private Constant[] constantPool;
41     private FieldOrMethodInfo[] fields;
42     private FieldOrMethodInfo[] methods;
43     private AttributeInfo[] attributes;
44     private DataInputStream in;
45
46     
47     public ClassFileParser() {
48         this(new PackageFilter());
49     }
50
51     public ClassFileParser(PackageFilter filter) {
52         super(filter);
53         reset();
54     }
55
56     private void reset() {
57         className = null;
58         superClassName = null;
59         interfaceNames = new String JavaDoc[0];
60         isAbstract = false;
61
62         jClass = null;
63         constantPool = new Constant[1];
64         fields = new FieldOrMethodInfo[0];
65         methods = new FieldOrMethodInfo[0];
66         attributes = new AttributeInfo[0];
67     }
68
69     /**
70      * Registered parser listeners are informed that the resulting
71      * <code>JavaClass</code> was parsed.
72      */

73     public JavaClass parse(File classFile) throws IOException {
74
75         this.fileName = classFile.getCanonicalPath();
76
77         debug("\nParsing " + fileName + "...");
78
79         FileInputStream in = null;
80
81         try {
82
83             in = new FileInputStream(classFile);
84
85             return parse(in);
86
87         } finally {
88             if (in != null) {
89                 try {
90                     in.close();
91                 } catch (IOException ioe) {
92                     ioe.printStackTrace();
93                 }
94             }
95         }
96     }
97
98     public JavaClass parse(InputStream is) throws IOException {
99
100         reset();
101
102         jClass = new JavaClass("Unknown");
103
104         in = new DataInputStream(is);
105
106         int magic = parseMagic();
107
108         int minorVersion = parseMinorVersion();
109         int majorVersion = parseMajorVersion();
110
111         constantPool = parseConstantPool();
112
113         parseAccessFlags();
114
115         className = parseClassName();
116
117         superClassName = parseSuperClassName();
118
119         interfaceNames = parseInterfaces();
120
121         fields = parseFields();
122
123         methods = parseMethods();
124
125         parseAttributes();
126
127         addClassConstantReferences();
128
129         onParsedJavaClass(jClass);
130
131         return jClass;
132     }
133
134     private int parseMagic() throws IOException {
135         int magic = in.readInt();
136         if (magic != JAVA_MAGIC) {
137             throw new IOException("Invalid class file: " + fileName);
138         }
139
140         return magic;
141     }
142
143     private int parseMinorVersion() throws IOException {
144         return in.readUnsignedShort();
145     }
146
147     private int parseMajorVersion() throws IOException {
148         return in.readUnsignedShort();
149     }
150
151     private Constant[] parseConstantPool() throws IOException {
152         int constantPoolSize = in.readUnsignedShort();
153
154         Constant[] pool = new Constant[constantPoolSize];
155
156         for (int i = 1; i < constantPoolSize; i++) {
157
158             Constant constant = parseNextConstant();
159
160             pool[i] = constant;
161
162             //
163
// 8-byte constants use two constant pool entries
164
//
165
if (constant.getTag() == CONSTANT_DOUBLE
166                     || constant.getTag() == CONSTANT_LONG) {
167                 i++;
168             }
169         }
170
171         return pool;
172     }
173
174     private void parseAccessFlags() throws IOException {
175         int accessFlags = in.readUnsignedShort();
176
177         boolean isAbstract = ((accessFlags & ACC_ABSTRACT) != 0);
178         boolean isInterface = ((accessFlags & ACC_INTERFACE) != 0);
179
180         this.isAbstract = isAbstract || isInterface;
181         jClass.isAbstract(this.isAbstract);
182
183         debug("Parser: abstract = " + this.isAbstract);
184     }
185
186     private String JavaDoc parseClassName() throws IOException {
187         int entryIndex = in.readUnsignedShort();
188         String JavaDoc className = getClassConstantName(entryIndex);
189         jClass.setName(className);
190         jClass.setPackageName(getPackageName(className));
191
192         debug("Parser: class name = " + className);
193         debug("Parser: package name = " + getPackageName(className));
194         
195         return className;
196     }
197
198     private String JavaDoc parseSuperClassName() throws IOException {
199         int entryIndex = in.readUnsignedShort();
200         String JavaDoc superClassName = getClassConstantName(entryIndex);
201         addImport(getPackageName(superClassName));
202
203         debug("Parser: super class name = " + superClassName);
204         
205         return superClassName;
206     }
207
208     private String JavaDoc[] parseInterfaces() throws IOException {
209         int interfacesCount = in.readUnsignedShort();
210         String JavaDoc[] interfaceNames = new String JavaDoc[interfacesCount];
211         for (int i = 0; i < interfacesCount; i++) {
212             int entryIndex = in.readUnsignedShort();
213             interfaceNames[i] = getClassConstantName(entryIndex);
214             addImport(getPackageName(interfaceNames[i]));
215
216             debug("Parser: interface = " + interfaceNames[i]);
217         }
218
219         return interfaceNames;
220     }
221
222     private FieldOrMethodInfo[] parseFields() throws IOException {
223         int fieldsCount = in.readUnsignedShort();
224         FieldOrMethodInfo[] fields = new FieldOrMethodInfo[fieldsCount];
225         for (int i = 0; i < fieldsCount; i++) {
226             fields[i] = parseFieldOrMethodInfo();
227             String JavaDoc descriptor = toUTF8(fields[i].getDescriptorIndex());
228             debug("Parser: field descriptor = " + descriptor);
229             String JavaDoc[] types = descriptorToTypes(descriptor);
230             for (int t = 0; t < types.length; t++) {
231                 addImport(getPackageName(types[t]));
232                 debug("Parser: field type = " + types[t]);
233             }
234         }
235
236         return fields;
237     }
238
239     private FieldOrMethodInfo[] parseMethods() throws IOException {
240         int methodsCount = in.readUnsignedShort();
241         FieldOrMethodInfo[] methods = new FieldOrMethodInfo[methodsCount];
242         for (int i = 0; i < methodsCount; i++) {
243             methods[i] = parseFieldOrMethodInfo();
244             String JavaDoc descriptor = toUTF8(methods[i].getDescriptorIndex());
245             debug("Parser: method descriptor = " + descriptor);
246             String JavaDoc[] types = descriptorToTypes(descriptor);
247             for (int t = 0; t < types.length; t++) {
248                 if (types[t].length() > 0) {
249                     addImport(getPackageName(types[t]));
250                     debug("Parser: method type = " + types[t]);
251                 }
252             }
253         }
254
255         return methods;
256     }
257
258     private Constant parseNextConstant() throws IOException {
259
260         Constant result;
261
262         byte tag = in.readByte();
263
264         switch (tag) {
265
266         case (ClassFileParser.CONSTANT_CLASS):
267         case (ClassFileParser.CONSTANT_STRING):
268             result = new Constant(tag, in.readUnsignedShort());
269             break;
270         case (ClassFileParser.CONSTANT_FIELD):
271         case (ClassFileParser.CONSTANT_METHOD):
272         case (ClassFileParser.CONSTANT_INTERFACEMETHOD):
273         case (ClassFileParser.CONSTANT_NAMEANDTYPE):
274             result = new Constant(tag, in.readUnsignedShort(), in
275                     .readUnsignedShort());
276             break;
277         case (ClassFileParser.CONSTANT_INTEGER):
278             result = new Constant(tag, new Integer JavaDoc(in.readInt()));
279             break;
280         case (ClassFileParser.CONSTANT_FLOAT):
281             result = new Constant(tag, new Float JavaDoc(in.readFloat()));
282             break;
283         case (ClassFileParser.CONSTANT_LONG):
284             result = new Constant(tag, new Long JavaDoc(in.readLong()));
285             break;
286         case (ClassFileParser.CONSTANT_DOUBLE):
287             result = new Constant(tag, new Double JavaDoc(in.readDouble()));
288             break;
289         case (ClassFileParser.CONSTANT_UTF8):
290             result = new Constant(tag, in.readUTF());
291             break;
292         default:
293             throw new IOException("Unknown constant: " + tag);
294         }
295
296         return result;
297     }
298
299     private FieldOrMethodInfo parseFieldOrMethodInfo() throws IOException {
300
301         FieldOrMethodInfo result = new FieldOrMethodInfo(
302                 in.readUnsignedShort(), in.readUnsignedShort(), in
303                         .readUnsignedShort());
304
305         int attributesCount = in.readUnsignedShort();
306         for (int a = 0; a < attributesCount; a++) {
307             parseAttribute();
308         }
309
310         return result;
311     }
312
313     private void parseAttributes() throws IOException {
314         int attributesCount = in.readUnsignedShort();
315         attributes = new AttributeInfo[attributesCount];
316
317         for (int i = 0; i < attributesCount; i++) {
318             attributes[i] = parseAttribute();
319
320             // Section 4.7.7 of VM Spec - Class File Format
321
if (attributes[i].getName() != null) {
322                 if (attributes[i].getName().equals("SourceFile")) {
323                     byte[] b = attributes[i].getValue();
324                     int b0 = b[0] < 0 ? b[0] + 256 : b[0];
325                     int b1 = b[1] < 0 ? b[1] + 256 : b[1];
326                     int pe = b0 * 256 + b1;
327
328                     String JavaDoc descriptor = toUTF8(pe);
329                     jClass.setSourceFile(descriptor);
330                 }
331             }
332         }
333     }
334
335     private AttributeInfo parseAttribute() throws IOException {
336         AttributeInfo result = new AttributeInfo();
337
338         int nameIndex = in.readUnsignedShort();
339         if (nameIndex != -1) {
340             result.setName(toUTF8(nameIndex));
341         }
342
343         int attributeLength = in.readInt();
344         byte[] value = new byte[attributeLength];
345         for (int b = 0; b < attributeLength; b++) {
346             value[b] = in.readByte();
347         }
348
349         result.setValue(value);
350         return result;
351     }
352
353     private Constant getConstantPoolEntry(int entryIndex) throws IOException {
354
355         if (entryIndex < 0 || entryIndex >= constantPool.length) {
356             throw new IOException("Illegal constant pool index : " + entryIndex);
357         }
358
359         return constantPool[entryIndex];
360     }
361
362     private void addClassConstantReferences() throws IOException {
363         for (int j = 1; j < constantPool.length; j++) {
364             if (constantPool[j].getTag() == CONSTANT_CLASS) {
365                 String JavaDoc name = toUTF8(constantPool[j].getNameIndex());
366                 addImport(getPackageName(name));
367
368                 debug("Parser: class type = " + slashesToDots(name));
369             }
370
371             if (constantPool[j].getTag() == CONSTANT_DOUBLE
372                     || constantPool[j].getTag() == CONSTANT_LONG) {
373                 j++;
374             }
375         }
376     }
377
378     private String JavaDoc getClassConstantName(int entryIndex) throws IOException {
379
380         Constant entry = getConstantPoolEntry(entryIndex);
381         if (entry == null) {
382             return "";
383         }
384         return slashesToDots(toUTF8(entry.getNameIndex()));
385     }
386
387     private String JavaDoc toUTF8(int entryIndex) throws IOException {
388         Constant entry = getConstantPoolEntry(entryIndex);
389         if (entry.getTag() == CONSTANT_UTF8) {
390             return (String JavaDoc) entry.getValue();
391         }
392
393         throw new IOException("Constant pool entry is not a UTF8 type: "
394                 + entryIndex);
395     }
396
397     private void addImport(String JavaDoc importPackage) {
398         if ((importPackage != null) && (getFilter().accept(importPackage))) {
399             jClass.addImportedPackage(new JavaPackage(importPackage));
400         }
401     }
402
403     private String JavaDoc slashesToDots(String JavaDoc s) {
404         return s.replace('/', '.');
405     }
406
407     private String JavaDoc getPackageName(String JavaDoc s) {
408         if ((s.length() > 0) && (s.charAt(0) == '[')) {
409             String JavaDoc types[] = descriptorToTypes(s);
410             if (types.length == 0) {
411                 return null; // primitives
412
}
413
414             s = types[0];
415         }
416
417         s = slashesToDots(s);
418         int index = s.lastIndexOf(".");
419         if (index > 0) {
420             return s.substring(0, index);
421         }
422
423         return "Default";
424     }
425
426     private String JavaDoc[] descriptorToTypes(String JavaDoc descriptor) {
427
428         int typesCount = 0;
429         for (int index = 0; index < descriptor.length(); index++) {
430             if (descriptor.charAt(index) == ';') {
431                 typesCount++;
432             }
433         }
434
435         String JavaDoc types[] = new String JavaDoc[typesCount];
436
437         int typeIndex = 0;
438         for (int index = 0; index < descriptor.length(); index++) {
439
440             int startIndex = descriptor.indexOf(CLASS_DESCRIPTOR, index);
441             if (startIndex < 0) {
442                 break;
443             }
444
445             index = descriptor.indexOf(';', startIndex + 1);
446             types[typeIndex++] = descriptor.substring(startIndex + 1, index);
447         }
448
449         return types;
450     }
451
452     class Constant {
453
454         private byte _tag;
455
456         private int _nameIndex;
457
458         private int _typeIndex;
459
460         private Object JavaDoc _value;
461
462         Constant(byte tag, int nameIndex) {
463             this(tag, nameIndex, -1);
464         }
465
466         Constant(byte tag, Object JavaDoc value) {
467             this(tag, -1, -1);
468             _value = value;
469         }
470
471         Constant(byte tag, int nameIndex, int typeIndex) {
472             _tag = tag;
473             _nameIndex = nameIndex;
474             _typeIndex = typeIndex;
475             _value = null;
476         }
477
478         byte getTag() {
479             return _tag;
480         }
481
482         int getNameIndex() {
483             return _nameIndex;
484         }
485
486         int getTypeIndex() {
487             return _typeIndex;
488         }
489
490         Object JavaDoc getValue() {
491             return _value;
492         }
493
494         public String JavaDoc toString() {
495
496             StringBuffer JavaDoc s = new StringBuffer JavaDoc("");
497
498             s.append("tag: " + getTag());
499
500             if (getNameIndex() > -1) {
501                 s.append(" nameIndex: " + getNameIndex());
502             }
503
504             if (getTypeIndex() > -1) {
505                 s.append(" typeIndex: " + getTypeIndex());
506             }
507
508             if (getValue() != null) {
509                 s.append(" value: " + getValue());
510             }
511
512             return s.toString();
513         }
514     }
515
516     class FieldOrMethodInfo {
517
518         private int _accessFlags;
519
520         private int _nameIndex;
521
522         private int _descriptorIndex;
523
524         FieldOrMethodInfo(int accessFlags, int nameIndex, int descriptorIndex) {
525
526             _accessFlags = accessFlags;
527             _nameIndex = nameIndex;
528             _descriptorIndex = descriptorIndex;
529         }
530
531         int accessFlags() {
532             return _accessFlags;
533         }
534
535         int getNameIndex() {
536             return _nameIndex;
537         }
538
539         int getDescriptorIndex() {
540             return _descriptorIndex;
541         }
542
543         public String JavaDoc toString() {
544             StringBuffer JavaDoc s = new StringBuffer JavaDoc("");
545
546             try {
547
548                 s.append("\n name (#" + getNameIndex() + ") = "
549                         + toUTF8(getNameIndex()));
550
551                 s.append("\n signature (#" + getDescriptorIndex() + ") = "
552                         + toUTF8(getDescriptorIndex()));
553
554                 String JavaDoc[] types = descriptorToTypes(toUTF8(getDescriptorIndex()));
555                 for (int t = 0; t < types.length; t++) {
556                     s.append("\n type = " + types[t]);
557                 }
558
559             } catch (Exception JavaDoc e) {
560                 e.printStackTrace();
561             }
562
563             return s.toString();
564         }
565     }
566
567     class AttributeInfo {
568
569         private String JavaDoc name;
570
571         private byte[] value;
572
573         public void setName(String JavaDoc name) {
574             this.name = name;
575         }
576
577         public String JavaDoc getName() {
578             return this.name;
579         }
580
581         public void setValue(byte[] value) {
582             this.value = value;
583         }
584
585         public byte[] getValue() {
586             return this.value;
587         }
588     }
589
590     /**
591      * Returns a string representation of this object.
592      *
593      * @return String representation.
594      */

595     public String JavaDoc toString() {
596
597         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
598
599         try {
600
601             s.append("\n" + className + ":\n");
602
603             s.append("\nConstants:\n");
604             for (int i = 1; i < constantPool.length; i++) {
605                 Constant entry = getConstantPoolEntry(i);
606                 s.append(" " + i + ". " + entry.toString() + "\n");
607                 if (entry.getTag() == CONSTANT_DOUBLE
608                         || entry.getTag() == CONSTANT_LONG) {
609                     i++;
610                 }
611             }
612
613             s.append("\nClass Name: " + className + "\n");
614             s.append("Super Name: " + superClassName + "\n\n");
615
616             s.append(interfaceNames.length + " interfaces\n");
617             for (int i = 0; i < interfaceNames.length; i++) {
618                 s.append(" " + interfaceNames[i] + "\n");
619             }
620
621             s.append("\n" + fields.length + " fields\n");
622             for (int i = 0; i < fields.length; i++) {
623                 s.append(fields[i].toString() + "\n");
624             }
625
626             s.append("\n" + methods.length + " methods\n");
627             for (int i = 0; i < methods.length; i++) {
628                 s.append(methods[i].toString() + "\n");
629             }
630
631             s.append("\nDependencies:\n");
632             Iterator imports = jClass.getImportedPackages().iterator();
633             while (imports.hasNext()) {
634                 JavaPackage jPackage = (JavaPackage) imports.next();
635                 s.append(" " + jPackage.getName() + "\n");
636             }
637
638         } catch (Exception JavaDoc e) {
639             e.printStackTrace();
640         }
641
642         return s.toString();
643     }
644
645     /**
646      * Test main.
647      */

648     public static void main(String JavaDoc args[]) {
649         try {
650
651             ClassFileParser.DEBUG = true;
652
653             if (args.length <= 0) {
654                 System.err.println("usage: ClassFileParser <class-file>");
655                 System.exit(0);
656             }
657
658             ClassFileParser parser = new ClassFileParser();
659
660             parser.parse(new File(args[0]));
661
662             System.err.println(parser.toString());
663
664         } catch (Exception JavaDoc e) {
665             System.err.println(e.getMessage());
666         }
667     }
668 }
669
Popular Tags