KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > classfmt > ClassFileReader


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.classfmt;
12
13 import java.io.File JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.util.Arrays JavaDoc;
17
18 import org.eclipse.jdt.core.compiler.CharOperation;
19 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
20 import org.eclipse.jdt.internal.compiler.env.*;
21 import org.eclipse.jdt.internal.compiler.impl.Constant;
22 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
23 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
24 import org.eclipse.jdt.internal.compiler.util.Util;
25
26 public class ClassFileReader extends ClassFileStruct implements IBinaryType {
27 private static String JavaDoc printTypeModifiers(int modifiers) {
28     java.io.ByteArrayOutputStream JavaDoc out = new java.io.ByteArrayOutputStream JavaDoc();
29     java.io.PrintWriter JavaDoc print = new java.io.PrintWriter JavaDoc(out);
30
31     if ((modifiers & ClassFileConstants.AccPublic) != 0) print.print("public "); //$NON-NLS-1$
32
if ((modifiers & ClassFileConstants.AccPrivate) != 0) print.print("private "); //$NON-NLS-1$
33
if ((modifiers & ClassFileConstants.AccFinal) != 0) print.print("final "); //$NON-NLS-1$
34
if ((modifiers & ClassFileConstants.AccSuper) != 0) print.print("super "); //$NON-NLS-1$
35
if ((modifiers & ClassFileConstants.AccInterface) != 0) print.print("interface "); //$NON-NLS-1$
36
if ((modifiers & ClassFileConstants.AccAbstract) != 0) print.print("abstract "); //$NON-NLS-1$
37
print.flush();
38     return out.toString();
39 }
40 public static ClassFileReader read(InputStream JavaDoc stream, String JavaDoc fileName) throws ClassFormatException, IOException JavaDoc {
41     return read(stream, fileName, false);
42 }
43 public static ClassFileReader read(File JavaDoc file) throws ClassFormatException, IOException JavaDoc {
44     return read(file, false);
45 }
46 public static ClassFileReader read(InputStream JavaDoc stream, String JavaDoc fileName, boolean fullyInitialize) throws ClassFormatException, IOException JavaDoc {
47     byte classFileBytes[] = Util.getInputStreamAsByteArray(stream, -1);
48     ClassFileReader classFileReader = new ClassFileReader(classFileBytes, fileName.toCharArray());
49     if (fullyInitialize) {
50         classFileReader.initialize();
51     }
52     return classFileReader;
53 }
54 public static ClassFileReader read(File JavaDoc file, boolean fullyInitialize) throws ClassFormatException, IOException JavaDoc {
55     byte classFileBytes[] = Util.getFileByteContent(file);
56     ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
57     if (fullyInitialize) {
58         classFileReader.initialize();
59     }
60     return classFileReader;
61 }
62 public static ClassFileReader read(
63     java.util.zip.ZipFile JavaDoc zip,
64     String JavaDoc filename)
65     throws ClassFormatException, java.io.IOException JavaDoc {
66         return read(zip, filename, false);
67 }
68 public static ClassFileReader read(
69     java.util.zip.ZipFile JavaDoc zip,
70     String JavaDoc filename,
71     boolean fullyInitialize)
72     throws ClassFormatException, java.io.IOException JavaDoc {
73     java.util.zip.ZipEntry JavaDoc ze = zip.getEntry(filename);
74     if (ze == null)
75         return null;
76     byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
77     ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
78     if (fullyInitialize) {
79         classFileReader.initialize();
80     }
81     return classFileReader;
82 }
83 public static ClassFileReader read(String JavaDoc fileName) throws ClassFormatException, java.io.IOException JavaDoc {
84     return read(fileName, false);
85 }
86 public static ClassFileReader read(String JavaDoc fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException JavaDoc {
87     return read(new File JavaDoc(fileName), fullyInitialize);
88 }
89     private int accessFlags;
90     private char[] classFileName;
91     private char[] className;
92     private int classNameIndex;
93     private int constantPoolCount;
94     private AnnotationInfo[] annotations;
95     private FieldInfo[] fields;
96     private int fieldsCount;
97     // initialized in case the .class file is a nested type
98
private InnerClassInfo innerInfo;
99     private int innerInfoIndex;
100     private InnerClassInfo[] innerInfos;
101     private char[][] interfaceNames;
102     private int interfacesCount;
103     private MethodInfo[] methods;
104     private int methodsCount;
105     private char[] signature;
106     private char[] sourceName;
107     private char[] sourceFileName;
108     private char[] superclassName;
109     private long tagBits;
110     private long version;
111     
112     private char[] enclosingTypeName;
113
114 /**
115  * @param classFileBytes Actual bytes of a .class file
116  * @param fileName Actual name of the file that contains the bytes, can be null
117  *
118  * @exception ClassFormatException
119  */

120 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
121     this(classFileBytes, fileName, false);
122 }
123
124 /**
125  * @param classFileBytes byte[]
126  * Actual bytes of a .class file
127  *
128  * @param fileName char[]
129  * Actual name of the file that contains the bytes, can be null
130  *
131  * @param fullyInitialize boolean
132  * Flag to fully initialize the new object
133  * @exception ClassFormatException
134  */

135 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
136     // This method looks ugly but is actually quite simple, the constantPool is constructed
137
// in 3 passes. All non-primitive constant pool members that usually refer to other members
138
// by index are tweaked to have their value in inst vars, this minor cost at read-time makes
139
// all subsequent uses of the constant pool element faster.
140
super(classFileBytes, null, 0);
141     this.classFileName = fileName;
142     int readOffset = 10;
143     try {
144         this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
145
constantPoolCount = this.u2At(8);
146         // Pass #1 - Fill in all primitive constants
147
this.constantPoolOffsets = new int[constantPoolCount];
148         for (int i = 1; i < constantPoolCount; i++) {
149             int tag = this.u1At(readOffset);
150             switch (tag) {
151                 case ClassFileConstants.Utf8Tag :
152                     this.constantPoolOffsets[i] = readOffset;
153                     readOffset += u2At(readOffset + 1);
154                     readOffset += ClassFileConstants.ConstantUtf8FixedSize;
155                     break;
156                 case ClassFileConstants.IntegerTag :
157                     this.constantPoolOffsets[i] = readOffset;
158                     readOffset += ClassFileConstants.ConstantIntegerFixedSize;
159                     break;
160                 case ClassFileConstants.FloatTag :
161                     this.constantPoolOffsets[i] = readOffset;
162                     readOffset += ClassFileConstants.ConstantFloatFixedSize;
163                     break;
164                 case ClassFileConstants.LongTag :
165                     this.constantPoolOffsets[i] = readOffset;
166                     readOffset += ClassFileConstants.ConstantLongFixedSize;
167                     i++;
168                     break;
169                 case ClassFileConstants.DoubleTag :
170                     this.constantPoolOffsets[i] = readOffset;
171                     readOffset += ClassFileConstants.ConstantDoubleFixedSize;
172                     i++;
173                     break;
174                 case ClassFileConstants.ClassTag :
175                     this.constantPoolOffsets[i] = readOffset;
176                     readOffset += ClassFileConstants.ConstantClassFixedSize;
177                     break;
178                 case ClassFileConstants.StringTag :
179                     this.constantPoolOffsets[i] = readOffset;
180                     readOffset += ClassFileConstants.ConstantStringFixedSize;
181                     break;
182                 case ClassFileConstants.FieldRefTag :
183                     this.constantPoolOffsets[i] = readOffset;
184                     readOffset += ClassFileConstants.ConstantFieldRefFixedSize;
185                     break;
186                 case ClassFileConstants.MethodRefTag :
187                     this.constantPoolOffsets[i] = readOffset;
188                     readOffset += ClassFileConstants.ConstantMethodRefFixedSize;
189                     break;
190                 case ClassFileConstants.InterfaceMethodRefTag :
191                     this.constantPoolOffsets[i] = readOffset;
192                     readOffset += ClassFileConstants.ConstantInterfaceMethodRefFixedSize;
193                     break;
194                 case ClassFileConstants.NameAndTypeTag :
195                     this.constantPoolOffsets[i] = readOffset;
196                     readOffset += ClassFileConstants.ConstantNameAndTypeFixedSize;
197             }
198         }
199         // Read and validate access flags
200
this.accessFlags = u2At(readOffset);
201         readOffset += 2;
202
203         // Read the classname, use exception handlers to catch bad format
204
this.classNameIndex = u2At(readOffset);
205         this.className = getConstantClassNameAt(this.classNameIndex);
206         readOffset += 2;
207
208         // Read the superclass name, can be null for java.lang.Object
209
int superclassNameIndex = u2At(readOffset);
210         readOffset += 2;
211         // if superclassNameIndex is equals to 0 there is no need to set a value for the
212
// field this.superclassName. null is fine.
213
if (superclassNameIndex != 0) {
214             this.superclassName = getConstantClassNameAt(superclassNameIndex);
215         }
216
217         // Read the interfaces, use exception handlers to catch bad format
218
this.interfacesCount = u2At(readOffset);
219         readOffset += 2;
220         if (this.interfacesCount != 0) {
221             this.interfaceNames = new char[this.interfacesCount][];
222             for (int i = 0; i < this.interfacesCount; i++) {
223                 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
224                 readOffset += 2;
225             }
226         }
227         // Read the fields, use exception handlers to catch bad format
228
this.fieldsCount = u2At(readOffset);
229         readOffset += 2;
230         if (this.fieldsCount != 0) {
231             FieldInfo field;
232             this.fields = new FieldInfo[this.fieldsCount];
233             for (int i = 0; i < this.fieldsCount; i++) {
234                 field = FieldInfo.createField(reference, this.constantPoolOffsets, readOffset);
235                 this.fields[i] = field;
236                 readOffset += field.sizeInBytes();
237             }
238         }
239         // Read the methods
240
this.methodsCount = u2At(readOffset);
241         readOffset += 2;
242         if (this.methodsCount != 0) {
243             this.methods = new MethodInfo[this.methodsCount];
244             boolean isAnnotationType = (this.accessFlags & ClassFileConstants.AccAnnotation) != 0;
245             for (int i = 0; i < this.methodsCount; i++) {
246                 this.methods[i] = isAnnotationType
247                     ? AnnotationMethodInfo.createAnnotationMethod(reference, this.constantPoolOffsets, readOffset)
248                     : MethodInfo.createMethod(reference, this.constantPoolOffsets, readOffset);
249                 readOffset += this.methods[i].sizeInBytes();
250             }
251         }
252
253         // Read the attributes
254
int attributesCount = u2At(readOffset);
255         readOffset += 2;
256
257         for (int i = 0; i < attributesCount; i++) {
258             int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
259             char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
260             if (attributeName.length == 0) {
261                 readOffset += (6 + u4At(readOffset + 2));
262                 continue;
263             }
264             switch(attributeName[0] ) {
265                 case 'E' :
266                     if (CharOperation.equals(attributeName, AttributeNamesConstants.EnclosingMethodName)) {
267                         utf8Offset =
268                             constantPoolOffsets[u2At(constantPoolOffsets[u2At(readOffset + 6)] - structOffset + 1)] - structOffset;
269                         this.enclosingTypeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
270                     }
271                     break;
272                 case 'D' :
273                     if (CharOperation.equals(attributeName, AttributeNamesConstants.DeprecatedName)) {
274                         this.accessFlags |= ClassFileConstants.AccDeprecated;
275                     }
276                     break;
277                 case 'I' :
278                     if (CharOperation.equals(attributeName, AttributeNamesConstants.InnerClassName)) {
279                         int innerOffset = readOffset + 6;
280                         int number_of_classes = u2At(innerOffset);
281                         if (number_of_classes != 0) {
282                             innerOffset+= 2;
283                             this.innerInfos = new InnerClassInfo[number_of_classes];
284                             for (int j = 0; j < number_of_classes; j++) {
285                                 this.innerInfos[j] =
286                                     new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset);
287                                 if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
288                                     this.innerInfo = this.innerInfos[j];
289                                     this.innerInfoIndex = j;
290                                 }
291                                 innerOffset += 8;
292                             }
293                             if (this.innerInfo != null) {
294                                 char[] enclosingType = this.innerInfo.getEnclosingTypeName();
295                                 if (enclosingType != null) {
296                                     this.enclosingTypeName = enclosingType;
297                                 }
298                             }
299                         }
300                     } else if (CharOperation.equals(attributeName, AttributeNamesConstants.InconsistentHierarchy)) {
301                         this.tagBits |= TagBits.HasInconsistentHierarchy;
302                     }
303                     break;
304                 case 'S' :
305                     if (attributeName.length > 2) {
306                         switch(attributeName[1]) {
307                             case 'o' :
308                                 if (CharOperation.equals(attributeName, AttributeNamesConstants.SourceName)) {
309                                     utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
310                                     this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
311                                 }
312                                 break;
313                             case 'y' :
314                                 if (CharOperation.equals(attributeName, AttributeNamesConstants.SyntheticName)) {
315                                     this.accessFlags |= ClassFileConstants.AccSynthetic;
316                                 }
317                                 break;
318                             case 'i' :
319                                 if (CharOperation.equals(attributeName, AttributeNamesConstants.SignatureName)) {
320                                     utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
321                                     this.signature = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
322                                 }
323                         }
324                     }
325                     break;
326                 case 'R' :
327                     if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeVisibleAnnotationsName)) {
328                         decodeAnnotations(readOffset, true);
329                     } else if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeInvisibleAnnotationsName)) {
330                         decodeAnnotations(readOffset, false);
331                     }
332                     break;
333             }
334             readOffset += (6 + u4At(readOffset + 2));
335         }
336         if (fullyInitialize) {
337             this.initialize();
338         }
339     } catch(ClassFormatException e) {
340         throw e;
341     } catch (Exception JavaDoc e) {
342         throw new ClassFormatException(
343             ClassFormatException.ErrTruncatedInput,
344             readOffset);
345     }
346 }
347
348 /**
349  * Answer the receiver's access flags. The value of the access_flags
350  * item is a mask of modifiers used with class and interface declarations.
351  * @return int
352  */

