added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / classfmt / ClassFileReader.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-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;
14 import java.io.IOException;
15 import java.util.Arrays;
16
17 import org.eclipse.jdt.core.Signature;
18 import org.eclipse.jdt.core.compiler.CharOperation;
19 import org.eclipse.jdt.internal.compiler.ast.Annotation;
20 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
21 import org.eclipse.jdt.internal.compiler.env.*;
22 import org.eclipse.jdt.internal.compiler.impl.Constant;
23 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
24 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
25 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
26 import org.eclipse.jdt.internal.compiler.util.Util;
27
28 public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
29 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
30         return read(file, false);
31 }
32 public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
33         byte classFileBytes[] = Util.getFileByteContent(file);
34         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
35         if (fullyInitialize) {
36                 classFileReader.initialize();
37         }
38         return classFileReader;
39 }
40 public static ClassFileReader read(
41         java.util.zip.ZipFile zip, 
42         String filename)
43         throws ClassFormatException, java.io.IOException {
44                 return read(zip, filename, false);
45 }
46 public static ClassFileReader read(
47         java.util.zip.ZipFile zip, 
48         String filename,
49         boolean fullyInitialize)
50         throws ClassFormatException, java.io.IOException {
51         java.util.zip.ZipEntry ze = zip.getEntry(filename);
52         if (ze == null)
53                 return null;
54         byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
55         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
56         if (fullyInitialize) {
57                 classFileReader.initialize();
58         }
59         return classFileReader;
60 }
61 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
62         return read(fileName, false);
63 }
64 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
65         return read(new File(fileName), fullyInitialize);
66 }
67         private int accessFlags;
68         private char[] classFileName;
69         private char[] className;
70         private int classNameIndex;
71         private int constantPoolCount;
72         private int[] constantPoolOffsets;
73         private FieldInfo[] fields;
74         private int fieldsCount;
75         // initialized in case the .class file is a nested type
76         private InnerClassInfo innerInfo;
77         private int innerInfoIndex;
78         private InnerClassInfo[] innerInfos;
79         private char[][] interfaceNames;
80         private int interfacesCount;
81         private MethodInfo[] methods;
82         private int methodsCount;
83         private char[] signature;
84         private char[] sourceFileName;
85         private char[] superclassName;
86         private long tagBits;
87         private long version;
88
89 /**
90  * @param classFileBytes Actual bytes of a .class file
91  * @param fileName      Actual name of the file that contains the bytes, can be null
92  * 
93  * @exception ClassFormatException
94  */
95 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
96         this(classFileBytes, fileName, false);
97 }
98
99 /**
100  * @param classFileBytes byte[]
101  *              Actual bytes of a .class file
102  * 
103  * @param fileName char[]
104  *              Actual name of the file that contains the bytes, can be null
105  * 
106  * @param fullyInitialize boolean
107  *              Flag to fully initialize the new object
108  * @exception ClassFormatException
109  */
110 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
111         // This method looks ugly but is actually quite simple, the constantPool is constructed
112         // in 3 passes.  All non-primitive constant pool members that usually refer to other members
113         // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
114         // all subsequent uses of the constant pool element faster.
115         super(classFileBytes, 0);
116         this.classFileName = fileName;
117         int readOffset = 10;
118         try {
119                 this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
120                 constantPoolCount = this.u2At(8);
121                 // Pass #1 - Fill in all primitive constants
122                 this.constantPoolOffsets = new int[constantPoolCount];
123                 for (int i = 1; i < constantPoolCount; i++) {
124                         int tag = this.u1At(readOffset);
125                         switch (tag) {
126                                 case Utf8Tag :
127                                         this.constantPoolOffsets[i] = readOffset;
128                                         readOffset += u2At(readOffset + 1);
129                                         readOffset += ConstantUtf8FixedSize;
130                                         break;
131                                 case IntegerTag :
132                                         this.constantPoolOffsets[i] = readOffset;
133                                         readOffset += ConstantIntegerFixedSize;
134                                         break;
135                                 case FloatTag :
136                                         this.constantPoolOffsets[i] = readOffset;
137                                         readOffset += ConstantFloatFixedSize;
138                                         break;
139                                 case LongTag :
140                                         this.constantPoolOffsets[i] = readOffset;
141                                         readOffset += ConstantLongFixedSize;
142                                         i++;
143                                         break;
144                                 case DoubleTag :
145                                         this.constantPoolOffsets[i] = readOffset;
146                                         readOffset += ConstantDoubleFixedSize;
147                                         i++;
148                                         break;
149                                 case ClassTag :
150                                         this.constantPoolOffsets[i] = readOffset;
151                                         readOffset += ConstantClassFixedSize;
152                                         break;
153                                 case StringTag :
154                                         this.constantPoolOffsets[i] = readOffset;
155                                         readOffset += ConstantStringFixedSize;
156                                         break;
157                                 case FieldRefTag :
158                                         this.constantPoolOffsets[i] = readOffset;
159                                         readOffset += ConstantFieldRefFixedSize;
160                                         break;
161                                 case MethodRefTag :
162                                         this.constantPoolOffsets[i] = readOffset;
163                                         readOffset += ConstantMethodRefFixedSize;
164                                         break;
165                                 case InterfaceMethodRefTag :
166                                         this.constantPoolOffsets[i] = readOffset;
167                                         readOffset += ConstantInterfaceMethodRefFixedSize;
168                                         break;
169                                 case NameAndTypeTag :
170                                         this.constantPoolOffsets[i] = readOffset;
171                                         readOffset += ConstantNameAndTypeFixedSize;
172                         }
173                 }
174                 // Read and validate access flags
175                 this.accessFlags = u2At(readOffset);
176                 readOffset += 2;
177
178                 // Read the classname, use exception handlers to catch bad format
179                 this.classNameIndex = u2At(readOffset);
180                 this.className = getConstantClassNameAt(this.classNameIndex);
181                 readOffset += 2;
182
183                 // Read the superclass name, can be null for java.lang.Object
184                 int superclassNameIndex = u2At(readOffset);
185                 readOffset += 2;
186                 // if superclassNameIndex is equals to 0 there is no need to set a value for the 
187                 // field this.superclassName. null is fine.
188                 if (superclassNameIndex != 0) {
189                         this.superclassName = getConstantClassNameAt(superclassNameIndex);
190                 }
191
192                 // Read the interfaces, use exception handlers to catch bad format
193                 this.interfacesCount = u2At(readOffset);
194                 readOffset += 2;
195                 if (this.interfacesCount != 0) {
196                         this.interfaceNames = new char[this.interfacesCount][];
197                         for (int i = 0; i < this.interfacesCount; i++) {
198                                 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
199                                 readOffset += 2;
200                         }
201                 }
202                 // Read the this.fields, use exception handlers to catch bad format
203                 this.fieldsCount = u2At(readOffset);
204                 readOffset += 2;
205                 if (this.fieldsCount != 0) {
206                         FieldInfo field;
207                         this.fields = new FieldInfo[this.fieldsCount];
208                         for (int i = 0; i < this.fieldsCount; i++) {
209                                 field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
210                                 this.fields[i] = field;
211                                 readOffset += field.sizeInBytes();
212                         }
213                 }
214                 // Read the this.methods
215                 this.methodsCount = u2At(readOffset);
216                 readOffset += 2;
217                 if (this.methodsCount != 0) {
218                         this.methods = new MethodInfo[this.methodsCount];
219                         MethodInfo method;
220                         for (int i = 0; i < this.methodsCount; i++) {
221                                 method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
222                                 this.methods[i] = method;
223                                 readOffset += method.sizeInBytes();
224                         }
225                 }
226
227                 // Read the attributes
228                 int attributesCount = u2At(readOffset);
229                 readOffset += 2;
230
231                 for (int i = 0; i < attributesCount; i++) {
232                         int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
233                         char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
234                         if (attributeName.length == 0) {
235                                 readOffset += (6 + u4At(readOffset + 2));
236                                 continue;
237                         }
238                         switch(attributeName[0] ) {
239                                 case 'D' :
240                                         if (CharOperation.equals(attributeName, DeprecatedName)) {
241                                                 this.accessFlags |= AccDeprecated;
242                                         }
243                                         break;
244                                 case 'I' :
245                                         if (CharOperation.equals(attributeName, InnerClassName)) {
246                                                 int innerOffset = readOffset + 6;
247                                                 int number_of_classes = u2At(innerOffset);
248                                                 if (number_of_classes != 0) {
249                                                         innerOffset+= 2;
250                                                         this.innerInfos = new InnerClassInfo[number_of_classes];
251                                                         for (int j = 0; j < number_of_classes; j++) {
252                                                                 this.innerInfos[j] = 
253                                                                         new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset); 
254                                                                 if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
255                                                                         this.innerInfo = this.innerInfos[j];
256                                                                         this.innerInfoIndex = j;
257                                                                 }
258                                                                 innerOffset += 8;
259                                                         }
260                                                 }
261                                         }
262                                         break;
263                                 case 'S' :
264                                         if (attributeName.length > 2) {
265                                                 switch(attributeName[1]) {
266                                                         case 'o' :
267                                                                 if (CharOperation.equals(attributeName, SourceName)) {
268                                                                         utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
269                                                                         this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
270                                                                 }
271                                                                 break;
272                                                         case 'y' :
273                                                                 if (CharOperation.equals(attributeName, SyntheticName)) {
274                                                                         this.accessFlags |= AccSynthetic;
275                                                                 }
276                                                                 break;
277                                                         case 'i' :
278                                                                 if (CharOperation.equals(attributeName, SignatureName)) {
279                                                                         utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
280                                                                         this.signature = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));                          
281                                                                 }
282                                                 }
283                                         }
284                                         break;
285                                 case 'R' :
286                                         if (CharOperation.equals(attributeName, RuntimeVisibleAnnotationsName)) {
287                                                 decodeStandardAnnotations(readOffset);
288                                         }
289                                         break;
290                         }
291                         readOffset += (6 + u4At(readOffset + 2));
292                 }
293                 if (fullyInitialize) {
294                         this.initialize();
295                 }
296         } catch(ClassFormatException e) {
297                 throw e;
298         } catch (Exception e) {
299                 throw new ClassFormatException(
300                         ClassFormatException.ErrTruncatedInput, 
301                         readOffset); 
302         }
303 }
304
305 /**
306  *      Answer the receiver's access flags.  The value of the access_flags
307  *      item is a mask of modifiers used with class and interface declarations.
308  *  @return int 
309  */
310 public int accessFlags() {
311         return this.accessFlags;
312 }
313 private int decodeAnnotation(int offset) {
314         int readOffset = offset;
315         int utf8Offset = this.constantPoolOffsets[u2At(offset)];
316         char[] typeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
317         typeName = Signature.toCharArray(typeName);
318         CharOperation.replace(typeName, '/', '.');
319         char[][] qualifiedTypeName = CharOperation.splitOn('.', typeName);
320         int numberOfPairs = u2At(offset + 2);
321         readOffset += 4;
322         switch(qualifiedTypeName.length) {
323                 case 3 :
324                         if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_DEPRECATED)) {
325                                 this.tagBits |= TagBits.AnnotationDeprecated;
326                                 return readOffset;              
327                         }
328                         break;
329                 case 4 :
330                         char[] lastPart = qualifiedTypeName[3];
331                         if (lastPart.length > 0) {
332                                 switch(lastPart[0]) {
333                                         case 'R' :
334                                                 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_RETENTION)) {
335                                                         for (int i = 0; i < numberOfPairs; i++) {
336                                                                 readOffset += 2;
337                                                                 readOffset = decodeElementValueForJavaLangAnnotationRetention(readOffset);
338                                                         }
339                                                         return readOffset;
340                                                 }
341                                                 break;
342                                         case 'T' :
343                                                 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_TARGET)) {
344                                                         for (int i = 0; i < numberOfPairs; i++) {
345                                                                 readOffset += 2;
346                                                                 readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
347                                                         }
348                                                         return readOffset;              
349                                                 }
350                                                 break;
351                                         case 'D' :
352                                                 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED)) {
353                                                         this.tagBits |= TagBits.AnnotationDocumented;
354                                                         return readOffset;              
355                                                 }
356                                                 break;
357                                         case 'I' :
358                                                 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_INHERITED)) {
359                                                         this.tagBits |= TagBits.AnnotationInherited;
360                                                         return readOffset;              
361                                                 }
362                                 }
363                         }
364         }
365         for (int i = 0; i < numberOfPairs; i++) {
366                 readOffset += 2;
367                 readOffset = decodeElementValue(readOffset);
368         }
369         return readOffset;
370 }
371 private int decodeElementValue(int offset) {
372         int readOffset = offset;
373         int tag = u1At(readOffset);
374         readOffset++;
375         switch(tag) {
376                 case 'B' :
377                 case 'C' :
378                 case 'D' :
379                 case 'F' :
380                 case 'I' :
381                 case 'J' :
382                 case 'S' :
383                 case 'Z' :
384                 case 's' :
385                         readOffset += 2;
386                         break;
387                 case 'e' :
388                         readOffset += 4;
389                         break;
390                 case 'c' :
391                         readOffset += 2;
392                         break;
393                 case '@' :
394                         readOffset += decodeAnnotation(readOffset);
395                         break;
396                 case '[' :
397                         int numberOfValues = u2At(readOffset);
398                         readOffset += 2;
399                         for (int i = 0; i < numberOfValues; i++) {
400                                 readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
401                         }
402                         break;
403         }
404         return readOffset;
405 }
406 private int decodeElementValueForJavaLangAnnotationTarget(int offset) {
407         int readOffset = offset;
408         int tag = u1At(readOffset);
409         readOffset++;
410         switch(tag) {
411                 case 'B' :
412                 case 'C' :
413                 case 'D' :
414                 case 'F' :
415                 case 'I' :
416                 case 'J' :
417                 case 'S' :
418                 case 'Z' :
419                 case 's' :
420                         readOffset += 2;
421                         break;
422                 case 'e' :
423                         readOffset += 2;
424                         int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
425                         char[] constName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
426                         readOffset += 2;
427                         this.tagBits |= Annotation.getTargetElementType(constName);
428                         break;
429                 case 'c' :
430                         readOffset += 2;
431                         break;
432                 case '@' :
433                         readOffset += decodeAnnotation(readOffset);
434                         break;
435                 case '[' :
436                         int numberOfValues = u2At(readOffset);
437                         readOffset += 2;
438                         if (numberOfValues == 0) {
439                                 this.tagBits |= TagBits.AnnotationTarget;
440                         } else {
441                                 for (int i = 0; i < numberOfValues; i++) {
442                                         readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
443                                 }
444                         }
445                         break;
446         }
447         return readOffset;
448 }
449 private int decodeElementValueForJavaLangAnnotationRetention(int offset) {
450         int readOffset = offset;
451         int tag = u1At(readOffset);
452         readOffset++;
453         switch(tag) {
454                 case 'B' :
455                 case 'C' :
456                 case 'D' :
457                 case 'F' :
458                 case 'I' :
459                 case 'J' :
460                 case 'S' :
461                 case 'Z' :
462                 case 's' :
463                         readOffset += 2;
464                         break;
465                 case 'e' :
466                         readOffset += 2;
467                         int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
468                         char[] constName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
469                         readOffset += 2;
470                         this.tagBits |= Annotation.getRetentionPolicy(constName);
471                         break;
472                 case 'c' :
473                         readOffset += 2;
474                         break;
475                 case '@' :
476                         readOffset += decodeAnnotation(readOffset);
477                         break;
478                 case '[' :
479                         int numberOfValues = u2At(readOffset);
480                         readOffset += 2;
481                         for (int i = 0; i < numberOfValues; i++) {
482                                 readOffset = decodeElementValueForJavaLangAnnotationRetention(readOffset);
483                         }
484                         break;
485         }
486         return readOffset;
487 }
488 /**
489  * @param offset the offset is located at the beginning of the runtime visible 
490  * annotation attribute.
491  */
492 private void decodeStandardAnnotations(int offset) {
493         int numberOfAnnotations = u2At(offset + 6);
494         int readOffset = offset + 8;
495         for (int i = 0; i < numberOfAnnotations; i++) {
496                 readOffset = decodeAnnotation(readOffset);
497         }
498 }
499 /**
500  * Answer the char array that corresponds to the class name of the constant class.
501  * constantPoolIndex is the index in the constant pool that is a constant class entry.
502  *
503  * @param constantPoolIndex int
504  * @return char[]
505  */
506 private char[] getConstantClassNameAt(int constantPoolIndex) {
507         int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
508         return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
509 }
510 /**
511  * Answer the int array that corresponds to all the offsets of each entry in the constant pool
512  *
513  * @return int[]
514  */
515 public int[] getConstantPoolOffsets() {
516         return this.constantPoolOffsets;
517 }
518 /*
519  * Answer the resolved compoundName of the enclosing type
520  * or null if the receiver is a top level type.
521  */
522 public char[] getEnclosingTypeName() {
523         if (this.innerInfo != null && !this.isAnonymous()) {
524                 return this.innerInfo.getEnclosingTypeName();
525         }
526         return null;
527 }
528 /**
529  * Answer the receiver's this.fields or null if the array is empty.
530  * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
531  */
532 public IBinaryField[] getFields() {
533         return this.fields;
534 }
535 /**
536  * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
537  */
538 public char[] getFileName() {
539         return this.classFileName;
540 }
541 public char[] getGenericSignature() {
542         return this.signature;
543 }
544 /**
545  * 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.
546  * e.g.
547  * public class A {
548  *      public class B {
549  *      }
550  *      public void foo() {
551  *              class C {}
552  *      }
553  *      public Runnable bar() {
554  *              return new Runnable() {
555  *                      public void run() {}
556  *              };
557  *      }
558  * }
559  * It returns {'B'} for the member A$B
560  * It returns null for A
561  * It returns {'C'} for the local class A$1$C
562  * It returns null for the anonymous A$1
563  * @return char[]
564  */
565 public char[] getInnerSourceName() {
566         if (this.innerInfo != null)
567                 return this.innerInfo.getSourceName();
568         return null;
569 }
570 /**
571  * Answer the resolved names of the receiver's interfaces in the
572  * class file format as specified in section 4.2 of the Java 2 VM spec
573  * or null if the array is empty.
574  *
575  * For example, java.lang.String is java/lang/String.
576  * @return char[][]
577  */
578 public char[][] getInterfaceNames() {
579         return this.interfaceNames;
580 }
581
582 /**
583  * @see org.eclipse.jdt.internal.compiler.env.IGenericType#getKind()
584  */
585 public int getKind() {
586         int modifiers = getModifiers();
587         if ((modifiers & AccInterface) != 0) {
588                 if ((modifiers & AccAnnotation) != 0) return IGenericType.ANNOTATION_TYPE_DECL;
589                 return IGenericType.INTERFACE_DECL;
590         }
591         if ((modifiers & AccEnum) != 0) return IGenericType.ENUM_DECL;
592         return IGenericType.CLASS_DECL;
593 }
594 /**
595  * Answer the receiver's nested types or null if the array is empty.
596  * 
597  * This nested type info is extracted from the inner class attributes. Ask the
598  * name environment to find a member type using its compound name
599  * 
600  * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
601  */
602 public IBinaryNestedType[] getMemberTypes() {
603         // we might have some member types of the current type
604         if (this.innerInfos == null) return null;
605
606         int length = this.innerInfos.length;
607         int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
608         if (length != startingIndex) {
609                 IBinaryNestedType[] memberTypes = 
610                         new IBinaryNestedType[length - this.innerInfoIndex]; 
611                 int memberTypeIndex = 0;
612                 for (int i = startingIndex; i < length; i++) {
613                         InnerClassInfo currentInnerInfo = this.innerInfos[i];
614                         int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
615                         int innerNameIndex = currentInnerInfo.innerNameIndex;
616                         /*
617                          * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
618                          * attribute entry is a member class, but due to the bug:
619                          * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
620                          * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
621                          * 
622                          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
623                          * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
624                          * terminateQueue() method for which the inner attribute is boggus.
625                          * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
626                          * So I added this extra check to filter out this anonymous class from the 
627                          * member types.
628                          */
629                         if (outerClassNameIdx != 0
630                                 && innerNameIndex != 0
631                                 && outerClassNameIdx == this.classNameIndex
632                                 && currentInnerInfo.getSourceName().length != 0) {
633                                 memberTypes[memberTypeIndex++] = currentInnerInfo;
634                         }
635                 }
636                 if (memberTypeIndex == 0) return null;
637                 if (memberTypeIndex != memberTypes.length) {
638                         // we need to resize the memberTypes array. Some local or anonymous classes
639                         // are present in the current class.
640                         System.arraycopy(
641                                 memberTypes, 
642                                 0, 
643                                 (memberTypes = new IBinaryNestedType[memberTypeIndex]), 
644                                 0, 
645                                 memberTypeIndex); 
646                 }
647                 return memberTypes;
648         }
649         return null;
650 }
651 /**
652  * Answer the receiver's this.methods or null if the array is empty.
653  * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
654  */
655 public IBinaryMethod[] getMethods() {
656         return this.methods;
657 }
658 /**
659  * Answer an int whose bits are set according the access constants
660  * defined by the VM spec.
661  * Set the AccDeprecated and AccSynthetic bits if necessary
662  * @return int
663  */
664 public int getModifiers() {
665         if (this.innerInfo != null) {
666                 return this.innerInfo.getModifiers() | (this.accessFlags & AccDeprecated);
667         }
668         return this.accessFlags;
669 }
670 /**
671  * Answer the resolved name of the type in the
672  * class file format as specified in section 4.2 of the Java 2 VM spec.
673  *
674  * For example, java.lang.String is java/lang/String.
675  * @return char[]
676  */
677 public char[] getName() {
678         return this.className;
679 }
680 /**
681  * Answer the resolved name of the receiver's superclass in the
682  * class file format as specified in section 4.2 of the Java 2 VM spec
683  * or null if it does not have one.
684  *
685  * For example, java.lang.String is java/lang/String.
686  * @return char[]
687  */
688 public char[] getSuperclassName() {
689         return this.superclassName;
690 }
691 public long getTagBits() {
692         return this.tagBits;
693 }
694 /**
695  * Answer the major/minor version defined in this class file according to the VM spec.
696  * as a long: (major<<16)+minor
697  * @return the major/minor version found
698  */
699 public long getVersion() {
700         return this.version;
701 }
702 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
703         int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
704         int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
705         int index1 = 0;
706         int index2 = 0;
707
708         end : while (index1 < length1 && index2 < length2) {
709                 while (currentFieldInfos[index1].isSynthetic()) {
710                         if (++index1 >= length1) break end;
711                 }
712                 while (otherFieldInfos[index2].isSynthetic()) {
713                         if (++index2 >= length2) break end;
714                 }
715                 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
716                         return true;
717         }
718
719         while (index1 < length1) {
720                 if (!currentFieldInfos[index1++].isSynthetic()) return true;
721         }
722         while (index2 < length2) {
723                 if (!otherFieldInfos[index2++].isSynthetic()) return true;
724         }
725         return false;
726 }
727 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
728         int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
729         int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
730         int index1 = 0;
731         int index2 = 0;
732
733         MethodInfo m;
734         end : while (index1 < length1 && index2 < length2) {
735                 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
736                         if (++index1 >= length1) break end;
737                 }
738                 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
739                         if (++index2 >= length2) break end;
740                 }
741                 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
742                         return true;
743         }
744
745         while (index1 < length1) {
746                 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
747         }
748         while (index2 < length2) {
749                 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
750         }
751         return false;
752 }
753 /**
754  * Check if the receiver has structural changes compare to the byte array in argument.
755  * Structural changes are:
756  * - modifiers changes for the class, the this.fields or the this.methods
757  * - signature changes for this.fields or this.methods.
758  * - changes in the number of this.fields or this.methods
759  * - changes for field constants
760  * - changes for thrown exceptions
761  * - change for the super class or any super interfaces.
762  * - changes for member types name or modifiers
763  * If any of these changes occurs, the method returns true. false otherwise. 
764  * The synthetic fields are included and the members are not required to be sorted.
765  * @param newBytes the bytes of the .class file we want to compare the receiver to
766  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
767  */
768 public boolean hasStructuralChanges(byte[] newBytes) {
769         return hasStructuralChanges(newBytes, true, true);
770 }
771 /**
772  * Check if the receiver has structural changes compare to the byte array in argument.
773  * Structural changes are:
774  * - modifiers changes for the class, the this.fields or the this.methods
775  * - signature changes for this.fields or this.methods.
776  * - changes in the number of this.fields or this.methods
777  * - changes for field constants
778  * - changes for thrown exceptions
779  * - change for the super class or any super interfaces.
780  * - changes for member types name or modifiers
781  * If any of these changes occurs, the method returns true. false otherwise.
782  * @param newBytes the bytes of the .class file we want to compare the receiver to
783  * @param orderRequired a boolean indicating whether the members should be sorted or not
784  * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
785  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
786  */
787 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
788         try {
789                 ClassFileReader newClassFile =
790                         new ClassFileReader(newBytes, this.classFileName);
791                 // type level comparison
792                 // modifiers
793                 if (this.getModifiers() != newClassFile.getModifiers())
794                         return true;
795
796                 // meta-annotations
797                 if ((this.getTagBits() & TagBits.AnnotationTargetMASK|TagBits.AnnotationDeprecated|TagBits.AnnotationRetentionMASK) != (newClassFile.getTagBits() & TagBits.AnnotationTargetMASK|TagBits.AnnotationDeprecated|TagBits.AnnotationRetentionMASK))
798                         return true;
799                 
800                 // superclass
801                 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
802                         return true;
803                 // interfaces
804                 char[][] newInterfacesNames = newClassFile.getInterfaceNames();
805                 if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
806                         int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
807                         if (newInterfacesLength != this.interfacesCount)
808                                 return true;
809                         for (int i = 0, max = this.interfacesCount; i < max; i++)
810                                 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
811                                         return true;
812                 }
813
814                 // member types
815                 IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
816                 IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
817                 if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
818                         int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
819                         int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
820                         if (currentMemberTypeLength != otherMemberTypeLength)
821                                 return true;
822                         for (int i = 0; i < currentMemberTypeLength; i++)
823                                 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
824                                         || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
825                                                 return true;
826                 }
827
828                 // fields
829                 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
830                 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
831                 boolean compareFields = true;
832                 if (this.fieldsCount == otherFieldInfosLength) {
833                         int i = 0;
834                         for (; i < this.fieldsCount; i++)
835                                 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
836                         if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
837                                 return true;
838                 }
839                 if (compareFields) {
840                         if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
841                                 return true;
842                         if (orderRequired) {
843                                 if (this.fieldsCount != 0)
844                                         Arrays.sort(this.fields);
845                                 if (otherFieldInfosLength != 0)
846                                         Arrays.sort(otherFieldInfos);
847                         }
848                         if (excludesSynthetic) {
849                                 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
850                                         return true;
851                         } else {
852                                 for (int i = 0; i < this.fieldsCount; i++)
853                                         if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
854                                                 return true;
855                         }
856                 }
857                 
858                 // methods
859                 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
860                 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
861                 boolean compareMethods = true;
862                 if (this.methodsCount == otherMethodInfosLength) {
863                         int i = 0;
864                         for (; i < this.methodsCount; i++)
865                                 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
866                         if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
867                                 return true;
868                 }
869                 if (compareMethods) {
870                         if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
871                                 return true;
872                         if (orderRequired) {
873                                 if (this.methodsCount != 0)
874                                         Arrays.sort(this.methods);
875                                 if (otherMethodInfosLength != 0)
876                                         Arrays.sort(otherMethodInfos);  
877                         }
878                         if (excludesSynthetic) {
879                                 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
880                                         return true;
881                         } else {
882                                 for (int i = 0; i < this.methodsCount; i++)
883                                         if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
884                                                 return true;
885                         }
886                 }
887
888                 return false;
889         } catch (ClassFormatException e) {
890                 return true;
891         }
892 }
893 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
894         if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
895                 return true;
896         if ((currentFieldInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherFieldInfo.getTagBits() & TagBits.AnnotationDeprecated))
897                 return true;
898         if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
899                 return true;
900         if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
901                 return true;
902         if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
903                 return true;
904         if (currentFieldInfo.hasConstant()) {
905                 Constant currentConstant = currentFieldInfo.getConstant();
906                 Constant otherConstant = otherFieldInfo.getConstant();
907                 if (currentConstant.typeID() != otherConstant.typeID())
908                         return true;
909                 if (!currentConstant.getClass().equals(otherConstant.getClass()))
910                         return true;
911                 switch (currentConstant.typeID()) {
912                         case TypeIds.T_int :
913                                 return currentConstant.intValue() != otherConstant.intValue();
914                         case TypeIds.T_byte :
915                                 return currentConstant.byteValue() != otherConstant.byteValue();
916                         case TypeIds.T_short :
917                                 return currentConstant.shortValue() != otherConstant.shortValue();
918                         case TypeIds.T_char :
919                                 return currentConstant.charValue() != otherConstant.charValue();
920                         case TypeIds.T_long :
921                                 return currentConstant.longValue() != otherConstant.longValue();
922                         case TypeIds.T_float :
923                                 return currentConstant.floatValue() != otherConstant.floatValue();
924                         case TypeIds.T_double :
925                                 return currentConstant.doubleValue() != otherConstant.doubleValue();
926                         case TypeIds.T_boolean :
927                                 return currentConstant.booleanValue() != otherConstant.booleanValue();
928                         case TypeIds.T_JavaLangString :
929                                 return !currentConstant.stringValue().equals(otherConstant.stringValue());
930                 }
931         }
932         return false;
933 }
934 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
935         if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
936                 return true;
937         if ((currentMethodInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherMethodInfo.getTagBits() & TagBits.AnnotationDeprecated))
938                 return true;
939         if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
940                 return true;
941         if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
942                 return true;
943         if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
944                 return true;
945
946         char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
947         char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
948         if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
949                 int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
950                 int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
951                 if (currentThrownExceptionsLength != otherThrownExceptionsLength)
952                         return true;
953                 for (int k = 0; k < currentThrownExceptionsLength; k++)
954                         if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
955                                 return true;
956         }
957         return false;
958 }
959 /**
960  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
961  * will be therefore fully initialized and we can get rid of the bytes.
962  */
963 private void initialize() throws ClassFormatException {
964         try {
965                 for (int i = 0, max = fieldsCount; i < max; i++) {
966                         fields[i].initialize();
967                 }
968                 for (int i = 0, max = methodsCount; i < max; i++) {
969                         methods[i].initialize();
970                 }
971                 if (innerInfos != null) {
972                         for (int i = 0, max = innerInfos.length; i < max; i++) {
973                                 innerInfos[i].initialize();
974                         }
975                 }
976                 this.reset();
977         } catch(RuntimeException e) {
978                 ClassFormatException exception = new ClassFormatException(e, this.classFileName);
979                 throw exception;
980         }
981 }
982 /**
983  * Answer true if the receiver is an anonymous type, false otherwise
984  *
985  * @return <CODE>boolean</CODE>
986  */
987 public boolean isAnonymous() {
988         if (this.innerInfo == null) return false;
989         char[] sourceName = this.innerInfo.getSourceName();
990         return (sourceName == null || sourceName.length == 0);
991 }
992 /**
993  * Answer whether the receiver contains the resolved binary form
994  * or the unresolved source form of the type.
995  * @return boolean
996  */
997 public boolean isBinaryType() {
998         return true;
999 }
1000
1001 /**
1002  * Answer true if the receiver is a local type, false otherwise
1003  *
1004  * @return <CODE>boolean</CODE>
1005  */
1006 public boolean isLocal() {
1007         if (this.innerInfo == null) return false;
1008         if (this.innerInfo.getEnclosingTypeName() != null) return false;
1009         char[] sourceName = this.innerInfo.getSourceName();
1010         return (sourceName != null && sourceName.length > 0);   
1011 }
1012 /**
1013  * Answer true if the receiver is a member type, false otherwise
1014  *
1015  * @return <CODE>boolean</CODE>
1016  */
1017 public boolean isMember() {
1018         if (this.innerInfo == null) return false;
1019         if (this.innerInfo.getEnclosingTypeName() == null) return false;
1020         char[] sourceName = this.innerInfo.getSourceName();
1021         return (sourceName != null && sourceName.length > 0);    // protection against ill-formed attributes (67600)
1022 }
1023 /**
1024  * Answer true if the receiver is a nested type, false otherwise
1025  *
1026  * @return <CODE>boolean</CODE>
1027  */
1028 public boolean isNestedType() {
1029         return this.innerInfo != null;
1030 }
1031 protected void reset() {
1032         this.constantPoolOffsets = null;
1033         super.reset();
1034 }
1035 /**
1036  * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
1037  * 
1038  * @return char[]
1039  */
1040 public char[] sourceFileName() {
1041         return this.sourceFileName;
1042 }
1043 public String toString() {
1044         java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
1045         java.io.PrintWriter print = new java.io.PrintWriter(out);
1046         print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
1047         print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
1048         print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
1049         print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
1050         print.flush();
1051         return out.toString();
1052 }
1053 }