KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > classfile > ClassFile


1 /*
2  * ClassFile.java
3  *
4  * The contents of this file are subject to the terms of the Common Development
5  * and Distribution License (the License). You may not use this file except in
6  * compliance with the License.
7  *
8  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
9  * or http://www.netbeans.org/cddl.txt.
10  *
11  * When distributing Covered Code, include this CDDL Header Notice in each file
12  * and include the License file at http://www.netbeans.org/cddl.txt.
13  * If applicable, add the following below the CDDL Header, with the fields
14  * enclosed by brackets [] replaced by your own identifying information:
15  * "Portions Copyrighted [year] [name of copyright owner]"
16  *
17  * The Original Software is NetBeans. The Initial Developer of the Original
18  * Software is Sun Microsystems, Inc. Portions Copyright 2000-2001 Sun
19  * Microsystems, Inc. All Rights Reserved.
20  *
21  * Contributor(s): Thomas Ball
22  *
23  * Version: $Revision: 1.28 $
24  */

25
26 package org.netbeans.modules.classfile;
27
28 import java.io.*;
29 import java.util.*;
30
31 /**
32  * Class representing a Java class file.
33  *
34  * @author Thomas Ball
35  */

36 public class ClassFile {
37
38     ConstantPool constantPool;
39     int classAccess;
40     CPClassInfo classInfo;
41     CPClassInfo superClassInfo;
42     CPClassInfo[] interfaces;
43     Variable[] variables;
44     Method[] methods;
45     String JavaDoc sourceFileName;
46     InnerClass[] innerClasses;
47     private AttributeMap attributes;
48     private Map<ClassName,Annotation> annotations;
49     short majorVersion;
50     short minorVersion;
51     String JavaDoc typeSignature;
52     EnclosingMethod enclosingMethod;
53     private boolean includeCode = false;
54     
55     /** size of buffer in buffered input streams */
56     private static final int BUFFER_SIZE = 4096;
57     
58     /**
59      * Create a new ClassFile object.
60      * @param classData an InputStream from which the defining bytes of this
61      * class or interface are read.
62      * @throws IOException if InputStream can't be read, or if the class data
63      * is malformed.
64      */

65     public ClassFile(InputStream classData) throws IOException {
66     this(classData, true);
67     }
68     
69     /**
70      * Create a new ClassFile object.
71      * @param classFileName the path of a class file.
72      * @throws IOException if file cannot be opened or read.
73      **/

74     public ClassFile(String JavaDoc classFileName) throws IOException {
75     this(classFileName, true);
76     }
77     
78     /**
79      * Create a new ClassFile object.
80      * @param file a File instance of a class file.
81      * @param includeCode true if this classfile should support operations
82      * at the bytecode level. Specify false to conserve
83      * memory if code access isn't needed.
84      * @throws IOException if file cannot be opened or read.
85      **/

86     public ClassFile(File file, boolean includeCode) throws IOException {
87     InputStream is = null;
88     this.includeCode = includeCode;
89         if( file == null || !file.exists() )
90             throw new FileNotFoundException(file != null ?
91                         file.getPath() : "null");
92         try {
93             is = new BufferedInputStream( new FileInputStream( file ), BUFFER_SIZE);
94             load(is);
95         } catch (InvalidClassFormatException e) {
96             throw new InvalidClassFormatException(file.getPath() + '(' +
97                           e.getMessage() + ')');
98         } finally {
99             if (is != null)
100                 is.close();
101         }
102     }
103
104     /**
105      * Create a new ClassFile object.
106      * @param classData an InputStream from which the defining bytes of this
107      * class or interface are read.
108      * @param includeCode true if this classfile should support operations
109      * at the bytecode level. Specify false to conserve
110      * memory if code access isn't needed.
111      * @throws IOException if InputStream can't be read, or if the class data
112      * is malformed.
113      */

114     public ClassFile(InputStream classData, boolean includeCode) throws IOException {
115         if (classData == null)
116             throw new IOException("input stream not specified");
117     this.includeCode = includeCode;
118         load(classData);
119     }
120     
121     /**
122      * Create a new ClassFile object.
123      * @param classFileName the path of a class file.
124      * @param includeCode true if this classfile should support operations
125      * at the bytecode level. Specify false to conserve
126      * memory if code access isn't needed.
127      * @throws IOException if file cannot be opened or read.
128      **/

129     public ClassFile(String JavaDoc classFileName, boolean includeCode) throws IOException {
130         InputStream in = null;
131     this.includeCode = includeCode;
132         try {
133             if (classFileName == null)
134                 throw new IOException("input stream not specified");
135             in = new BufferedInputStream(new FileInputStream(classFileName), BUFFER_SIZE);
136             load(in);
137         } catch (InvalidClassFormatException e) {
138             throw new InvalidClassFormatException(classFileName + '(' +
139                           e.getMessage() + ')');
140         } finally {
141             if (in != null)
142                 in.close();
143         }
144     }
145     
146     
147     /** Returns the ConstantPool object associated with this ClassFile.
148      * @return the constant pool object
149      */

150     public final ConstantPool getConstantPool() {
151         return constantPool;
152     }
153
154     private void load(InputStream classData) throws IOException {
155         try {
156             DataInputStream in = new DataInputStream(classData);
157             constantPool = loadClassHeader(in);
158             interfaces = getCPClassList(in, constantPool);
159             variables = Variable.loadFields(in, constantPool, this);
160             methods = Method.loadMethods(in, constantPool, this, includeCode);
161             attributes = AttributeMap.load(in, constantPool);
162         } catch (IOException ioe) {
163         throw new InvalidClassFormatException(ioe);
164         }
165     }
166
167     private ConstantPool loadClassHeader(DataInputStream in) throws IOException {
168         int magic = in.readInt();
169         if (magic != 0xCAFEBABE) {
170             throw new InvalidClassFormatException();
171         }
172             
173         minorVersion = in.readShort();
174         majorVersion = in.readShort();
175         int count = in.readUnsignedShort();
176         ConstantPool pool = new ConstantPool(count, in);
177         classAccess = in.readUnsignedShort();
178         classInfo = pool.getClass(in.readUnsignedShort());
179         if (classInfo == null)
180             throw new InvalidClassFormatException();
181         int index = in.readUnsignedShort();
182         if (index != 0) // true for java.lang.Object
183
superClassInfo = pool.getClass(index);
184         return pool;
185     }
186
187     static CPClassInfo[] getCPClassList(DataInputStream in, ConstantPool pool)
188       throws IOException {
189         int count = in.readUnsignedShort();
190         CPClassInfo[] classes = new CPClassInfo[count];
191         for (int i = 0; i < count; i++) {
192             classes[i] = pool.getClass(in.readUnsignedShort());
193         }
194         return classes;
195     }
196     
197     /**
198      * Returns the access permissions of this class or interface.
199      * @return a mask of access flags.
200      * @see org.netbeans.modules.classfile.Access
201      */

202     public final int getAccess() {
203         return classAccess;
204     }
205     
206     /** Returns the name of this class.
207      * @return the name of this class.
208      */

209     public final ClassName getName() {
210         return classInfo.getClassName();
211     }
212
213     /** Returns the name of this class's superclass. A string is returned
214      * instead of a ClassFile object to reduce object creation.
215      * @return the name of the superclass of this class.
216      */

217     public final ClassName getSuperClass() {
218         if (superClassInfo == null)
219             return null;
220     return superClassInfo.getClassName();
221     }
222     
223     /**
224      * @return a collection of Strings describing this class's interfaces.
225      */

226     public final Collection<ClassName> getInterfaces() {
227         List<ClassName> l = new ArrayList<ClassName>();
228         int n = interfaces.length;
229         for (int i = 0; i < n; i++)
230             l.add(interfaces[i].getClassName());
231         return l;
232     }
233     
234     /**
235      * Looks up a variable by its name.
236      *
237      * NOTE: this method only looks up variables defined by this class,
238      * and not inherited from its superclass.
239      *
240      * @param name the name of the variable
241      * @return the variable,or null if no such variable in this class.
242      */

243     public final Variable getVariable(String JavaDoc name) {
244         int n = variables.length;
245         for (int i = 0; i < n; i++) {
246             Variable v = variables[i];
247             if (v.getName().equals(name))
248                 return v;
249         }
250         return null;
251     }
252     
253     /**
254      * @return a Collection of Variable objects representing the fields
255      * defined by this class.
256      */

257     public final Collection<Variable> getVariables() {
258         return Arrays.asList(variables);
259     }
260
261     /**
262      * @return the number of variables defined by this class.
263      */

264     public final int getVariableCount() {
265         return variables.length;
266     }
267     
268     /**
269      * Looks up a method by its name and type signature, as defined
270      * by the Java Virtual Machine Specification, section 4.3.3.
271      *
272      * NOTE: this method only looks up methods defined by this class,
273      * and not methods inherited from its superclass.
274      *
275      * @param name the name of the method
276      * @param signature the method's type signature
277      * @return the method, or null if no such method in this class.
278      */

279     public final Method getMethod(String JavaDoc name, String JavaDoc signature) {
280         int n = methods.length;
281         for (int i = 0; i < n; i++) {
282             Method m = methods[i];
283             if (m.getName().equals(name) && m.getDescriptor().equals(signature))
284                 return m;
285         }
286         return null;
287     }
288     
289     /**
290      * @return a Collection of Method objects representing the methods
291      * defined by this class.
292      */

293     public final Collection<Method> getMethods() {
294         return Arrays.asList(methods);
295     }
296     
297     /**
298      * @return the number of methods defined by this class.
299      */

300     public final int getMethodCount() {
301         return methods.length;
302     }
303     
304     /**
305      * @return the name of the source file the compiler used to create this class.
306      */

307     public final String JavaDoc getSourceFileName() {
308     if (sourceFileName == null) {
309         DataInputStream in = attributes.getStream("SourceFile"); // NOI18N
310
if (in != null) {
311         try {
312             int ipool = in.readUnsignedShort();
313             CPUTF8Info entry = (CPUTF8Info)constantPool.get(ipool);
314             sourceFileName = entry.getName();
315             in.close();
316         } catch (IOException e) {
317             throw new InvalidClassFileAttributeException("invalid SourceFile attribute", e);
318         }
319         }
320     }
321         return sourceFileName;
322     }
323     
324     public final boolean isDeprecated() {
325     return attributes.get("Deprecated") != null;
326     }
327
328     public final boolean isSynthetic() {
329         return (classAccess & Access.SYNTHETIC) == Access.SYNTHETIC ||
330         attributes.get("Synthetic") != null;
331     }
332
333
334     /**
335      * Returns true if this class is an annotation type.
336      */

337     public final boolean isAnnotation() {
338     return (classAccess & Access.ANNOTATION) == Access.ANNOTATION;
339     }
340             
341     /**
342      * Returns true if this class defines an enum type.
343      */

344     public final boolean isEnum() {
345     return (classAccess & Access.ENUM) == Access.ENUM;
346     }
347
348     /**
349      * Returns a map of the raw attributes for this classfile.
350      * Field attributes are
351      * not returned in this map.
352      *
353      * @see org.netbeans.modules.classfile.Field#getAttributes
354      */

355     public final AttributeMap getAttributes(){
356         return attributes;
357     }
358     
359     public final Collection<InnerClass> getInnerClasses(){
360     if (innerClasses == null) {
361         DataInputStream in = attributes.getStream("InnerClasses"); // NOI18N
362
if (in != null) {
363         try {
364             innerClasses =
365             InnerClass.loadInnerClasses(in, constantPool);
366             in.close();
367         } catch (IOException e) {
368             throw new InvalidClassFileAttributeException("invalid InnerClasses attribute", e);
369         }
370         } else
371         innerClasses = new InnerClass[0];
372     }
373         return Arrays.asList(innerClasses);
374     }
375
376     /**
377      * Returns the major version number of this classfile.
378      */

379     public int getMajorVersion() {
380     return majorVersion;
381     }
382
383     /**
384      * Returns the minor version number of this classfile.
385      */

386     public int getMinorVersion() {
387     return minorVersion;
388     }
389
390     /**
391      * Returns the generic type information associated with this class.
392      * If this class does not have generic type information, then null
393      * is returned.
394      */

395     public String JavaDoc getTypeSignature() {
396     if (typeSignature == null) {
397         DataInputStream in = attributes.getStream("Signature"); // NOI18N
398
if (in != null) {
399         try {
400             CPUTF8Info entry =
401             (CPUTF8Info)constantPool.get(in.readUnsignedShort());
402             typeSignature = entry.getName();
403             in.close();
404         } catch (IOException e) {
405             throw new InvalidClassFileAttributeException("invalid Signature attribute", e);
406         }
407         }
408     }
409     return typeSignature;
410     }
411
412     /**
413      * Returns the enclosing method for this class. A class will have an
414      * enclosing class if and only if it is a local class or an anonymous
415      * class, and has been compiled with a compiler target level of 1.5
416      * or above. If no such attribute is present in the classfile, then
417      * null is returned.
418      */

419     public EnclosingMethod getEnclosingMethod() {
420     if (enclosingMethod == null) {
421         DataInputStream in =
422         attributes.getStream("EnclosingMethod"); // NOI18N
423
if (in != null) {
424         try {
425             int classIndex = in.readUnsignedShort();
426             int natIndex = in.readUnsignedShort();
427             CPEntry classInfo = constantPool.get(classIndex);
428             if (classInfo.getTag() == ConstantPool.CONSTANT_Class)
429             enclosingMethod =
430                 new EnclosingMethod(constantPool,
431                         (CPClassInfo)classInfo,
432                         natIndex);
433             else
434             ; // JDK 1.5 beta1 bug
435
in.close();
436         } catch (IOException e) {
437             throw new InvalidClassFileAttributeException("invalid EnclosingMethod attribute", e);
438         }
439         }
440     }
441     return enclosingMethod;
442     }
443
444     private void loadAnnotations() {
445     if (annotations == null)
446         annotations = buildAnnotationMap(constantPool, attributes);
447     }
448
449     static Map<ClassName,Annotation> buildAnnotationMap(ConstantPool pool, AttributeMap attrs) {
450     Map<ClassName,Annotation> annotations = new HashMap<ClassName,Annotation>(2);
451     DataInputStream in =
452         attrs.getStream("RuntimeVisibleAnnotations"); //NOI18N
453
if (in != null) {
454         try {
455         Annotation.load(in, pool, true, annotations);
456         in.close();
457         } catch (IOException e) {
458         throw new InvalidClassFileAttributeException("invalid RuntimeVisibleAnnotations attribute", e);
459         }
460     }
461     in = attrs.getStream("RuntimeInvisibleAnnotations"); //NOI18N
462
if (in != null) {
463         try {
464         Annotation.load(in, pool, false, annotations);
465         in.close();
466         } catch (IOException e) {
467         throw new InvalidClassFileAttributeException("invalid RuntimeInvisibleAnnotations attribute", e);
468         }
469     }
470     return annotations;
471     }
472
473     /**
474      * Returns all runtime annotations defined for this class. Inherited
475      * annotations are not included in this collection.
476      */

477     public final Collection<Annotation> getAnnotations() {
478     loadAnnotations();
479     return annotations.values();
480     }
481
482     /**
483      * Returns the annotation for a specified annotation type, or null if
484      * no annotation of that type exists for this class.
485      */

486     public final Annotation getAnnotation(final ClassName annotationClass) {
487     loadAnnotations();
488     return annotations.get(annotationClass);
489     }
490     
491     /**
492      * Returns true if an annotation of the specified type is defined for
493      * this class.
494      */

495     public final boolean isAnnotationPresent(final ClassName annotationClass) {
496     loadAnnotations();
497     return annotations.get(annotationClass) != null;
498     }
499     
500     /** Return the collection of all unique class references in this class.
501      *
502      * @return a Set of ClassNames specifying the referenced classnames.
503      */

504     public final Set<ClassName> getAllClassNames() {
505         Set<ClassName> set = new HashSet<ClassName>();
506
507         // include all class name constants from constant pool
508
Collection c = constantPool.getAllConstants(CPClassInfo.class);
509         for (Iterator i = c.iterator(); i.hasNext();) {
510             CPClassInfo ci = (CPClassInfo)i.next();
511             set.add(ci.getClassName());
512         }
513
514     // scan variables and methods for other class references
515
// (inner classes will caught above)
516
for (int i = 0; i < variables.length; i++)
517         addClassNames(set, variables[i].getDescriptor());
518     for (int i = 0; i < methods.length; i++)
519         addClassNames(set, methods[i].getDescriptor());
520
521         return Collections.unmodifiableSet(set);
522     }
523
524     private void addClassNames(Set<ClassName> set, String JavaDoc type) {
525         int i = 0;
526         while ((i = type.indexOf('L', i)) != -1) {
527             int j = type.indexOf(';', i);
528             if (j > i) {
529         // get name, minus leading 'L' and trailing ';'
530
String JavaDoc classType = type.substring(i + 1, j);
531         set.add(ClassName.getClassName(classType));
532                 i = j + 1;
533             } else
534         break;
535         }
536     }
537
538     public String JavaDoc toString() {
539         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
540         sb.append("ClassFile: "); //NOI18N
541
sb.append(Access.toString(classAccess));
542         sb.append(' ');
543         sb.append(classInfo);
544         if (isSynthetic())
545             sb.append(" (synthetic)"); //NOI18N
546
if (isDeprecated())
547             sb.append(" (deprecated)"); //NOI18N
548
sb.append("\n source: "); //NOI18N
549
sb.append(getSourceFileName());
550         sb.append("\n super: "); //NOI18N
551
sb.append(superClassInfo);
552     if (getTypeSignature() != null) {
553         sb.append("\n signature: "); //NOI18N
554
sb.append(typeSignature);
555     }
556     if (getEnclosingMethod() != null) {
557         sb.append("\n enclosing method: "); //NOI18N
558
sb.append(enclosingMethod);
559     }
560         sb.append("\n ");
561     loadAnnotations();
562     if (annotations.size() > 0) {
563         Iterator iter = annotations.values().iterator();
564         sb.append("annotations: ");
565         while (iter.hasNext()) {
566                 sb.append("\n ");
567         sb.append(iter.next().toString());
568         }
569         sb.append("\n ");
570     }
571         if (interfaces.length > 0) {
572             sb.append(arrayToString("interfaces", interfaces)); //NOI18N
573
sb.append("\n ");
574         }
575         if (getInnerClasses().size() > 0) {
576             sb.append(arrayToString("innerclasses", innerClasses)); //NOI18N
577
sb.append("\n ");
578         }
579         if (variables.length > 0) {
580             sb.append(arrayToString("variables", variables)); //NOI18N
581
sb.append("\n ");
582         }
583         if (methods.length > 0)
584             sb.append(arrayToString("methods", methods)); //NOI18N
585
return sb.toString();
586     }
587
588     private String JavaDoc arrayToString(String JavaDoc name, Object JavaDoc[] array) {
589         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
590         sb.append(name);
591         sb.append(": ");
592         int n = array.length;
593         if (n > 0) {
594             int i = 0;
595             do {
596                 sb.append("\n ");
597                 sb.append(array[i++].toString());
598             } while (i < n);
599         } else
600             sb.append("none"); //NOI18N
601
return sb.toString();
602     }
603 }
604
Popular Tags