View Javadoc

1   package serp.bytecode;
2   
3   import java.io.*;
4   import java.net.*;
5   import java.util.*;
6   
7   import serp.bytecode.lowlevel.*;
8   import serp.bytecode.visitor.*;
9   import serp.util.*;
10  
11  /**
12   * The BCClass represents a class object in the bytecode framework, in many
13   * ways mirroring the {@link Class} class of Java reflection. The represented
14   * class might be a primitive, array, existing object type, or some new user-
15   * defined type. As with most entities in the bytecode framework, the BCClass
16   * contains methods to manipulate the low-level state of the class (constant
17   * pool indexes, etc), but these can and should be ignored in
18   * favor of the available high-level methods.
19   *
20   * <p>A BCClass instance is loaded from a {@link Project} and remains
21   * attached to that project for its lifetime. If a BCClass is removed from
22   * its project, the result of any further operations on the class are
23   * undefined.</p>
24   *
25   * <p>Note that if a BCClass represents a primitive or array type, all of the
26   * available mutator methods and any methods that access the constant pool
27   * will throw {@link UnsupportedOperationException}s.</p>
28   *
29   * @author Abe White
30   */
31  public class BCClass extends Annotated implements VisitAcceptor {
32      private Project _project = null;
33      private State _state = null;
34      private ClassLoader _loader = null;
35  
36      /**
37       * Hide constructor. For use by the owning project only.
38       */
39      BCClass(Project project) {
40          _project = project;
41      }
42  
43      /**
44       * Set the class state. For use by the owning project only.
45       */
46      void setState(State state) {
47          _state = state;
48      }
49  
50      /**
51       * Invalidate this class.
52       */
53      void invalidate() {
54          _project = null;
55          _state = State.INVALID;
56      }
57  
58      //////////////////
59      // I/O operations
60      //////////////////
61  
62      /**
63       * Initialize from the class definition in the given file. For use by
64       * the owning project only.
65       */
66      void read(File classFile, ClassLoader loader) throws IOException {
67          InputStream in = new FileInputStream(classFile);
68          try {
69              read(in, loader);
70          } finally {
71              in.close();
72          }
73      }
74  
75      /**
76       * Initialize from the class definition in the given stream. For use by
77       * the owning project only.
78       */
79      void read(InputStream instream, ClassLoader loader)
80          throws IOException {
81          DataInput in = new DataInputStream(instream);
82  
83          // header information
84          _state.setMagic(in.readInt());
85          _state.setMinorVersion(in.readUnsignedShort());
86          _state.setMajorVersion(in.readUnsignedShort());
87  
88          // constant pool
89          _state.getPool().read(in);
90  
91          // access flags
92          _state.setAccessFlags(in.readUnsignedShort());
93  
94          // class, super class, interfaces
95          _state.setIndex(in.readUnsignedShort());
96          _state.setSuperclassIndex(in.readUnsignedShort());
97  
98          Collection interfaces = _state.getInterfacesHolder();
99          interfaces.clear();
100         int interfaceCount = in.readUnsignedShort();
101         for (int i = 0; i < interfaceCount; i++)
102             interfaces.add(Numbers.valueOf(in.readUnsignedShort()));
103 
104         // fields
105         Collection fields = _state.getFieldsHolder();
106         fields.clear();
107         int fieldCount = in.readUnsignedShort();
108         BCField field;
109         for (int i = 0; i < fieldCount; i++) {
110             field = new BCField(this);
111             fields.add(field);
112             field.read(in);
113         }
114 
115         // methods
116         Collection methods = _state.getMethodsHolder();
117         methods.clear();
118         int methodCount = in.readUnsignedShort();
119         BCMethod method;
120         for (int i = 0; i < methodCount; i++) {
121             method = new BCMethod(this);
122             methods.add(method);
123             method.read(in);
124         }
125 
126         readAttributes(in);
127         _loader = loader;
128     }
129 
130     /**
131      * Initialize from the bytecode of the definition of the given class.
132      * For use by the owning project only.
133      */
134     void read(Class type) throws IOException {
135         // find out the length of the package name
136         int dotIndex = type.getName().lastIndexOf('.') + 1;
137 
138         // strip the package off of the class name
139         String className = type.getName().substring(dotIndex);
140 
141         // attempt to get the class file for the class as a stream
142         InputStream in = type.getResourceAsStream(className + ".class");
143         try {
144             read(in, type.getClassLoader());
145         } finally {
146             in.close();
147         }
148     }
149 
150     /**
151      * Initialize from the given parsed bytecode.
152      * For use by the owning project only.
153      */
154     void read(BCClass orig) {
155         try {
156             ByteArrayInputStream in = new ByteArrayInputStream
157                 (orig.toByteArray());
158             read(in, orig.getClassLoader());
159             in.close();
160         } catch (IOException ioe) {
161             throw new RuntimeException(ioe.toString());
162         }
163     }
164 
165     /**
166      * Write the class bytecode to the .class file in the proper directory of
167      * the CLASSPATH. The file must exist already, so this method only works
168      * on existing classes.
169      */
170     public void write() throws IOException {
171         String name = getName();
172         int dotIndex = name.lastIndexOf('.') + 1;
173         name = name.substring(dotIndex);
174         Class type = getType();
175 
176         // attempt to get the class file for the class as a stream;
177         // we need to use the url decoder in case the target directory
178         // has spaces in it
179         OutputStream out = new FileOutputStream(URLDecoder.decode
180             (type.getResource(name + ".class").getFile()));
181         try {
182             write(out);
183         } finally {
184             out.close();
185         }
186     }
187 
188     /**
189      * Write the class bytecode to the specified file.
190      */
191     public void write(File classFile) throws IOException {
192         OutputStream out = new FileOutputStream(classFile);
193         try {
194             write(out);
195         } finally {
196             out.close();
197         }
198     }
199 
200     /**
201      * Write the class bytecode to the specified stream.
202      */
203     public void write(OutputStream outstream) throws IOException {
204         DataOutput out = new DataOutputStream(outstream);
205 
206         // header information
207         out.writeInt(_state.getMagic());
208         out.writeShort(_state.getMinorVersion());
209         out.writeShort(_state.getMajorVersion());
210 
211         // constant pool
212         _state.getPool().write(out);
213 
214         // access flags
215         out.writeShort(_state.getAccessFlags());
216 
217         // class, super class
218         out.writeShort(_state.getIndex());
219         out.writeShort(_state.getSuperclassIndex());
220 
221         // interfaces
222         Collection interfaces = _state.getInterfacesHolder();
223         out.writeShort(interfaces.size());
224         for (Iterator itr = interfaces.iterator(); itr.hasNext();)
225             out.writeShort(((Number) itr.next()).intValue());
226 
227         // fields
228         Collection fields = _state.getFieldsHolder();
229         out.writeShort(fields.size());
230         for (Iterator itr = fields.iterator(); itr.hasNext();)
231             ((BCField) itr.next()).write(out);
232 
233         // methods
234         Collection methods = _state.getMethodsHolder();
235         out.writeShort(methods.size());
236         for (Iterator itr = methods.iterator(); itr.hasNext();)
237             ((BCMethod) itr.next()).write(out);
238 
239         // attributes
240         writeAttributes(out);
241     }
242 
243     /**
244      * Return the bytecode of this class as a byte array, possibly for use
245      * in a custom {@link ClassLoader}.
246      */
247     public byte[] toByteArray() {
248         ByteArrayOutputStream out = new ByteArrayOutputStream();
249         try {
250             write(out);
251             out.flush();
252             return out.toByteArray();
253         } catch (IOException ioe) {
254             throw new RuntimeException(ioe.toString());
255         } finally {
256             try { out.close(); } catch (IOException ioe) {}
257         }
258     }
259 
260     /////////////////////
261     // Access operations
262     /////////////////////
263 
264     /**
265      * Return the magic number for this class; if this is a valid type, this
266      * should be equal to {@link Constants#VALID_MAGIC} (the default value).
267      */
268     public int getMagic() {
269         return _state.getMagic();
270     }
271 
272     /**
273      * Set the magic number for this class; if this is a valid type, this
274      * should be equal to {@link Constants#VALID_MAGIC} (the default value).
275      */
276     public void setMagic(int magic) {
277         _state.setMagic(magic);
278     }
279 
280     /**
281      * Return the major version of the bytecode spec used for this class.
282      * JVMs are only required to operate with versions that they understand;
283      * leaving the default value of {@link Constants#MAJOR_VERSION} is safe.
284      */
285     public int getMajorVersion() {
286         return _state.getMajorVersion();
287     }
288 
289     /**
290      * Set the major version of the bytecode spec used for this class.
291      * JVMs are only required to operate with versions that they understand;
292      * leaving the default value of {@link Constants#MAJOR_VERSION} is safe.
293      */
294     public void setMajorVersion(int majorVersion) {
295         _state.setMajorVersion(majorVersion);
296     }
297 
298     /**
299      * Get the minor version of the bytecode spec used for this class.
300      * JVMs are only required to operate with versions that they understand;
301      * leaving the default value of {@link Constants#MINOR_VERSION} is safe.
302      */
303     public int getMinorVersion() {
304         return _state.getMinorVersion();
305     }
306 
307     /**
308      * Set the minor version of the bytecode spec used for this class.
309      * JVMs are only required to operate with versions that they understand;
310      * leaving the default value of {@link Constants#MINOR_VERSION} is safe.
311      */
312     public void setMinorVersion(int minorVersion) {
313         _state.setMinorVersion(minorVersion);
314     }
315 
316     /**
317      * Return the access flags for this class as a bit array of
318      * ACCESS_XXX constants from {@link Constants}. This can be used to
319      * transfer access flags between classes without getting/setting each
320      * possible flag.
321      */
322     public int getAccessFlags() {
323         return _state.getAccessFlags();
324     }
325 
326     /**
327      * Set the access flags for this class as a bit array of
328      * ACCESS_XXX constants from {@link Constants}. This can be used to
329      * transfer access flags between classes without getting/setting each
330      * possible flag.
331      */
332     public void setAccessFlags(int access) {
333         _state.setAccessFlags(access);
334     }
335 
336     /**
337      * Manipulate the class access flags.
338      */
339     public boolean isPublic() {
340         return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0;
341     }
342 
343     /**
344      * Manipulate the class access flags.
345      */
346     public void makePublic() {
347         setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC);
348     }
349 
350     /**
351      * Manipulate the class access flags.
352      */
353     public boolean isPackage() {
354         return !isPublic();
355     }
356 
357     /**
358      * Manipulate the class access flags.
359      */
360     public void makePackage() {
361         setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC);
362     }
363 
364     /**
365      * Manipulate the class access flags.
366      */
367     public boolean isFinal() {
368         return (getAccessFlags() & Constants.ACCESS_FINAL) > 0;
369     }
370 
371     /**
372      * Manipulate the class access flags.
373      */
374     public void setFinal(boolean on) {
375         if (on) 
376             setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL);
377         else
378             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL);
379     }
380 
381     /**
382      * Manipulate the class access flags.
383      */
384     public boolean isInterface() {
385         return (getAccessFlags() & Constants.ACCESS_INTERFACE) > 0;
386     }
387 
388     /**
389      * Manipulate the class access flags.
390      */
391     public void setInterface(boolean on) {
392         if (on) {
393             setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE);
394             setAbstract(true);
395         } else
396             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE);
397     }
398 
399     /**
400      * Manipulate the class access flags.
401      */
402     public boolean isAbstract() {
403         return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0;
404     }
405 
406     /**
407      * Manipulate the class access flags.
408      */
409     public void setAbstract(boolean on) {
410         if (on)
411             setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT);
412         else
413             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT);
414     }
415 
416     /**
417      * Manipulate the class access flags.
418      */
419     public boolean isSynthetic() {
420         return (getAccessFlags() & Constants.ACCESS_SYNTHETIC) > 0;
421     }
422 
423     /**
424      * Manipulate the class access flags.
425      */
426     public void setSynthetic(boolean on) {
427         if (on)
428             setAccessFlags(getAccessFlags() | Constants.ACCESS_SYNTHETIC);
429         else
430             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_SYNTHETIC);
431     }
432 
433     /**
434      * Manipulate the class access flags.
435      */
436     public boolean isAnnotation() {
437         return (getAccessFlags() & Constants.ACCESS_ANNOTATION) > 0;
438     }
439 
440     /**
441      * Manipulate the class access flags.  Setting to true also makes this
442      * an interface.
443      */
444     public void setAnnotation(boolean on) {
445         if (on) {
446             setAccessFlags(getAccessFlags() | Constants.ACCESS_ANNOTATION);
447             setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE);
448         } else
449             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ANNOTATION);
450     }
451 
452     /**
453      * Manipulate the class access flags.
454      */
455     public boolean isEnum() {
456         return (getAccessFlags() & Constants.ACCESS_ENUM) > 0;
457     }
458 
459     /**
460      * Manipulate the class access flags.
461      */
462     public void setEnum(boolean on) {
463         if (on)
464             setAccessFlags(getAccessFlags() | Constants.ACCESS_ENUM);
465         else
466             setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ENUM);
467     }
468 
469     /**
470      * Return true if this class is a primitive type.
471      */
472     public boolean isPrimitive() {
473         return _state.isPrimitive();
474     }
475 
476     /**
477      * Return true if this class is an array type.
478      */
479     public boolean isArray() {
480         return _state.isArray();
481     }
482 
483     /////////////////////////
484     // Class name operations
485     /////////////////////////
486 
487     /**
488      * Return the {@link ConstantPool} index of the
489      * {@link ClassEntry} for this class. Returns 0 if the class does not
490      * have a constant pool (such as a primitive or array).
491      */
492     public int getIndex() {
493         return _state.getIndex();
494     }
495 
496     /**
497      * Set the {@link ConstantPool} index of the {@link ClassEntry} for this
498      * class. Unlike most other low-level methods, the index
499      * will be checked against the pool immediately;
500      * classes must have a valid name at all times.
501      */
502     public void setIndex(int index) {
503         String oldName = getName();
504         String newName = ((ClassEntry) getPool().getEntry(index)).
505             getNameEntry().getValue();
506         beforeRename(oldName, newName);
507         _state.setIndex(index);
508     }
509 
510     /**
511      * Return the name of this class, including package name. The name will
512      * be in a form suitable for a {@link Class#forName} call.
513      */
514     public String getName() {
515         return _state.getName();
516     }
517 
518     /**
519      * Return the name of the class only, without package.
520      */
521     public String getClassName() {
522         String name = _project.getNameCache().getExternalForm(getName(), true);
523         return name.substring(name.lastIndexOf('.') + 1);
524     }
525 
526     /**
527      * Return the package name only, without class, or null if none.
528      */
529     public String getPackageName() {
530         String name = _project.getNameCache().getExternalForm(getName(), true);
531         int index = name.lastIndexOf('.');
532         if (index == -1)
533             return null;
534         return name.substring(0, index);
535     }
536 
537     /**
538      * Set the name of this class, including package name.
539      */
540     public void setName(String name) {
541         name = _project.getNameCache().getExternalForm(name, false);
542         String oldName = getName();
543 
544         // get a reference to the class entry for this class
545         int index = getIndex();
546         if (index == 0)
547             index = getPool().findClassEntry(name, true);
548         ClassEntry entry = (ClassEntry) getPool().getEntry(index);
549 
550         // make sure the rename is ok with the project
551         beforeRename(oldName, name);
552 
553         // reset the name index of the class entry to the new name
554         int nameIndex = getPool().findUTF8Entry(_project.getNameCache().
555             getInternalForm(name, false), true);
556         entry.setNameIndex(nameIndex);
557 
558         // we might have just added a new entry; set the index
559         _state.setIndex(index);
560     }
561 
562     /**
563      * Return the {@link Class} object for this class, if it is loadable.
564      */
565     public Class getType() {
566         return Strings.toClass(getName(), getClassLoader());
567     }
568 
569     /**
570      * Return the component type name of this class, or null if not an array.
571      * The name will be in a form suitable for a {@link Class#forName} call.
572      */
573     public String getComponentName() {
574         return _state.getComponentName();
575     }
576 
577     /**
578      * Return the component type of this class, or null if not an array.
579      */
580     public Class getComponentType() {
581         String componentName = getComponentName();
582         if (componentName == null)
583             return null;
584         return Strings.toClass(componentName, getClassLoader());
585     }
586 
587     /**
588      * Return the component type of this class, or null if not an array.
589      */
590     public BCClass getComponentBC() {
591         String componentName = getComponentName();
592         if (componentName == null)
593             return null;
594         return getProject().loadClass(componentName, getClassLoader());
595     }
596 
597     /////////////////////////
598     // Superclass operations
599     /////////////////////////
600 
601     /**
602      * Return the {@link ConstantPool} index of the
603      * {@link ClassEntry} for the superclass of this class. Returns -1 if
604      * the class does not have a constant pool (such as a primitive or array).
605      */
606     public int getSuperclassIndex() {
607         return _state.getSuperclassIndex();
608     }
609 
610     /**
611      * Set the {@link ConstantPool} index of the
612      * {@link ClassEntry} for the superclass of this class.
613      */
614     public void setSuperclassIndex(int index) {
615         _state.setSuperclassIndex(index);
616     }
617 
618     /**
619      * Return the name of the superclass for this class, including package
620      * name. The name will be in a form suitable for a
621      * {@link Class#forName} call, or null for types without superclasses.
622      */
623     public String getSuperclassName() {
624         return _state.getSuperclassName();
625     }
626 
627     /**
628      * Return the {@link Class} object for the superclass of this class, if it
629      * is loadable. Returns null for types without superclasses.
630      */
631     public Class getSuperclassType() {
632         String name = getSuperclassName();
633         if (name == null)
634             return null;
635         return Strings.toClass(name, getClassLoader());
636     }
637 
638     /**
639      * Return the bytecode of the superclass of this class, or
640      * null for types without superclasses.
641      */
642     public BCClass getSuperclassBC() {
643         String name = getSuperclassName();
644         if (name == null)
645             return null;
646         return getProject().loadClass(name, getClassLoader());
647     }
648 
649     /**
650      * Set the superclass of this class.
651      */
652     public void setSuperclass(String name) {
653         if (name == null)
654             setSuperclassIndex(0);
655         else
656             setSuperclassIndex(getPool().findClassEntry(_project.getNameCache().
657                 getInternalForm(name, false), true));
658     }
659 
660     /**
661      * Set the superclass of this class.
662      */
663     public void setSuperclass(Class type) {
664         if (type == null)
665             setSuperclass((String) null);
666         else
667             setSuperclass(type.getName());
668     }
669 
670     /**
671      * Set the superclass of this class.
672      */
673     public void setSuperclass(BCClass type) {
674         if (type == null)
675             setSuperclass((String) null);
676         else
677             setSuperclass(type.getName());
678     }
679 
680     ////////////////////////
681     // Interface operations
682     ////////////////////////
683 
684     /**
685      * Return the list of {@link ConstantPool} indexes of the
686      * {@link ClassEntry}s describing all the interfaces this class declares
687      * that it implements/extends.
688      *
689      * @return the implmented interfaces, or an empty array if none
690      */
691     public int[] getDeclaredInterfaceIndexes() {
692         Collection interfaces = _state.getInterfacesHolder();
693         int[] indexes = new int[interfaces.size()];
694         Iterator itr = interfaces.iterator();
695         for (int i = 0, max = interfaces.size(); i < max; i++)
696             indexes[i] = ((Number) itr.next()).intValue();
697         return indexes;
698     }
699 
700     /**
701      * Set the list of {@link ConstantPool} indexes of the
702      * {@link ClassEntry}s describing all the interfaces this class declares
703      * it implements/extends; set to null or an empty array if none.
704      */
705     public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) {
706         Collection stateIndexes = _state.getInterfacesHolder();
707         stateIndexes.clear();
708         Integer idx;
709         for (int i = 0; i < interfaceIndexes.length; i++) {
710             idx = Numbers.valueOf(interfaceIndexes[i]);
711             if (!stateIndexes.contains(idx))
712                 stateIndexes.add(idx);
713         }
714     }
715 
716     /**
717      * Return the names of the interfaces declared for this class, including
718      * package names, or an empty array if none. The names will be in a form
719      * suitable for a {@link Class#forName} call.
720      */
721     public String[] getDeclaredInterfaceNames() {
722         int[] indexes = getDeclaredInterfaceIndexes();
723         String[] names = new String[indexes.length];
724         ClassEntry entry;
725         for (int i = 0; i < indexes.length; i++) {
726             entry = (ClassEntry) getPool().getEntry(indexes[i]);
727             names[i] = _project.getNameCache().getExternalForm
728                 (entry.getNameEntry().getValue(), false);
729         }
730         return names;
731     }
732 
733     /**
734      * Return the {@link Class} objects for the declared interfaces of this
735      * class, or an empty array if none.
736      */
737     public Class[] getDeclaredInterfaceTypes() {
738         String[] names = getDeclaredInterfaceNames();
739         Class[] types = new Class[names.length];
740         for (int i = 0; i < names.length; i++)
741             types[i] = Strings.toClass(names[i], getClassLoader());
742         return types;
743     }
744 
745     /**
746      * Return the bytecode for the declared interfaces of this class, or an
747      * empty array if none.
748      */
749     public BCClass[] getDeclaredInterfaceBCs() {
750         String[] names = getDeclaredInterfaceNames();
751         BCClass[] types = new BCClass[names.length];
752         for (int i = 0; i < names.length; i++)
753             types[i] = getProject().loadClass(names[i], getClassLoader());
754         return types;
755     }
756 
757     /**
758      * Set the interfaces declared implemented/extended by this class; set to
759      * null or an empty array if none.
760      */
761     public void setDeclaredInterfaces(String[] interfaces) {
762         clearDeclaredInterfaces();
763         if (interfaces != null)
764             for (int i = 0; i < interfaces.length; i++)
765                 declareInterface(interfaces[i]);
766     }
767 
768     /**
769      * Set the interfaces declared implemented/extended by this class; set to
770      * null or an empty array if none.
771      */
772     public void setDeclaredInterfaces(Class[] interfaces) {
773         String[] names = null;
774         if (interfaces != null) {
775             names = new String[interfaces.length];
776             for (int i = 0; i < interfaces.length; i++)
777                 names[i] = interfaces[i].getName();
778         }
779         setDeclaredInterfaces(names);
780     }
781 
782     /**
783      * Set the interfaces declared implemented/extended by this class; set to
784      * null or an empty array if none.
785      */
786     public void setDeclaredInterfaces(BCClass[] interfaces) {
787         String[] names = null;
788         if (interfaces != null) {
789             names = new String[interfaces.length];
790             for (int i = 0; i < interfaces.length; i++)
791                 names[i] = interfaces[i].getName();
792         }
793         setDeclaredInterfaces(names);
794     }
795 
796     /**
797      * Return the names of all unique interfaces implemented by this class,
798      * including those of all superclasses. The names will be returned in a
799      * form suitable for a {@link Class#forName} call.
800      * This method does not recurse into interfaces-of-interfaces.
801      */
802     public String[] getInterfaceNames() {
803         Collection allNames = new LinkedList();
804         String[] names;
805         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
806             names = type.getDeclaredInterfaceNames();
807             for (int i = 0; i < names.length; i++)
808                 allNames.add(names[i]);
809         }
810         return (String[]) allNames.toArray(new String[allNames.size()]);
811     }
812 
813     /**
814      * Return the {@link Class} objects of all unique interfaces implemented
815      * by this class, including those of all superclasses.
816      * This method does not recurse into interfaces-of-interfaces.
817      */
818     public Class[] getInterfaceTypes() {
819         Collection allTypes = new LinkedList();
820         Class[] types;
821         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
822             types = type.getDeclaredInterfaceTypes();
823             for (int i = 0; i < types.length; i++)
824                 allTypes.add(types[i]);
825         }
826         return (Class[]) allTypes.toArray(new Class[allTypes.size()]);
827     }
828 
829     /**
830      * Return the bytecode of all unique interfaces implemented by this class,
831      * including those of all superclasses.
832      * This method does not recurse into interfaces-of-interfaces.
833      */
834     public BCClass[] getInterfaceBCs() {
835         Collection allTypes = new LinkedList();
836         BCClass[] types;
837         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
838             types = type.getDeclaredInterfaceBCs();
839             for (int i = 0; i < types.length; i++)
840                 allTypes.add(types[i]);
841         }
842         return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]);
843     }
844 
845     /**
846      * Clear this class of all interface declarations.
847      */
848     public void clearDeclaredInterfaces() {
849         _state.getInterfacesHolder().clear();
850     }
851 
852     /**
853      * Remove an interface declared by this class.
854      *
855      * @return true if the class had the interface, false otherwise
856      */
857     public boolean removeDeclaredInterface(String name) {
858         String[] names = getDeclaredInterfaceNames();
859         Iterator itr = _state.getInterfacesHolder().iterator();
860         for (int i = 0; i < names.length; i++) {
861             itr.next();
862             if (names[i].equals(name)) {
863                 itr.remove();
864                 return true;
865             }
866         }
867         return false;
868     }
869 
870     /**
871      * Remove an interface declared by this class.
872      *
873      * @return true if the class had the interface, false otherwise
874      */
875     public boolean removeDeclaredInterface(Class type) {
876         if (type == null)
877             return false;
878         return removeDeclaredInterface(type.getName());
879     }
880 
881     /**
882      * Remove an interface declared by this class.
883      *
884      * @return true if the class had the interface, false otherwise
885      */
886     public boolean removeDeclaredInterface(BCClass type) {
887         if (type == null)
888             return false;
889         return removeDeclaredInterface(type.getName());
890     }
891 
892     /**
893      * Add an interface to those declared by this class.
894      */
895     public void declareInterface(String name) {
896         Integer index = Numbers.valueOf(getPool().findClassEntry(_project.
897             getNameCache().getInternalForm(name, false), true));
898         Collection interfaces = _state.getInterfacesHolder();
899         if (!interfaces.contains(index))
900             interfaces.add(index);
901     }
902 
903     /**
904      * Add an interface to those declared by this class.
905      */
906     public void declareInterface(Class type) {
907         declareInterface(type.getName());
908     }
909 
910     /**
911      * Add an interface to those declared by this class.
912      */
913     public void declareInterface(BCClass type) {
914         declareInterface(type.getName());
915     }
916 
917     /**
918      * Return true if this class or any of its superclasses implement/extend
919      * the given interface/class.
920      * This method does not recurse into interfaces-of-interfaces.
921      */
922     public boolean isInstanceOf(String name) {
923         name = _project.getNameCache().getExternalForm(name, false);
924         String[] interfaces = getInterfaceNames();
925         for (int i = 0; i < interfaces.length; i++)
926             if (interfaces[i].equals(name))
927                 return true;
928         for (BCClass type = this; type != null; type = type.getSuperclassBC())
929             if (type.getName().equals(name))
930                 return true;
931         return false;
932     }
933 
934     /**
935      * Return true if this class or any of its superclasses implement/extend
936      * the given interface/class.
937      * This method does not recurse into interfaces-of-interfaces.
938      */
939     public boolean isInstanceOf(Class type) {
940         if (type == null)
941             return false;
942         return isInstanceOf(type.getName());
943     }
944 
945     /**
946      * Return true if this class or any of its superclasses implement/extend
947      * the given interface/class.
948      * This method does not recurse into interfaces-of-interfaces.
949      */
950     public boolean isInstanceOf(BCClass type) {
951         if (type == null)
952             return false;
953         return isInstanceOf(type.getName());
954     }
955 
956     //////////////////////
957     // Field operations
958     //////////////////////
959 
960     /**
961      * Return all the declared fields of this class, or an empty array if none.
962      */
963     public BCField[] getDeclaredFields() {
964         Collection fields = _state.getFieldsHolder();
965         return (BCField[]) fields.toArray(new BCField[fields.size()]);
966     }
967 
968     /**
969      * Return the declared field with the given name, or null if none.
970      */
971     public BCField getDeclaredField(String name) {
972         BCField[] fields = getDeclaredFields();
973         for (int i = 0; i < fields.length; i++)
974             if (fields[i].getName().equals(name))
975                 return fields[i];
976         return null;
977     }
978 
979     /**
980      * Return all the fields of this class, including those of all
981      * superclasses, or an empty array if none.
982      */
983     public BCField[] getFields() {
984         Collection allFields = new LinkedList();
985         BCField[] fields;
986         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
987             fields = type.getDeclaredFields();
988             for (int i = 0; i < fields.length; i++)
989                 allFields.add(fields[i]);
990         }
991         return (BCField[]) allFields.toArray(new BCField[allFields.size()]);
992     }
993 
994     /**
995      * Return all fields with the given name, including those of all
996      * superclasses, or an empty array if none.
997      */
998     public BCField[] getFields(String name) {
999         List matches = new LinkedList();
1000         BCField[] fields = getFields();
1001         for (int i = 0; i < fields.length; i++)
1002             if (fields[i].getName().equals(name))
1003                 matches.add(fields[i]);
1004         return (BCField[]) matches.toArray(new BCField[matches.size()]);
1005     }
1006 
1007     /**
1008      * Set the fields for this class; this method is useful for importing all
1009      * fields from another class. Set to null or empty array if none.
1010      */
1011     public void setDeclaredFields(BCField[] fields) {
1012         clearDeclaredFields();
1013         if (fields != null)
1014             for (int i = 0; i < fields.length; i++)
1015                 declareField(fields[i]);
1016     }
1017 
1018     /**
1019      * Import the information from given field as a new field in this class.
1020      *
1021      * @return the added field
1022      */
1023     public BCField declareField(BCField field) {
1024         BCField newField = declareField(field.getName(), field.getTypeName());
1025         newField.setAccessFlags(field.getAccessFlags());
1026         newField.setAttributes(field.getAttributes());
1027         return newField;
1028     }
1029 
1030     /**
1031      * Add a field to this class.
1032      *
1033      * @return the added field
1034      */
1035     public BCField declareField(String name, String type) {
1036         BCField field = new BCField(this);
1037         _state.getFieldsHolder().add(field);
1038         field.initialize(name, _project.getNameCache().getInternalForm(type, 
1039             true));
1040         return field;
1041     }
1042 
1043     /**
1044      * Add a field to this class.
1045      *
1046      * @return the added field
1047      */
1048     public BCField declareField(String name, Class type) {
1049         String typeName = (type == null) ? null : type.getName();
1050         return declareField(name, typeName);
1051     }
1052 
1053     /**
1054      * Add a field to this class.
1055      *
1056      * @return the added field
1057      */
1058     public BCField declareField(String name, BCClass type) {
1059         String typeName = (type == null) ? null : type.getName();
1060         return declareField(name, typeName);
1061     }
1062 
1063     /**
1064      * Clear all fields from this class.
1065      */
1066     public void clearDeclaredFields() {
1067         Collection fields = _state.getFieldsHolder();
1068         BCField field;
1069         for (Iterator itr = fields.iterator(); itr.hasNext();) {
1070             field = (BCField) itr.next();
1071             itr.remove();
1072             field.invalidate();
1073         }
1074     }
1075 
1076     /**
1077      * Remove a field from this class. After this method, the removed field
1078      * will be invalid, and the result of any operations on it is undefined.
1079      *
1080      * @return true if this class contained the field, false otherwise
1081      */
1082     public boolean removeDeclaredField(String name) {
1083         Collection fields = _state.getFieldsHolder();
1084         BCField field;
1085         for (Iterator itr = fields.iterator(); itr.hasNext();) {
1086             field = (BCField) itr.next();
1087             if (field.getName().equals(name)) {
1088                 itr.remove();
1089                 field.invalidate();
1090                 return true;
1091             }
1092         }
1093         return false;
1094     }
1095 
1096     /**
1097      * Remove a field from this class. After this method, the removed field
1098      * will be invalid, and the result of any operations on it is undefined.
1099      *
1100      * @return true if this class contained the field, false otherwise
1101      */
1102     public boolean removeDeclaredField(BCField field) {
1103         if (field == null)
1104             return false;
1105         return removeDeclaredField(field.getName());
1106     }
1107 
1108     //////////////////////
1109     // Method operations
1110     //////////////////////
1111 
1112     /**
1113      * Return all methods declared by this class. Constructors and static
1114      * initializers are included.
1115      */
1116     public BCMethod[] getDeclaredMethods() {
1117         Collection methods = _state.getMethodsHolder();
1118         return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]);
1119     }
1120 
1121     /**
1122      * Return the declared method with the given name, or null if none.
1123      * If multiple methods are declared with the given name, which is returned
1124      * is undefined.
1125      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1126      * and static initializers are named <code>&lt;clinit&gt;</code>.
1127      */
1128     public BCMethod getDeclaredMethod(String name) {
1129         BCMethod[] methods = getDeclaredMethods();
1130         for (int i = 0; i < methods.length; i++)
1131             if (methods[i].getName().equals(name))
1132                 return methods[i];
1133         return null;
1134     }
1135 
1136     /**
1137      * Return all the declared methods with the given name, or an empty array
1138      * if none.
1139      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1140      * and static initializers are named <code>&lt;clinit&gt;</code>.
1141      */
1142     public BCMethod[] getDeclaredMethods(String name) {
1143         Collection matches = new LinkedList();
1144         BCMethod[] methods = getDeclaredMethods();
1145         for (int i = 0; i < methods.length; i++)
1146             if (methods[i].getName().equals(name))
1147                 matches.add(methods[i]);
1148         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1149     }
1150 
1151     /**
1152      * Return the declared method with the given name and parameter types,
1153      * or null if none.
1154      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1155      * and static initializers are named <code>&lt;clinit&gt;</code>.
1156      */
1157     public BCMethod getDeclaredMethod(String name, String[] paramTypes) {
1158         if (paramTypes == null)
1159             paramTypes = new String[0];
1160 
1161         String[] curParams;
1162         boolean match;
1163         BCMethod[] methods = getDeclaredMethods();
1164         for (int i = 0; i < methods.length; i++) {
1165             if (!methods[i].getName().equals(name))
1166                 continue;
1167             curParams = methods[i].getParamNames();
1168             if (curParams.length != paramTypes.length)
1169                 continue;
1170 
1171             match = true;
1172             for (int j = 0; j < paramTypes.length; j++) {
1173                 if (!curParams[j].equals(_project.getNameCache().
1174                     getExternalForm(paramTypes[j], false))) {
1175                     match = false;
1176                     break;
1177                 }
1178             }
1179             if (match)
1180                 return methods[i];
1181         }
1182         return null;
1183     }
1184 
1185     /**
1186      * Return the declared method with the given name and parameter types,
1187      * or null if none.
1188      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1189      * and static initializers are named <code>&lt;clinit&gt;</code>.
1190      */
1191     public BCMethod getDeclaredMethod(String name, Class[] paramTypes) {
1192         if (paramTypes == null)
1193             return getDeclaredMethod(name, (String[]) null);
1194 
1195         String[] paramNames = new String[paramTypes.length];
1196         for (int i = 0; i < paramTypes.length; i++)
1197             paramNames[i] = paramTypes[i].getName();
1198         return getDeclaredMethod(name, paramNames);
1199     }
1200 
1201     /**
1202      * Return the declared method with the given name and parameter types,
1203      * or null if none.
1204      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1205      * and static initializers are named <code>&lt;clinit&gt;</code>.
1206      */
1207     public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) {
1208         if (paramTypes == null)
1209             return getDeclaredMethod(name, (String[]) null);
1210 
1211         String[] paramNames = new String[paramTypes.length];
1212         for (int i = 0; i < paramTypes.length; i++)
1213             paramNames[i] = paramTypes[i].getName();
1214         return getDeclaredMethod(name, paramNames);
1215     }
1216 
1217     /**
1218      * Return the methods of this class, including those of all superclasses,
1219      * or an empty array if none.
1220      * The base version of methods that are overridden will be included, as
1221      * will all constructors and static initializers.
1222      * The methods will be ordered from those in the most-specific type up to
1223      * those in {@link Object}.
1224      */
1225     public BCMethod[] getMethods() {
1226         Collection allMethods = new LinkedList();
1227         BCMethod[] methods;
1228         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
1229             methods = type.getDeclaredMethods();
1230             for (int i = 0; i < methods.length; i++)
1231                 allMethods.add(methods[i]);
1232         }
1233         return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]);
1234     }
1235 
1236     /**
1237      * Return the methods with the given name, including those of all
1238      * superclasses, or an empty array if none.
1239      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1240      * and static initializers are named <code>&lt;clinit&gt;</code>.
1241      */
1242     public BCMethod[] getMethods(String name) {
1243         Collection matches = new LinkedList();
1244         BCMethod[] methods = getMethods();
1245         for (int i = 0; i < methods.length; i++)
1246             if (methods[i].getName().equals(name))
1247                 matches.add(methods[i]);
1248         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1249     }
1250 
1251     /**
1252      * Return the methods with the given name and parameter types, including
1253      * those of all superclasses, or an empty array if none.
1254      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1255      * and static initializers are named <code>&lt;clinit&gt;</code>.
1256      */
1257     public BCMethod[] getMethods(String name, String[] paramTypes) {
1258         if (paramTypes == null)
1259             paramTypes = new String[0];
1260 
1261         String[] curParams;
1262         boolean match;
1263         BCMethod[] methods = getMethods();
1264         Collection matches = new LinkedList();
1265         for (int i = 0; i < methods.length; i++) {
1266             if (!methods[i].getName().equals(name))
1267                 continue;
1268             curParams = methods[i].getParamNames();
1269             if (curParams.length != paramTypes.length)
1270                 continue;
1271 
1272             match = true;
1273             for (int j = 0; j < paramTypes.length; j++) {
1274                 if (!curParams[j].equals(_project.getNameCache().
1275                     getExternalForm(paramTypes[j], false))) {
1276                     match = false;
1277                     break;
1278                 }
1279             }
1280             if (match)
1281                 matches.add(methods[i]);
1282         }
1283         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1284     }
1285 
1286     /**
1287      * Return the methods with the given name and parameter types, including
1288      * those of all superclasses, or an empty array if none.
1289      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1290      * and static initializers are named <code>&lt;clinit&gt;</code>.
1291      */
1292     public BCMethod[] getMethods(String name, Class[] paramTypes) {
1293         if (paramTypes == null)
1294             return getMethods(name, (String[]) null);
1295 
1296         String[] paramNames = new String[paramTypes.length];
1297         for (int i = 0; i < paramTypes.length; i++)
1298             paramNames[i] = paramTypes[i].getName();
1299         return getMethods(name, paramNames);
1300     }
1301 
1302     /**
1303      * Return the methods with the given name and parameter types, including
1304      * those of all superclasses, or an empty array if none.
1305      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1306      * and static initializers are named <code>&lt;clinit&gt;</code>.
1307      */
1308     public BCMethod[] getMethods(String name, BCClass[] paramTypes) {
1309         if (paramTypes == null)
1310             return getMethods(name, (String[]) null);
1311 
1312         String[] paramNames = new String[paramTypes.length];
1313         for (int i = 0; i < paramTypes.length; i++)
1314             paramNames[i] = paramTypes[i].getName();
1315         return getMethods(name, paramNames);
1316     }
1317 
1318     /**
1319      * Set the methods for this class; this method is useful for importing all
1320      * methods from another class. Set to null or empty array if none.
1321      */
1322     public void setDeclaredMethods(BCMethod[] methods) {
1323         clearDeclaredMethods();
1324         if (methods != null)
1325             for (int i = 0; i < methods.length; i++)
1326                 declareMethod(methods[i]);
1327     }
1328 
1329     /**
1330      * Import the information in the given method as a new method of this class.
1331      *
1332      * @return the added method
1333      */
1334     public BCMethod declareMethod(BCMethod method) {
1335         BCMethod newMethod = declareMethod(method.getName(), 
1336             method.getReturnName(), method.getParamNames());
1337         newMethod.setAccessFlags(method.getAccessFlags());
1338         newMethod.setAttributes(method.getAttributes());
1339         return newMethod;
1340     }
1341 
1342     /**
1343      * Add a method to this class.
1344      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1345      * and static initializers are named <code>&lt;clinit&gt;</code>.
1346      *
1347      * @return the added method
1348      */
1349     public BCMethod declareMethod(String name, String returnType,
1350         String[] paramTypes) {
1351         BCMethod method = new BCMethod(this);
1352         _state.getMethodsHolder().add(method);
1353         method.initialize(name, _project.getNameCache().
1354             getDescriptor(returnType, paramTypes));
1355         return method;
1356     }
1357 
1358     /**
1359      * Add a method to this class.
1360      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1361      * and static initializers are named <code>&lt;clinit&gt;</code>.
1362      *
1363      * @return the added method
1364      */
1365     public BCMethod declareMethod(String name, Class returnType,
1366         Class[] paramTypes) {
1367         String[] paramNames = null;
1368         if (paramTypes != null) {
1369             paramNames = new String[paramTypes.length];
1370             for (int i = 0; i < paramTypes.length; i++)
1371                 paramNames[i] = paramTypes[i].getName();
1372         }
1373         String returnName = (returnType == null) ? null : returnType.getName();
1374         return declareMethod(name, returnName, paramNames);
1375     }
1376 
1377     /**
1378      * Add a method to this class.
1379      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1380      * and static initializers are named <code>&lt;clinit&gt;</code>.
1381      *
1382      * @return the added method
1383      */
1384     public BCMethod declareMethod(String name, BCClass returnType,
1385         BCClass[] paramTypes) {
1386         String[] paramNames = null;
1387         if (paramTypes != null) {
1388             paramNames = new String[paramTypes.length];
1389             for (int i = 0; i < paramTypes.length; i++)
1390                 paramNames[i] = paramTypes[i].getName();
1391         }
1392         String returnName = (returnType == null) ? null : returnType.getName();
1393         return declareMethod(name, returnName, paramNames);
1394     }
1395 
1396     /**
1397      * Clear all declared methods from this class.
1398      */
1399     public void clearDeclaredMethods() {
1400         Collection methods = _state.getMethodsHolder();
1401         BCMethod method;
1402         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1403             method = (BCMethod) itr.next();
1404             itr.remove();
1405             method.invalidate();
1406         }
1407     }
1408 
1409     /**
1410      * Remove a method from this class. After this method, the removed method
1411      * will be invalid, and the result of any operations on it is undefined.
1412      * If multiple methods match the given name, which is removed is undefined.
1413      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1414      * and static initializers are named <code>&lt;clinit&gt;</code>.
1415      *
1416      * @return true if this class contained the method, false otherwise
1417      */
1418     public boolean removeDeclaredMethod(String name) {
1419         Collection methods = _state.getMethodsHolder();
1420         BCMethod method;
1421         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1422             method = (BCMethod) itr.next();
1423             if (method.getName().equals(name)) {
1424                 itr.remove();
1425                 method.invalidate();
1426                 return true;
1427             }
1428         }
1429         return false;
1430     }
1431 
1432     /**
1433      * Removes a method from this class. After this method, the removed method
1434      * will be invalid, and the result of any operations on it is undefined.
1435      *
1436      * @return true if this class contained the method, false otherwise
1437      */
1438     public boolean removeDeclaredMethod(BCMethod method) {
1439         if (method == null)
1440             return false;
1441         return removeDeclaredMethod(method.getName(), method.getParamNames());
1442     }
1443 
1444     /**
1445      * Removes a method from this class. After this method, the removed method
1446      * will be invalid, and the result of any operations on it is undefined.
1447      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1448      * and static initializers are named <code>&lt;clinit&gt;</code>.
1449      *
1450      * @return true if this class contained the method, false otherwise
1451      */
1452     public boolean removeDeclaredMethod(String name, String[] paramTypes) {
1453         if (paramTypes == null)
1454             paramTypes = new String[0];
1455 
1456         String[] curParams;
1457         boolean match;
1458         Collection methods = _state.getMethodsHolder();
1459         BCMethod method;
1460         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1461             method = (BCMethod) itr.next();
1462             if (!method.getName().equals(name))
1463                 continue;
1464             curParams = method.getParamNames();
1465             if (curParams.length != paramTypes.length)
1466                 continue;
1467 
1468             match = true;
1469             for (int j = 0; j < paramTypes.length; j++) {
1470                 if (!curParams[j].equals(_project.getNameCache().
1471                     getExternalForm(paramTypes[j], false))) {
1472                     match = false;
1473                     break;
1474                 }
1475             }
1476             if (match) {
1477                 itr.remove();
1478                 method.invalidate();
1479                 return true;
1480             }
1481         }
1482         return false;
1483     }
1484 
1485     /**
1486      * Removes a method from this class. After this method, the removed method
1487      * will be invalid, and the result of any operations on it is undefined.
1488      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1489      * and static initializers are named <code>&lt;clinit&gt;</code>.
1490      *
1491      * @return true if this class contained the method, false otherwise
1492      */
1493     public boolean removeDeclaredMethod(String name, Class[] paramTypes) {
1494         if (paramTypes == null)
1495             return removeDeclaredMethod(name, (String[]) null);
1496 
1497         String[] paramNames = new String[paramTypes.length];
1498         for (int i = 0; i < paramTypes.length; i++)
1499             paramNames[i] = paramTypes[i].getName();
1500         return removeDeclaredMethod(name, paramNames);
1501     }
1502 
1503     /**
1504      * Removes a method from this class. After this method, the removed method
1505      * will be invalid, and the result of any operations on it is undefined.
1506      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1507      * and static initializers are named <code>&lt;clinit&gt;</code>.
1508      *
1509      * @return true if this class contained the method, false otherwise
1510      */
1511     public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) {
1512         if (paramTypes == null)
1513             return removeDeclaredMethod(name, (String[]) null);
1514 
1515         String[] paramNames = new String[paramTypes.length];
1516         for (int i = 0; i < paramTypes.length; i++)
1517             paramNames[i] = paramTypes[i].getName();
1518         return removeDeclaredMethod(name, paramNames);
1519     }
1520 
1521     ///////////////////////
1522     // Convenience methods
1523     ///////////////////////
1524 
1525     /**
1526      * Convenience method to add a default constructor to this class.
1527      * If a default constructor already exists, this method will return it
1528      * without modification.
1529      * This method can only be called if the superclass has been set.
1530      *
1531      * @return the default constructor
1532      */
1533     public BCMethod addDefaultConstructor() {
1534         BCMethod method = getDeclaredMethod("<init>", (String[]) null);
1535         if (method != null)
1536             return method;
1537 
1538         method = declareMethod("<init>", void.class, null);
1539         Code code = method.getCode(true);
1540         code.setMaxStack(1);
1541         code.setMaxLocals(1);
1542 
1543         code.xload().setThis();
1544         code.invokespecial()
1545             .setMethod(getSuperclassName(), "<init>", "void", null);
1546         code.vreturn();
1547         return method;
1548     }
1549 
1550     /**
1551      * Return source file information for the class.
1552      * Acts internally through the {@link Attributes} interface.
1553      *
1554      * @param add if true, a new source file attribute will be added
1555      * if not already present
1556      * @return the source file information, or null if none and the
1557      * <code>add</code> param is set to false
1558      */
1559     public SourceFile getSourceFile(boolean add) {
1560         SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE);
1561         if (!add || (source != null))
1562             return source;
1563         return (SourceFile) addAttribute(Constants.ATTR_SOURCE);
1564     }
1565 
1566     /**
1567      * Remove the source file attribute for the class.
1568      * Acts internally through the {@link Attributes} interface.
1569      *
1570      * @return true if there was a file to remove
1571      */
1572     public boolean removeSourceFile() {
1573         return removeAttribute(Constants.ATTR_SOURCE);
1574     }
1575 
1576     /**
1577      * Return inner classes information for the class.
1578      * Acts internally through the {@link Attributes} interface.
1579      *
1580      * @param add if true, a new inner classes attribute will be added
1581      * if not already present
1582      * @return the inner classes information, or null if none and the
1583      * <code>add</code> param is set to false
1584      */
1585     public InnerClasses getInnerClasses(boolean add) {
1586         InnerClasses inner = (InnerClasses) getAttribute
1587             (Constants.ATTR_INNERCLASS);
1588         if (!add || (inner != null))
1589             return inner;
1590         return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS);
1591     }
1592 
1593     /**
1594      * Remove the inner classes attribute for the class.
1595      * Acts internally through the {@link Attributes} interface.
1596      *
1597      * @return true if there was an attribute to remove
1598      */
1599     public boolean removeInnerClasses() {
1600         return removeAttribute(Constants.ATTR_INNERCLASS);
1601     }
1602 
1603     /**
1604      * Convenience method to return deprecation information for the class.
1605      * Acts internally through the {@link Attributes} interface.
1606      */
1607     public boolean isDeprecated() {
1608         return getAttribute(Constants.ATTR_DEPRECATED) != null;
1609     }
1610 
1611     /**
1612      * Convenience method to set whether this class should be considered
1613      * deprecated. Acts internally through the {@link Attributes} interface.
1614      */
1615     public void setDeprecated(boolean on) {
1616         if (!on)
1617             removeAttribute(Constants.ATTR_DEPRECATED);
1618         else if (!isDeprecated())
1619             addAttribute(Constants.ATTR_DEPRECATED);
1620     }
1621 
1622     ///////////////////////////////////
1623     // Implementation of VisitAcceptor
1624     ///////////////////////////////////
1625 
1626     public void acceptVisit(BCVisitor visit) {
1627         visit.enterBCClass(this);
1628 
1629         ConstantPool pool = null;
1630         try {
1631             pool = _state.getPool();
1632         } catch (UnsupportedOperationException uoe) {
1633         }
1634         if (pool != null)
1635             pool.acceptVisit(visit);
1636 
1637         BCField[] fields = getDeclaredFields();
1638         for (int i = 0; i < fields.length; i++) {
1639             visit.enterBCMember(fields[i]);
1640             fields[i].acceptVisit(visit);
1641             visit.exitBCMember(fields[i]);
1642         }
1643 
1644         BCMethod[] methods = getDeclaredMethods();
1645         for (int i = 0; i < methods.length; i++) {
1646             visit.enterBCMember(methods[i]);
1647             methods[i].acceptVisit(visit);
1648             visit.exitBCMember(methods[i]);
1649         }
1650 
1651         visitAttributes(visit);
1652         visit.exitBCClass(this);
1653     }
1654 
1655     ////////////////////////////////
1656     // Implementation of Attributes
1657     ////////////////////////////////
1658 
1659     public Project getProject() {
1660         return _project;
1661     }
1662 
1663     public ConstantPool getPool() {
1664         return _state.getPool();
1665     }
1666 
1667     public ClassLoader getClassLoader() {
1668         if (_loader != null)
1669             return _loader;
1670         return Thread.currentThread().getContextClassLoader();
1671     }
1672 
1673     public boolean isValid() {
1674         return _project != null;
1675     }
1676 
1677     Collection getAttributesHolder() {
1678         return _state.getAttributesHolder();
1679     }
1680 
1681     ///////////////////////////////
1682     // Implementation of Annotated
1683     ///////////////////////////////
1684 
1685     BCClass getBCClass() {
1686         return this;
1687     }
1688 
1689     /**
1690      * Attempts to change the class name with the owning project. The project
1691      * can reject the change if a class with the given new name already
1692      * exists; therefore this method should be called before the change is
1693      * recorded in the class.
1694      */
1695     private void beforeRename(String oldName, String newName) {
1696         if ((_project != null) && (oldName != null))
1697             _project.renameClass(oldName, newName, this);
1698     }
1699 }