353 public int accessFlags() {
354     return this.accessFlags;
355 }
356 private void decodeAnnotations(int offset, boolean runtimeVisible) {
357     int numberOfAnnotations = u2At(offset + 6);
358     if (numberOfAnnotations > 0) {
359         int readOffset = offset + 8;
360         AnnotationInfo[] newInfos = null;
361         int newInfoCount = 0;
362         for (int i = 0; i < numberOfAnnotations; i++) {
363             // With the last parameter being 'false', the data structure will not be flushed out
364
AnnotationInfo newInfo = new AnnotationInfo(this.reference, this.constantPoolOffsets, readOffset, runtimeVisible, false);
365             readOffset += newInfo.readOffset;
366             long standardTagBits = newInfo.standardAnnotationTagBits;
367             if (standardTagBits != 0) {
368                 this.tagBits |= standardTagBits;
369             } else {
370                 if (newInfos == null)
371                     newInfos = new AnnotationInfo[numberOfAnnotations - i];
372                 newInfos[newInfoCount++] = newInfo;
373             }
374         }
375         if (newInfos == null)
376             return; // nothing to record in this.annotations
377

378         if (this.annotations == null) {
379             if (newInfoCount != newInfos.length)
380                 System.arraycopy(newInfos, 0, newInfos = new AnnotationInfo[newInfoCount], 0, newInfoCount);
381             this.annotations = newInfos;
382         } else {
383             int length = this.annotations.length;
384             AnnotationInfo[] temp = new AnnotationInfo[length + newInfoCount];
385             System.arraycopy(this.annotations, 0, temp, 0, length);
386             System.arraycopy(newInfos, 0, temp, length, newInfoCount);
387             this.annotations = temp;
388         }
389     }
390 }
391 /**
392  * Answer the char array that corresponds to the class name of the constant class.
393  * constantPoolIndex is the index in the constant pool that is a constant class entry.
394  *
395  * @param constantPoolIndex int
396  * @return char[]
397  */

398 private char[] getConstantClassNameAt(int constantPoolIndex) {
399     int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
400     return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
401 }
402 /**
403  * Answer the int array that corresponds to all the offsets of each entry in the constant pool
404  *
405  * @return int[]
406  */

407 public int[] getConstantPoolOffsets() {
408     return this.constantPoolOffsets;
409 }
410 /*
411  * Answer the resolved compoundName of the enclosing type
412  * or null if the receiver is a top level type.
413  */

414 public char[] getEnclosingTypeName() {
415     return this.enclosingTypeName;
416 }
417 /**
418  * Answer the receiver's this.fields or null if the array is empty.
419  * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
420  */

421 public IBinaryField[] getFields() {
422     return this.fields;
423 }
424 /**
425  * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
426  */

427 public char[] getFileName() {
428     return this.classFileName;
429 }
430 public char[] getGenericSignature() {
431     return this.signature;
432 }
433 /**
434  * Answer the source name if the receiver is a inner type. Return null if it is an anonymous class or if the receiver is a top-level class.
435  * e.g.
436  * public class A {
437  * public class B {
438  * }
439  * public void foo() {
440  * class C {}
441  * }
442  * public Runnable bar() {
443  * return new Runnable() {
444  * public void run() {}
445  * };
446  * }
447  * }
448  * It returns {'B'} for the member A$B
449  * It returns null for A
450  * It returns {'C'} for the local class A$1$C
451  * It returns null for the anonymous A$1
452  * @return char[]
453  */

454 public char[] getInnerSourceName() {
455     if (this.innerInfo != null)
456         return this.innerInfo.getSourceName();
457     return null;
458 }
459 /**
460  * Answer the resolved names of the receiver's interfaces in the
461  * class file format as specified in section 4.2 of the Java 2 VM spec
462  * or null if the array is empty.
463  *
464  * For example, java.lang.String is java/lang/String.
465  * @return char[][]
466  */

467 public char[][] getInterfaceNames() {
468     return this.interfaceNames;
469 }
470
471 /**
472  * Answer the receiver's nested types or null if the array is empty.
473  *
474  * This nested type info is extracted from the inner class attributes. Ask the
475  * name environment to find a member type using its compound name
476  *
477  * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
478  */

479 public IBinaryNestedType[] getMemberTypes() {
480     // we might have some member types of the current type
481
if (this.innerInfos == null) return null;
482
483     int length = this.innerInfos.length;
484     int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
485     if (length != startingIndex) {
486         IBinaryNestedType[] memberTypes =
487             new IBinaryNestedType[length - this.innerInfoIndex];
488         int memberTypeIndex = 0;
489         for (int i = startingIndex; i < length; i++) {
490             InnerClassInfo currentInnerInfo = this.innerInfos[i];
491             int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
492             int innerNameIndex = currentInnerInfo.innerNameIndex;
493             /*
494              * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
495              * attribute entry is a member class, but due to the bug:
496              * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
497              * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
498              *
499              * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
500              * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
501              * terminateQueue() method for which the inner attribute is boggus.
502              * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
503              * So I added this extra check to filter out this anonymous class from the
504              * member types.
505              */

506             if (outerClassNameIdx != 0
507                 && innerNameIndex != 0
508                 && outerClassNameIdx == this.classNameIndex
509                 && currentInnerInfo.getSourceName().length != 0) {
510                 memberTypes[memberTypeIndex++] = currentInnerInfo;
511             }
512         }
513         if (memberTypeIndex == 0) return null;
514         if (memberTypeIndex != memberTypes.length) {
515             // we need to resize the memberTypes array. Some local or anonymous classes
516
// are present in the current class.
517
System.arraycopy(
518                 memberTypes,
519                 0,
520                 (memberTypes = new IBinaryNestedType[memberTypeIndex]),
521                 0,
522                 memberTypeIndex);
523         }
524         return memberTypes;
525     }
526     return null;
527 }
528 /**
529  * Answer the receiver's this.methods or null if the array is empty.
530  * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
531  */

532 public IBinaryMethod[] getMethods() {
533     return this.methods;
534 }
535 /**
536  * @return the annotations or null if there is none.
537  */

538 public IBinaryAnnotation[] getAnnotations() {
539     return this.annotations;
540 }
541 /**
542  * Answer an int whose bits are set according the access constants
543  * defined by the VM spec.
544  * Set the AccDeprecated and AccSynthetic bits if necessary
545  * @return int
546  */

547 public int getModifiers() {
548     if (this.innerInfo != null) {
549         return this.innerInfo.getModifiers() | (this.accessFlags & ClassFileConstants.AccDeprecated);
550     }
551     return this.accessFlags;
552 }
553 /**
554  * Answer the resolved name of the type in the
555  * class file format as specified in section 4.2 of the Java 2 VM spec.
556  *
557  * For example, java.lang.String is java/lang/String.
558  * @return char[]
559  */

560 public char[] getName() {
561     return this.className;
562 }
563 public char[] getSourceName() {
564     if (this.sourceName != null)
565         return this.sourceName;
566
567     char[] name = getInnerSourceName(); // member or local scenario
568
if (name == null) {
569         name = getName(); // extract from full name
570
int start;
571         if (isAnonymous()) {
572             start = CharOperation.indexOf('$', name, CharOperation.lastIndexOf('/', name) + 1) + 1;
573         } else {
574             start = CharOperation.lastIndexOf('/', name) + 1;
575         }
576         if (start > 0) {
577             char[] newName = new char[name.length - start];
578             System.arraycopy(name, start, newName, 0, newName.length);
579             name = newName;
580         }
581     }
582     return this.sourceName = name;
583 }
584 /**
585  * Answer the resolved name of the receiver's superclass in the
586  * class file format as specified in section 4.2 of the Java 2 VM spec
587  * or null if it does not have one.
588  *
589  * For example, java.lang.String is java/lang/String.
590  * @return char[]
591  */

592 public char[] getSuperclassName() {
593     return this.superclassName;
594 }
595 public long getTagBits() {
596     return this.tagBits;
597 }
598 /**
599  * Answer the major/minor version defined in this class file according to the VM spec.
600  * as a long: (major<<16)+minor
601  * @return the major/minor version found
602  */

603 public long getVersion() {
604     return this.version;
605 }
606 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
607     int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
608     int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
609     int index1 = 0;
610     int index2 = 0;
611
612     end : while (index1 < length1 && index2 < length2) {
613         while (currentFieldInfos[index1].isSynthetic()) {
614             if (++index1 >= length1) break end;
615         }
616         while (otherFieldInfos[index2].isSynthetic()) {
617             if (++index2 >= length2) break end;
618         }
619         if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
620             return true;
621     }
622
623     while (index1 < length1) {
624         if (!currentFieldInfos[index1++].isSynthetic()) return true;
625     }
626     while (index2 < length2) {
627         if (!otherFieldInfos[index2++].isSynthetic()) return true;
628     }
629     return false;
630 }
631 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
632     int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
633     int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
634     int index1 = 0;
635     int index2 = 0;
636
637     MethodInfo m;
638     end : while (index1 < length1 && index2 < length2) {
639         while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
640             if (++index1 >= length1) break end;
641         }
642         while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
643             if (++index2 >= length2) break end;
644         }
645         if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
646             return true;
647     }
648
649     while (index1 < length1) {
650         if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
651     }
652     while (index2 < length2) {
653         if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
654     }
655     return false;
656 }
657 /**
658  * Check if the receiver has structural changes compare to the byte array in argument.
659  * Structural changes are:
660  * - modifiers changes for the class, the this.fields or the this.methods
661  * - signature changes for this.fields or this.methods.
662  * - changes in the number of this.fields or this.methods
663  * - changes for field constants
664  * - changes for thrown exceptions
665  * - change for the super class or any super interfaces.
666  * - changes for member types name or modifiers
667  * If any of these changes occurs, the method returns true. false otherwise.
668  * The synthetic fields are included and the members are not required to be sorted.
669  * @param newBytes the bytes of the .class file we want to compare the receiver to
670  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
671  */

672 public boolean hasStructuralChanges(byte[] newBytes) {
673     return hasStructuralChanges(newBytes, true, true);
674 }
675 /**
676  * Check if the receiver has structural changes compare to the byte array in argument.
677  * Structural changes are:
678  * - modifiers changes for the class, the this.fields or the this.methods
679  * - signature changes for this.fields or this.methods.
680  * - changes in the number of this.fields or this.methods
681  * - changes for field constants
682  * - changes for thrown exceptions
683  * - change for the super class or any super interfaces.
684  * - changes for member types name or modifiers
685  * If any of these changes occurs, the method returns true. false otherwise.
686  * @param newBytes the bytes of the .class file we want to compare the receiver to
687  * @param orderRequired a boolean indicating whether the members should be sorted or not
688  * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
689  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
690  */

691 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
692     try {
693         ClassFileReader newClassFile =
694             new ClassFileReader(newBytes, this.classFileName);
695         // type level comparison
696
// modifiers
697
if (this.getModifiers() != newClassFile.getModifiers())
698             return true;
699
700         // only consider a portion of the tagbits which indicate a structural change for dependents
701
// e.g. @Override change has no influence outside
702
long OnlyStructuralTagBits = TagBits.AnnotationTargetMASK // different @Target status ?
703
| TagBits.AnnotationDeprecated // different @Deprecated status ?
704
| TagBits.AnnotationRetentionMASK // different @Retention status ?
705
| TagBits.HasInconsistentHierarchy; // different hierarchy status ?
706

707         // meta-annotations
708
if ((this.getTagBits() & OnlyStructuralTagBits) != (newClassFile.getTagBits() & OnlyStructuralTagBits))
709             return true;
710         
711         // generic signature
712
if (!CharOperation.equals(this.getGenericSignature(), newClassFile.getGenericSignature()))
713             return true;
714         // superclass
715
if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
716             return true;
717         // interfaces
718
char[][] newInterfacesNames = newClassFile.getInterfaceNames();
719         if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
720
int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
721             if (newInterfacesLength != this.interfacesCount)
722                 return true;
723             for (int i = 0, max = this.interfacesCount; i < max; i++)
724                 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
725                     return true;
726         }
727
728         // member types
729
IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
730         IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
731         if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
732
int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
733             int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
734             if (currentMemberTypeLength != otherMemberTypeLength)
735                 return true;
736             for (int i = 0; i < currentMemberTypeLength; i++)
737                 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
738                     || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
739                         return true;
740         }
741
742         // fields
743
FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
744         int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
745         boolean compareFields = true;
746         if (this.fieldsCount == otherFieldInfosLength) {
747             int i = 0;
748             for (; i < this.fieldsCount; i++)
749                 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
750             if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
751                 return true;
752         }
753         if (compareFields) {
754             if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
755                 return true;
756             if (orderRequired) {
757                 if (this.fieldsCount != 0)
758                     Arrays.sort(this.fields);
759                 if (otherFieldInfosLength != 0)
760                     Arrays.sort(otherFieldInfos);
761             }
762             if (excludesSynthetic) {
763                 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
764                     return true;
765             } else {
766                 for (int i = 0; i < this.fieldsCount; i++)
767                     if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
768                         return true;
769             }
770         }
771         
772         // methods
773
MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
774         int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
775         boolean compareMethods = true;
776         if (this.methodsCount == otherMethodInfosLength) {
777             int i = 0;
778             for (; i < this.methodsCount; i++)
779                 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
780             if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
781                 return true;
782         }
783         if (compareMethods) {
784             if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
785                 return true;
786             if (orderRequired) {
787                 if (this.methodsCount != 0)
788                     Arrays.sort(this.methods);
789                 if (otherMethodInfosLength != 0)
790                     Arrays.sort(otherMethodInfos);
791             }
792             if (excludesSynthetic) {
793                 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
794                     return true;
795             } else {
796                 for (int i = 0; i < this.methodsCount; i++)
797                     if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
798                         return true;
799             }
800         }
801
802         return false;
803     } catch (ClassFormatException e) {
804         return true;
805     }
806 }
807 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
808     // generic signature
809
if (!CharOperation.equals(currentFieldInfo.getGenericSignature(), otherFieldInfo.getGenericSignature()))
810         return true;
811     if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
812         return true;
813     if ((currentFieldInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherFieldInfo.getTagBits() & TagBits.AnnotationDeprecated))
814         return true;
815     if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
816         return true;
817     if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
818         return true;
819     if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
820         return true;
821     if (currentFieldInfo.hasConstant()) {
822         Constant currentConstant = currentFieldInfo.getConstant();
823         Constant otherConstant = otherFieldInfo.getConstant();
824         if (currentConstant.typeID() != otherConstant.typeID())
825             return true;
826         if (!currentConstant.getClass().equals(otherConstant.getClass()))
827             return true;
828         switch (currentConstant.typeID()) {
829             case TypeIds.T_int :
830                 return currentConstant.intValue() != otherConstant.intValue();
831             case TypeIds.T_byte :
832                 return currentConstant.byteValue() != otherConstant.byteValue();
833             case TypeIds.T_short :
834                 return currentConstant.shortValue() != otherConstant.shortValue();
835             case TypeIds.T_char :
836                 return currentConstant.charValue() != otherConstant.charValue();
837             case TypeIds.T_long :
838                 return currentConstant.longValue() != otherConstant.longValue();
839             case TypeIds.T_float :
840                 return currentConstant.floatValue() != otherConstant.floatValue();
841             case TypeIds.T_double :
842                 return currentConstant.doubleValue() != otherConstant.doubleValue();
843             case TypeIds.T_boolean :
844                 return currentConstant.booleanValue() != otherConstant.booleanValue();
845             case TypeIds.T_JavaLangString :
846                 return !currentConstant.stringValue().equals(otherConstant.stringValue());
847         }
848     }
849     return false;
850 }
851 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
852     // generic signature
853
if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
854         return true;
855     if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
856         return true;
857     if ((currentMethodInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherMethodInfo.getTagBits() & TagBits.AnnotationDeprecated))
858         return true;
859     if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
860         return true;
861     if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
862         return true;
863     if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
864         return true;
865
866     char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
867     char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
868     if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
869
int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
870         int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
871         if (currentThrownExceptionsLength != otherThrownExceptionsLength)
872             return true;
873         for (int k = 0; k < currentThrownExceptionsLength; k++)
874             if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
875                 return true;
876     }
877     return false;
878 }
879 /**
880  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
881  * will be therefore fully initialized and we can get rid of the bytes.
882  */

883 private void initialize() throws ClassFormatException {
884     try {
885         for (int i = 0, max = fieldsCount; i < max; i++) {
886             fields[i].initialize();
887         }
888         for (int i = 0, max = methodsCount; i < max; i++) {
889             methods[i].initialize();
890         }
891         if (innerInfos != null) {
892             for (int i = 0, max = innerInfos.length; i < max; i++) {
893                 innerInfos[i].initialize();
894             }
895         }
896         if (annotations != null) {
897             for (int i = 0, max = annotations.length; i < max; i++) {
898                 annotations[i].initialize();
899             }
900         }
901         this.reset();
902     } catch(RuntimeException JavaDoc e) {
903         ClassFormatException exception = new ClassFormatException(e, this.classFileName);
904         throw exception;
905     }
906 }
907 /**
908  * Answer true if the receiver is an anonymous type, false otherwise
909  *
910  * @return <CODE>boolean</CODE>
911  */

912 public boolean isAnonymous() {
913     if (this.innerInfo == null) return false;
914     char[] innerSourceName = this.innerInfo.getSourceName();
915     return (innerSourceName == null || innerSourceName.length == 0);
916 }
917 /**
918  * Answer whether the receiver contains the resolved binary form
919  * or the unresolved source form of the type.
920  * @return boolean
921  */

922 public boolean isBinaryType() {
923     return true;
924 }
925
926 /**
927  * Answer true if the receiver is a local type, false otherwise
928  *
929  * @return <CODE>boolean</CODE>
930  */

931 public boolean isLocal() {
932     if (this.innerInfo == null) return false;
933     if (this.innerInfo.getEnclosingTypeName() != null) return false;
934     char[] innerSourceName = this.innerInfo.getSourceName();
935     return (innerSourceName != null && innerSourceName.length > 0);
936 }
937 /**
938  * Answer true if the receiver is a member type, false otherwise
939  *
940  * @return <CODE>boolean</CODE>
941  */

942 public boolean isMember() {
943     if (this.innerInfo == null) return false;
944     if (this.innerInfo.getEnclosingTypeName() == null) return false;
945     char[] innerSourceName = this.innerInfo.getSourceName();
946     return (innerSourceName != null && innerSourceName.length > 0); // protection against ill-formed attributes (67600)
947
}
948 /**
949  * Answer true if the receiver is a nested type, false otherwise
950  *
951  * @return <CODE>boolean</CODE>
952  */

953 public boolean isNestedType() {
954     return this.innerInfo != null;
955 }
956 /**
957  * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
958  *
959  * @return char[]
960  */

961 public char[] sourceFileName() {
962     return this.sourceFileName;
963 }
964 public String JavaDoc toString() {
965     java.io.ByteArrayOutputStream JavaDoc out = new java.io.ByteArrayOutputStream JavaDoc();
966     java.io.PrintWriter JavaDoc print = new java.io.PrintWriter JavaDoc(out);
967     print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
968
print.println(" this.className: " + new String JavaDoc(getName())); //$NON-NLS-1$
969
print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String JavaDoc(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
970
print.println(" access_flags: " + printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
971
print.flush();
972     return out.toString();
973 }
974
975 /*
976 public static void main(String[] args) throws ClassFormatException, IOException {
977     if (args == null || args.length != 1) {
978         System.err.println("ClassFileReader <filename>"); //$NON-NLS-1$
979         System.exit(1);
980     }
981     File file = new File(args[0]);
982     ClassFileReader reader = read(file, true);
983     if (reader.annotations != null) {
984         System.err.println();
985         for (int i = 0; i < reader.annotations.length; i++)
986             System.err.println(reader.annotations[i]);
987     }
988     System.err.print("class "); //$NON-NLS-1$
989     System.err.print(reader.getName());
990     char[] superclass = reader.getSuperclassName();
991     if (superclass != null) {
992         System.err.print(" extends "); //$NON-NLS-1$
993         System.err.print(superclass);
994     }
995     System.err.println();
996     char[][] interfaces = reader.getInterfaceNames();
997     if (interfaces != null && interfaces.length > 0) {
998         System.err.print(" implements "); //$NON-NLS-1$
999         for (int i = 0; i < interfaces.length; i++) {
1000            if (i != 0) System.err.print(", "); //$NON-NLS-1$
1001            System.err.println(interfaces[i]);
1002        }
1003    }
1004    System.err.println();
1005    System.err.println('{');
1006    if (reader.fields != null) {
1007        for (int i = 0; i < reader.fields.length; i++) {
1008            System.err.println(reader.fields[i]);
1009            System.err.println();
1010        }
1011    }
1012    if (reader.methods != null) {
1013        for (int i = 0; i < reader.methods.length; i++) {
1014            System.err.println(reader.methods[i]);
1015            System.err.println();
1016        }
1017    }
1018    System.err.println();
1019    System.err.println('}');
1020}
1021*/

1022}
1023
Popular Tags