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          List 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         List 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         List 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         List 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         List 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         List 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         List interfaces = _state.getInterfacesHolder();
693         int[] indexes = new int[interfaces.size()];
694         for (int i = 0; i < interfaces.size(); i++)
695             indexes[i] = ((Number) interfaces.get(i)).intValue();
696         return indexes;
697     }
698 
699     /***
700      * Set the list of {@link ConstantPool} indexes of the
701      * {@link ClassEntry}s describing all the interfaces this class declares
702      * it implements/extends; set to null or an empty array if none.
703      */
704     public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) {
705         List stateIndexes = _state.getInterfacesHolder();
706         stateIndexes.clear();
707         Integer idx;
708         for (int i = 0; i < interfaceIndexes.length; i++) {
709             idx = Numbers.valueOf(interfaceIndexes[i]);
710             if (!stateIndexes.contains(idx))
711                 stateIndexes.add(idx);
712         }
713     }
714 
715     /***
716      * Return the names of the interfaces declared for this class, including
717      * package names, or an empty array if none. The names will be in a form
718      * suitable for a {@link Class#forName} call.
719      */
720     public String[] getDeclaredInterfaceNames() {
721         int[] indexes = getDeclaredInterfaceIndexes();
722         String[] names = new String[indexes.length];
723         ClassEntry entry;
724         for (int i = 0; i < indexes.length; i++) {
725             entry = (ClassEntry) getPool().getEntry(indexes[i]);
726             names[i] = _project.getNameCache().getExternalForm
727                 (entry.getNameEntry().getValue(), false);
728         }
729         return names;
730     }
731 
732     /***
733      * Return the {@link Class} objects for the declared interfaces of this
734      * class, or an empty array if none.
735      */
736     public Class[] getDeclaredInterfaceTypes() {
737         String[] names = getDeclaredInterfaceNames();
738         Class[] types = new Class[names.length];
739         for (int i = 0; i < names.length; i++)
740             types[i] = Strings.toClass(names[i], getClassLoader());
741         return types;
742     }
743 
744     /***
745      * Return the bytecode for the declared interfaces of this class, or an
746      * empty array if none.
747      */
748     public BCClass[] getDeclaredInterfaceBCs() {
749         String[] names = getDeclaredInterfaceNames();
750         BCClass[] types = new BCClass[names.length];
751         for (int i = 0; i < names.length; i++)
752             types[i] = getProject().loadClass(names[i], getClassLoader());
753         return types;
754     }
755 
756     /***
757      * Set the interfaces declared implemented/extended by this class; set to
758      * null or an empty array if none.
759      */
760     public void setDeclaredInterfaces(String[] interfaces) {
761         clearDeclaredInterfaces();
762         if (interfaces != null)
763             for (int i = 0; i < interfaces.length; i++)
764                 declareInterface(interfaces[i]);
765     }
766 
767     /***
768      * Set the interfaces declared implemented/extended by this class; set to
769      * null or an empty array if none.
770      */
771     public void setDeclaredInterfaces(Class[] interfaces) {
772         String[] names = null;
773         if (interfaces != null) {
774             names = new String[interfaces.length];
775             for (int i = 0; i < interfaces.length; i++)
776                 names[i] = interfaces[i].getName();
777         }
778         setDeclaredInterfaces(names);
779     }
780 
781     /***
782      * Set the interfaces declared implemented/extended by this class; set to
783      * null or an empty array if none.
784      */
785     public void setDeclaredInterfaces(BCClass[] interfaces) {
786         String[] names = null;
787         if (interfaces != null) {
788             names = new String[interfaces.length];
789             for (int i = 0; i < interfaces.length; i++)
790                 names[i] = interfaces[i].getName();
791         }
792         setDeclaredInterfaces(names);
793     }
794 
795     /***
796      * Return the names of all unique interfaces implemented by this class,
797      * including those of all superclasses. The names will be returned in a
798      * form suitable for a {@link Class#forName} call.
799      * This method does not recurse into interfaces-of-interfaces.
800      */
801     public String[] getInterfaceNames() {
802         Collection allNames = new LinkedList();
803         String[] names;
804         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
805             names = type.getDeclaredInterfaceNames();
806             for (int i = 0; i < names.length; i++)
807                 allNames.add(names[i]);
808         }
809         return (String[]) allNames.toArray(new String[allNames.size()]);
810     }
811 
812     /***
813      * Return the {@link Class} objects of all unique interfaces implemented
814      * by this class, including those of all superclasses.
815      * This method does not recurse into interfaces-of-interfaces.
816      */
817     public Class[] getInterfaceTypes() {
818         Collection allTypes = new LinkedList();
819         Class[] types;
820         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
821             types = type.getDeclaredInterfaceTypes();
822             for (int i = 0; i < types.length; i++)
823                 allTypes.add(types[i]);
824         }
825         return (Class[]) allTypes.toArray(new Class[allTypes.size()]);
826     }
827 
828     /***
829      * Return the bytecode of all unique interfaces implemented by this class,
830      * including those of all superclasses.
831      * This method does not recurse into interfaces-of-interfaces.
832      */
833     public BCClass[] getInterfaceBCs() {
834         Collection allTypes = new LinkedList();
835         BCClass[] types;
836         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
837             types = type.getDeclaredInterfaceBCs();
838             for (int i = 0; i < types.length; i++)
839                 allTypes.add(types[i]);
840         }
841         return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]);
842     }
843 
844     /***
845      * Clear this class of all interface declarations.
846      */
847     public void clearDeclaredInterfaces() {
848         _state.getInterfacesHolder().clear();
849     }
850 
851     /***
852      * Remove an interface declared by this class.
853      *
854      * @return true if the class had the interface, false otherwise
855      */
856     public boolean removeDeclaredInterface(String name) {
857         String[] names = getDeclaredInterfaceNames();
858         Iterator itr = _state.getInterfacesHolder().iterator();
859         for (int i = 0; i < names.length; i++) {
860             itr.next();
861             if (names[i].equals(name)) {
862                 itr.remove();
863                 return true;
864             }
865         }
866         return false;
867     }
868 
869     /***
870      * Remove an interface declared by this class.
871      *
872      * @return true if the class had the interface, false otherwise
873      */
874     public boolean removeDeclaredInterface(Class type) {
875         if (type == null)
876             return false;
877         return removeDeclaredInterface(type.getName());
878     }
879 
880     /***
881      * Remove an interface declared by this class.
882      *
883      * @return true if the class had the interface, false otherwise
884      */
885     public boolean removeDeclaredInterface(BCClass type) {
886         if (type == null)
887             return false;
888         return removeDeclaredInterface(type.getName());
889     }
890 
891     /***
892      * Rearrange declared interface order.  
893      */
894     public void moveDeclaredInterface(int fromIdx, int toIdx) {
895         if (fromIdx == toIdx)
896             return;
897         List interfaces = _state.getInterfacesHolder();
898         Object o = interfaces.remove(fromIdx);
899         interfaces.add(toIdx, o);
900     }
901 
902     /***
903      * Add an interface to those declared by this class.
904      */
905     public void declareInterface(String name) {
906         Integer index = Numbers.valueOf(getPool().findClassEntry(_project.
907             getNameCache().getInternalForm(name, false), true));
908         List interfaces = _state.getInterfacesHolder();
909         if (!interfaces.contains(index))
910             interfaces.add(index);
911     }
912 
913     /***
914      * Add an interface to those declared by this class.
915      */
916     public void declareInterface(Class type) {
917         declareInterface(type.getName());
918     }
919 
920     /***
921      * Add an interface to those declared by this class.
922      */
923     public void declareInterface(BCClass type) {
924         declareInterface(type.getName());
925     }
926 
927     /***
928      * Return true if this class or any of its superclasses implement/extend
929      * the given interface/class.
930      * This method does not recurse into interfaces-of-interfaces.
931      */
932     public boolean isInstanceOf(String name) {
933         name = _project.getNameCache().getExternalForm(name, false);
934         String[] interfaces = getInterfaceNames();
935         for (int i = 0; i < interfaces.length; i++)
936             if (interfaces[i].equals(name))
937                 return true;
938         for (BCClass type = this; type != null; type = type.getSuperclassBC())
939             if (type.getName().equals(name))
940                 return true;
941         return false;
942     }
943 
944     /***
945      * Return true if this class or any of its superclasses implement/extend
946      * the given interface/class.
947      * This method does not recurse into interfaces-of-interfaces.
948      */
949     public boolean isInstanceOf(Class type) {
950         if (type == null)
951             return false;
952         return isInstanceOf(type.getName());
953     }
954 
955     /***
956      * Return true if this class or any of its superclasses implement/extend
957      * the given interface/class.
958      * This method does not recurse into interfaces-of-interfaces.
959      */
960     public boolean isInstanceOf(BCClass type) {
961         if (type == null)
962             return false;
963         return isInstanceOf(type.getName());
964     }
965 
966     //////////////////////
967     // Field operations
968     //////////////////////
969 
970     /***
971      * Return all the declared fields of this class, or an empty array if none.
972      */
973     public BCField[] getDeclaredFields() {
974         List fields = _state.getFieldsHolder();
975         return (BCField[]) fields.toArray(new BCField[fields.size()]);
976     }
977 
978     /***
979      * Return the declared field with the given name, or null if none.
980      */
981     public BCField getDeclaredField(String name) {
982         BCField[] fields = getDeclaredFields();
983         for (int i = 0; i < fields.length; i++)
984             if (fields[i].getName().equals(name))
985                 return fields[i];
986         return null;
987     }
988 
989     /***
990      * Return all the fields of this class, including those of all
991      * superclasses, or an empty array if none.
992      */
993     public BCField[] getFields() {
994         Collection allFields = new LinkedList();
995         BCField[] fields;
996         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
997             fields = type.getDeclaredFields();
998             for (int i = 0; i < fields.length; i++)
999                 allFields.add(fields[i]);
1000         }
1001         return (BCField[]) allFields.toArray(new BCField[allFields.size()]);
1002     }
1003 
1004     /***
1005      * Return all fields with the given name, including those of all
1006      * superclasses, or an empty array if none.
1007      */
1008     public BCField[] getFields(String name) {
1009         List matches = new LinkedList();
1010         BCField[] fields = getFields();
1011         for (int i = 0; i < fields.length; i++)
1012             if (fields[i].getName().equals(name))
1013                 matches.add(fields[i]);
1014         return (BCField[]) matches.toArray(new BCField[matches.size()]);
1015     }
1016 
1017     /***
1018      * Set the fields for this class; this method is useful for importing all
1019      * fields from another class. Set to null or empty array if none.
1020      */
1021     public void setDeclaredFields(BCField[] fields) {
1022         clearDeclaredFields();
1023         if (fields != null)
1024             for (int i = 0; i < fields.length; i++)
1025                 declareField(fields[i]);
1026     }
1027 
1028     /***
1029      * Import the information from given field as a new field in this class.
1030      *
1031      * @return the added field
1032      */
1033     public BCField declareField(BCField field) {
1034         BCField newField = declareField(field.getName(), field.getTypeName());
1035         newField.setAccessFlags(field.getAccessFlags());
1036         newField.setAttributes(field.getAttributes());
1037         return newField;
1038     }
1039 
1040     /***
1041      * Add a field to this class.
1042      *
1043      * @return the added field
1044      */
1045     public BCField declareField(String name, String type) {
1046         BCField field = new BCField(this);
1047         _state.getFieldsHolder().add(field);
1048         field.initialize(name, _project.getNameCache().getInternalForm(type, 
1049             true));
1050         return field;
1051     }
1052 
1053     /***
1054      * Add a field to this class.
1055      *
1056      * @return the added field
1057      */
1058     public BCField declareField(String name, Class type) {
1059         String typeName = (type == null) ? null : type.getName();
1060         return declareField(name, typeName);
1061     }
1062 
1063     /***
1064      * Add a field to this class.
1065      *
1066      * @return the added field
1067      */
1068     public BCField declareField(String name, BCClass type) {
1069         String typeName = (type == null) ? null : type.getName();
1070         return declareField(name, typeName);
1071     }
1072 
1073     /***
1074      * Clear all fields from this class.
1075      */
1076     public void clearDeclaredFields() {
1077         List fields = _state.getFieldsHolder();
1078         BCField field;
1079         for (Iterator itr = fields.iterator(); itr.hasNext();) {
1080             field = (BCField) itr.next();
1081             itr.remove();
1082             field.invalidate();
1083         }
1084     }
1085 
1086     /***
1087      * Remove a field from this class. After this method, the removed field
1088      * will be invalid, and the result of any operations on it is undefined.
1089      *
1090      * @return true if this class contained the field, false otherwise
1091      */
1092     public boolean removeDeclaredField(String name) {
1093         List fields = _state.getFieldsHolder();
1094         BCField field;
1095         for (Iterator itr = fields.iterator(); itr.hasNext();) {
1096             field = (BCField) itr.next();
1097             if (field.getName().equals(name)) {
1098                 itr.remove();
1099                 field.invalidate();
1100                 return true;
1101             }
1102         }
1103         return false;
1104     }
1105 
1106     /***
1107      * Remove a field from this class. After this method, the removed field
1108      * will be invalid, and the result of any operations on it is undefined.
1109      *
1110      * @return true if this class contained the field, false otherwise
1111      */
1112     public boolean removeDeclaredField(BCField field) {
1113         if (field == null)
1114             return false;
1115         return removeDeclaredField(field.getName());
1116     }
1117 
1118     /***
1119      * Rearrange declared field order.  
1120      */
1121     public void moveDeclaredField(int fromIdx, int toIdx) {
1122         if (fromIdx == toIdx)
1123             return;
1124         List fields = _state.getFieldsHolder();
1125         Object o = fields.remove(fromIdx);
1126         fields.add(toIdx, o);
1127     }
1128 
1129     //////////////////////
1130     // Method operations
1131     //////////////////////
1132 
1133     /***
1134      * Return all methods declared by this class. Constructors and static
1135      * initializers are included.
1136      */
1137     public BCMethod[] getDeclaredMethods() {
1138         List methods = _state.getMethodsHolder();
1139         return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]);
1140     }
1141 
1142     /***
1143      * Return the declared method with the given name, or null if none.
1144      * If multiple methods are declared with the given name, which is returned
1145      * is undefined.
1146      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1147      * and static initializers are named <code>&lt;clinit&gt;</code>.
1148      */
1149     public BCMethod getDeclaredMethod(String name) {
1150         BCMethod[] methods = getDeclaredMethods();
1151         for (int i = 0; i < methods.length; i++)
1152             if (methods[i].getName().equals(name))
1153                 return methods[i];
1154         return null;
1155     }
1156 
1157     /***
1158      * Return all the declared methods with the given name, or an empty array
1159      * if none.
1160      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1161      * and static initializers are named <code>&lt;clinit&gt;</code>.
1162      */
1163     public BCMethod[] getDeclaredMethods(String name) {
1164         Collection matches = new LinkedList();
1165         BCMethod[] methods = getDeclaredMethods();
1166         for (int i = 0; i < methods.length; i++)
1167             if (methods[i].getName().equals(name))
1168                 matches.add(methods[i]);
1169         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1170     }
1171 
1172     /***
1173      * Return the declared method with the given name and parameter types,
1174      * or null if none. If multiple methods are declared with the given name
1175      * and parameters, which is returned is undefined.
1176      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1177      * and static initializers are named <code>&lt;clinit&gt;</code>.
1178      */
1179     public BCMethod getDeclaredMethod(String name, String[] paramTypes) {
1180         if (paramTypes == null)
1181             paramTypes = new String[0];
1182 
1183         BCMethod[] methods = getDeclaredMethods();
1184         for (int i = 0; i < methods.length; i++) {
1185             if (methods[i].getName().equals(name) 
1186                 && paramsMatch(methods[i], paramTypes))
1187                 return methods[i];
1188         }
1189         return null;
1190     }
1191 
1192     /***
1193      * Return true iff the given method's parameters match <code>params</code>.
1194      */
1195     private boolean paramsMatch(BCMethod meth, String[] params) {
1196         String[] mparams = meth.getParamNames();
1197         if (mparams.length != params.length)
1198             return false;
1199 
1200         for (int i = 0; i < params.length; i++) {
1201             if (!mparams[i].equals(_project.getNameCache().
1202                 getExternalForm(params[i], false)))
1203                 return false;
1204         }
1205         return true;
1206     }
1207 
1208     /***
1209      * Return the declared method with the given name and parameter types,
1210      * or null if none. If multiple methods are declared with the given name
1211      * and parameters, which is returned is undefined.
1212      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1213      * and static initializers are named <code>&lt;clinit&gt;</code>.
1214      */
1215     public BCMethod getDeclaredMethod(String name, Class[] paramTypes) {
1216         if (paramTypes == null)
1217             return getDeclaredMethod(name, (String[]) null);
1218 
1219         String[] paramNames = new String[paramTypes.length];
1220         for (int i = 0; i < paramTypes.length; i++)
1221             paramNames[i] = paramTypes[i].getName();
1222         return getDeclaredMethod(name, paramNames);
1223     }
1224 
1225     /***
1226      * Return the declared method with the given name and parameter types,
1227      * or null if none. If multiple methods are declared with the given name
1228      * and parameters, which is returned is undefined.
1229      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1230      * and static initializers are named <code>&lt;clinit&gt;</code>.
1231      */
1232     public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) {
1233         if (paramTypes == null)
1234             return getDeclaredMethod(name, (String[]) null);
1235 
1236         String[] paramNames = new String[paramTypes.length];
1237         for (int i = 0; i < paramTypes.length; i++)
1238             paramNames[i] = paramTypes[i].getName();
1239         return getDeclaredMethod(name, paramNames);
1240     }
1241 
1242     /***
1243      * Return all declared methods with the given name and parameter types.
1244      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1245      * and static initializers are named <code>&lt;clinit&gt;</code>.
1246      */
1247     public BCMethod[] getDeclaredMethods(String name, String[] paramTypes) {
1248         if (paramTypes == null)
1249             paramTypes = new String[0];
1250 
1251         BCMethod[] methods = getDeclaredMethods();
1252         List matches = null;
1253         for (int i = 0; i < methods.length; i++) {
1254             if (methods[i].getName().equals(name) 
1255                 && paramsMatch(methods[i], paramTypes)) {
1256                 if (matches == null)
1257                     matches = new ArrayList(3);
1258                 matches.add(methods[i]);
1259             }
1260         }
1261         if (matches == null)
1262             return new BCMethod[0];
1263         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1264     }
1265 
1266     /***
1267      * Return all declared methods with the given name and parameter types.
1268      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1269      * and static initializers are named <code>&lt;clinit&gt;</code>.
1270      */
1271     public BCMethod[] getDeclaredMethods(String name, Class[] paramTypes) {
1272         if (paramTypes == null)
1273             return getDeclaredMethods(name, (String[]) null);
1274 
1275         String[] paramNames = new String[paramTypes.length];
1276         for (int i = 0; i < paramTypes.length; i++)
1277             paramNames[i] = paramTypes[i].getName();
1278         return getDeclaredMethods(name, paramNames);
1279     }
1280 
1281     /***
1282      * Return all declared methods with the given name and parameter types.
1283      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1284      * and static initializers are named <code>&lt;clinit&gt;</code>.
1285      */
1286     public BCMethod[] getDeclaredMethods(String name, BCClass[] paramTypes) {
1287         if (paramTypes == null)
1288             return getDeclaredMethods(name, (String[]) null);
1289 
1290         String[] paramNames = new String[paramTypes.length];
1291         for (int i = 0; i < paramTypes.length; i++)
1292             paramNames[i] = paramTypes[i].getName();
1293         return getDeclaredMethods(name, paramNames);
1294     }
1295 
1296     /***
1297      * Return the declared method with the given name and signature,
1298      * or null if none.
1299      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1300      * and static initializers are named <code>&lt;clinit&gt;</code>.
1301      */
1302     public BCMethod getDeclaredMethod(String name, String returnType, 
1303         String[] paramTypes) {
1304         if (paramTypes == null)
1305             paramTypes = new String[0];
1306 
1307         BCMethod[] methods = getDeclaredMethods();
1308         for (int i = 0; i < methods.length; i++) {
1309             if (methods[i].getName().equals(name) 
1310                 && methods[i].getReturnName().equals(_project.getNameCache().
1311                     getExternalForm(returnType, false))
1312                 && paramsMatch(methods[i], paramTypes))
1313                 return methods[i];
1314         }
1315         return null;
1316     }
1317 
1318     /***
1319      * Return the declared method with the given name and signature,
1320      * or null if none.
1321      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1322      * and static initializers are named <code>&lt;clinit&gt;</code>.
1323      */
1324     public BCMethod getDeclaredMethod(String name, Class returnType, 
1325         Class[] paramTypes) {
1326         if (paramTypes == null)
1327             return getDeclaredMethod(name, returnType.getName(), 
1328                 (String[]) null);
1329 
1330         String[] paramNames = new String[paramTypes.length];
1331         for (int i = 0; i < paramTypes.length; i++)
1332             paramNames[i] = paramTypes[i].getName();
1333         return getDeclaredMethod(name, returnType.getName(), paramNames);
1334     }
1335 
1336     /***
1337      * Return the declared method with the given name and signature,
1338      * or null if none.
1339      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1340      * and static initializers are named <code>&lt;clinit&gt;</code>.
1341      */
1342     public BCMethod getDeclaredMethod(String name, BCClass returnType, 
1343         BCClass[] paramTypes) {
1344         if (paramTypes == null)
1345             return getDeclaredMethod(name, returnType.getName(), 
1346                 (String[]) null);
1347 
1348         String[] paramNames = new String[paramTypes.length];
1349         for (int i = 0; i < paramTypes.length; i++)
1350             paramNames[i] = paramTypes[i].getName();
1351         return getDeclaredMethod(name, returnType.getName(), paramNames);
1352     }
1353 
1354     /***
1355      * Return the methods of this class, including those of all superclasses,
1356      * or an empty array if none.
1357      * The base version of methods that are overridden will be included, as
1358      * will all constructors and static initializers.
1359      * The methods will be ordered from those in the most-specific type up to
1360      * those in {@link Object}.
1361      */
1362     public BCMethod[] getMethods() {
1363         Collection allMethods = new LinkedList();
1364         BCMethod[] methods;
1365         for (BCClass type = this; type != null; type = type.getSuperclassBC()) {
1366             methods = type.getDeclaredMethods();
1367             for (int i = 0; i < methods.length; i++)
1368                 allMethods.add(methods[i]);
1369         }
1370         return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]);
1371     }
1372 
1373     /***
1374      * Return the methods with the given name, including those of all
1375      * superclasses, or an empty array if none.
1376      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1377      * and static initializers are named <code>&lt;clinit&gt;</code>.
1378      */
1379     public BCMethod[] getMethods(String name) {
1380         Collection matches = new LinkedList();
1381         BCMethod[] methods = getMethods();
1382         for (int i = 0; i < methods.length; i++)
1383             if (methods[i].getName().equals(name))
1384                 matches.add(methods[i]);
1385         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1386     }
1387 
1388     /***
1389      * Return the methods with the given name and parameter types, including
1390      * those of all superclasses, or an empty array if none.
1391      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1392      * and static initializers are named <code>&lt;clinit&gt;</code>.
1393      */
1394     public BCMethod[] getMethods(String name, String[] paramTypes) {
1395         if (paramTypes == null)
1396             paramTypes = new String[0];
1397 
1398         String[] curParams;
1399         boolean match;
1400         BCMethod[] methods = getMethods();
1401         Collection matches = new LinkedList();
1402         for (int i = 0; i < methods.length; i++) {
1403             if (!methods[i].getName().equals(name))
1404                 continue;
1405             curParams = methods[i].getParamNames();
1406             if (curParams.length != paramTypes.length)
1407                 continue;
1408 
1409             match = true;
1410             for (int j = 0; j < paramTypes.length; j++) {
1411                 if (!curParams[j].equals(_project.getNameCache().
1412                     getExternalForm(paramTypes[j], false))) {
1413                     match = false;
1414                     break;
1415                 }
1416             }
1417             if (match)
1418                 matches.add(methods[i]);
1419         }
1420         return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]);
1421     }
1422 
1423     /***
1424      * Return the methods with the given name and parameter types, including
1425      * those of all superclasses, or an empty array if none.
1426      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1427      * and static initializers are named <code>&lt;clinit&gt;</code>.
1428      */
1429     public BCMethod[] getMethods(String name, Class[] paramTypes) {
1430         if (paramTypes == null)
1431             return getMethods(name, (String[]) null);
1432 
1433         String[] paramNames = new String[paramTypes.length];
1434         for (int i = 0; i < paramTypes.length; i++)
1435             paramNames[i] = paramTypes[i].getName();
1436         return getMethods(name, paramNames);
1437     }
1438 
1439     /***
1440      * Return the methods with the given name and parameter types, including
1441      * those of all superclasses, or an empty array if none.
1442      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1443      * and static initializers are named <code>&lt;clinit&gt;</code>.
1444      */
1445     public BCMethod[] getMethods(String name, BCClass[] paramTypes) {
1446         if (paramTypes == null)
1447             return getMethods(name, (String[]) null);
1448 
1449         String[] paramNames = new String[paramTypes.length];
1450         for (int i = 0; i < paramTypes.length; i++)
1451             paramNames[i] = paramTypes[i].getName();
1452         return getMethods(name, paramNames);
1453     }
1454 
1455     /***
1456      * Set the methods for this class; this method is useful for importing all
1457      * methods from another class. Set to null or empty array if none.
1458      */
1459     public void setDeclaredMethods(BCMethod[] methods) {
1460         clearDeclaredMethods();
1461         if (methods != null)
1462             for (int i = 0; i < methods.length; i++)
1463                 declareMethod(methods[i]);
1464     }
1465 
1466     /***
1467      * Import the information in the given method as a new method of this class.
1468      *
1469      * @return the added method
1470      */
1471     public BCMethod declareMethod(BCMethod method) {
1472         BCMethod newMethod = declareMethod(method.getName(), 
1473             method.getReturnName(), method.getParamNames());
1474         newMethod.setAccessFlags(method.getAccessFlags());
1475         newMethod.setAttributes(method.getAttributes());
1476         return newMethod;
1477     }
1478 
1479     /***
1480      * Add a method to this class.
1481      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1482      * and static initializers are named <code>&lt;clinit&gt;</code>.
1483      *
1484      * @return the added method
1485      */
1486     public BCMethod declareMethod(String name, String returnType,
1487         String[] paramTypes) {
1488         BCMethod method = new BCMethod(this);
1489         _state.getMethodsHolder().add(method);
1490         method.initialize(name, _project.getNameCache().
1491             getDescriptor(returnType, paramTypes));
1492         return method;
1493     }
1494 
1495     /***
1496      * Add a method to this class.
1497      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1498      * and static initializers are named <code>&lt;clinit&gt;</code>.
1499      *
1500      * @return the added method
1501      */
1502     public BCMethod declareMethod(String name, Class returnType,
1503         Class[] paramTypes) {
1504         String[] paramNames = null;
1505         if (paramTypes != null) {
1506             paramNames = new String[paramTypes.length];
1507             for (int i = 0; i < paramTypes.length; i++)
1508                 paramNames[i] = paramTypes[i].getName();
1509         }
1510         String returnName = (returnType == null) ? null : returnType.getName();
1511         return declareMethod(name, returnName, paramNames);
1512     }
1513 
1514     /***
1515      * Add a method to this class.
1516      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1517      * and static initializers are named <code>&lt;clinit&gt;</code>.
1518      *
1519      * @return the added method
1520      */
1521     public BCMethod declareMethod(String name, BCClass returnType,
1522         BCClass[] paramTypes) {
1523         String[] paramNames = null;
1524         if (paramTypes != null) {
1525             paramNames = new String[paramTypes.length];
1526             for (int i = 0; i < paramTypes.length; i++)
1527                 paramNames[i] = paramTypes[i].getName();
1528         }
1529         String returnName = (returnType == null) ? null : returnType.getName();
1530         return declareMethod(name, returnName, paramNames);
1531     }
1532 
1533     /***
1534      * Clear all declared methods from this class.
1535      */
1536     public void clearDeclaredMethods() {
1537         List methods = _state.getMethodsHolder();
1538         BCMethod method;
1539         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1540             method = (BCMethod) itr.next();
1541             itr.remove();
1542             method.invalidate();
1543         }
1544     }
1545 
1546     /***
1547      * Remove a method from this class. After this method, the removed method
1548      * will be invalid, and the result of any operations on it is undefined.
1549      * If multiple methods match the given name, which is removed is undefined.
1550      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1551      * and static initializers are named <code>&lt;clinit&gt;</code>.
1552      *
1553      * @return true if this class contained the method, false otherwise
1554      */
1555     public boolean removeDeclaredMethod(String name) {
1556         List methods = _state.getMethodsHolder();
1557         BCMethod method;
1558         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1559             method = (BCMethod) itr.next();
1560             if (method.getName().equals(name)) {
1561                 itr.remove();
1562                 method.invalidate();
1563                 return true;
1564             }
1565         }
1566         return false;
1567     }
1568 
1569     /***
1570      * Removes a method from this class. After this method, the removed method
1571      * will be invalid, and the result of any operations on it is undefined.
1572      *
1573      * @return true if this class contained the method, false otherwise
1574      */
1575     public boolean removeDeclaredMethod(BCMethod method) {
1576         if (method == null)
1577             return false;
1578         return removeDeclaredMethod(method.getName(), method.getParamNames());
1579     }
1580 
1581     /***
1582      * Removes a method from this class. After this method, the removed method
1583      * will be invalid, and the result of any operations on it is undefined.
1584      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1585      * and static initializers are named <code>&lt;clinit&gt;</code>.
1586      *
1587      * @return true if this class contained the method, false otherwise
1588      */
1589     public boolean removeDeclaredMethod(String name, String[] paramTypes) {
1590         if (paramTypes == null)
1591             paramTypes = new String[0];
1592 
1593         String[] curParams;
1594         boolean match;
1595         List methods = _state.getMethodsHolder();
1596         BCMethod method;
1597         for (Iterator itr = methods.iterator(); itr.hasNext();) {
1598             method = (BCMethod) itr.next();
1599             if (!method.getName().equals(name))
1600                 continue;
1601             curParams = method.getParamNames();
1602             if (curParams.length != paramTypes.length)
1603                 continue;
1604 
1605             match = true;
1606             for (int j = 0; j < paramTypes.length; j++) {
1607                 if (!curParams[j].equals(_project.getNameCache().
1608                     getExternalForm(paramTypes[j], false))) {
1609                     match = false;
1610                     break;
1611                 }
1612             }
1613             if (match) {
1614                 itr.remove();
1615                 method.invalidate();
1616                 return true;
1617             }
1618         }
1619         return false;
1620     }
1621 
1622     /***
1623      * Removes a method from this class. After this method, the removed method
1624      * will be invalid, and the result of any operations on it is undefined.
1625      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1626      * and static initializers are named <code>&lt;clinit&gt;</code>.
1627      *
1628      * @return true if this class contained the method, false otherwise
1629      */
1630     public boolean removeDeclaredMethod(String name, Class[] paramTypes) {
1631         if (paramTypes == null)
1632             return removeDeclaredMethod(name, (String[]) null);
1633 
1634         String[] paramNames = new String[paramTypes.length];
1635         for (int i = 0; i < paramTypes.length; i++)
1636             paramNames[i] = paramTypes[i].getName();
1637         return removeDeclaredMethod(name, paramNames);
1638     }
1639 
1640     /***
1641      * Removes a method from this class. After this method, the removed method
1642      * will be invalid, and the result of any operations on it is undefined.
1643      * Note that in bytecode, constructors are named <code>&lt;init&gt;</code>
1644      * and static initializers are named <code>&lt;clinit&gt;</code>.
1645      *
1646      * @return true if this class contained the method, false otherwise
1647      */
1648     public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) {
1649         if (paramTypes == null)
1650             return removeDeclaredMethod(name, (String[]) null);
1651 
1652         String[] paramNames = new String[paramTypes.length];
1653         for (int i = 0; i < paramTypes.length; i++)
1654             paramNames[i] = paramTypes[i].getName();
1655         return removeDeclaredMethod(name, paramNames);
1656     }
1657 
1658     /***
1659      * Rearrange method order.  
1660      */
1661     public void moveDeclaredMethod(int fromIdx, int toIdx) {
1662         if (fromIdx == toIdx)
1663             return;
1664         List methods = _state.getMethodsHolder();
1665         Object o = methods.remove(fromIdx);
1666         methods.add(toIdx, o);
1667     }
1668 
1669     ///////////////////////
1670     // Convenience methods
1671     ///////////////////////
1672 
1673     /***
1674      * Convenience method to add a default constructor to this class.
1675      * If a default constructor already exists, this method will return it
1676      * without modification.
1677      * This method can only be called if the superclass has been set.
1678      *
1679      * @return the default constructor
1680      */
1681     public BCMethod addDefaultConstructor() {
1682         BCMethod method = getDeclaredMethod("<init>", (String[]) null);
1683         if (method != null)
1684             return method;
1685 
1686         method = declareMethod("<init>", void.class, null);
1687         Code code = method.getCode(true);
1688         code.setMaxStack(1);
1689         code.setMaxLocals(1);
1690 
1691         code.xload().setThis();
1692         code.invokespecial()
1693             .setMethod(getSuperclassName(), "<init>", "void", null);
1694         code.vreturn();
1695         return method;
1696     }
1697 
1698     /***
1699      * Return source file information for the class.
1700      * Acts internally through the {@link Attributes} interface.
1701      *
1702      * @param add if true, a new source file attribute will be added
1703      * if not already present
1704      * @return the source file information, or null if none and the
1705      * <code>add</code> param is set to false
1706      */
1707     public SourceFile getSourceFile(boolean add) {
1708         SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE);
1709         if (!add || (source != null))
1710             return source;
1711         return (SourceFile) addAttribute(Constants.ATTR_SOURCE);
1712     }
1713 
1714     /***
1715      * Remove the source file attribute for the class.
1716      * Acts internally through the {@link Attributes} interface.
1717      *
1718      * @return true if there was a file to remove
1719      */
1720     public boolean removeSourceFile() {
1721         return removeAttribute(Constants.ATTR_SOURCE);
1722     }
1723 
1724     /***
1725      * Return inner classes information for the class.
1726      * Acts internally through the {@link Attributes} interface.
1727      *
1728      * @param add if true, a new inner classes attribute will be added
1729      * if not already present
1730      * @return the inner classes information, or null if none and the
1731      * <code>add</code> param is set to false
1732      */
1733     public InnerClasses getInnerClasses(boolean add) {
1734         InnerClasses inner = (InnerClasses) getAttribute
1735             (Constants.ATTR_INNERCLASS);
1736         if (!add || (inner != null))
1737             return inner;
1738         return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS);
1739     }
1740 
1741     /***
1742      * Remove the inner classes attribute for the class.
1743      * Acts internally through the {@link Attributes} interface.
1744      *
1745      * @return true if there was an attribute to remove
1746      */
1747     public boolean removeInnerClasses() {
1748         return removeAttribute(Constants.ATTR_INNERCLASS);
1749     }
1750 
1751     /***
1752      * Convenience method to return deprecation information for the class.
1753      * Acts internally through the {@link Attributes} interface.
1754      */
1755     public boolean isDeprecated() {
1756         return getAttribute(Constants.ATTR_DEPRECATED) != null;
1757     }
1758 
1759     /***
1760      * Convenience method to set whether this class should be considered
1761      * deprecated. Acts internally through the {@link Attributes} interface.
1762      */
1763     public void setDeprecated(boolean on) {
1764         if (!on)
1765             removeAttribute(Constants.ATTR_DEPRECATED);
1766         else if (!isDeprecated())
1767             addAttribute(Constants.ATTR_DEPRECATED);
1768     }
1769 
1770     ///////////////////////////////////
1771     // Implementation of VisitAcceptor
1772     ///////////////////////////////////
1773 
1774     public void acceptVisit(BCVisitor visit) {
1775         visit.enterBCClass(this);
1776 
1777         ConstantPool pool = null;
1778         try {
1779             pool = _state.getPool();
1780         } catch (UnsupportedOperationException uoe) {
1781         }
1782         if (pool != null)
1783             pool.acceptVisit(visit);
1784 
1785         BCField[] fields = getDeclaredFields();
1786         for (int i = 0; i < fields.length; i++) {
1787             visit.enterBCMember(fields[i]);
1788             fields[i].acceptVisit(visit);
1789             visit.exitBCMember(fields[i]);
1790         }
1791 
1792         BCMethod[] methods = getDeclaredMethods();
1793         for (int i = 0; i < methods.length; i++) {
1794             visit.enterBCMember(methods[i]);
1795             methods[i].acceptVisit(visit);
1796             visit.exitBCMember(methods[i]);
1797         }
1798 
1799         visitAttributes(visit);
1800         visit.exitBCClass(this);
1801     }
1802 
1803     ////////////////////////////////
1804     // Implementation of Attributes
1805     ////////////////////////////////
1806 
1807     public Project getProject() {
1808         return _project;
1809     }
1810 
1811     public ConstantPool getPool() {
1812         return _state.getPool();
1813     }
1814 
1815     public ClassLoader getClassLoader() {
1816         if (_loader != null)
1817             return _loader;
1818         return Thread.currentThread().getContextClassLoader();
1819     }
1820 
1821     public boolean isValid() {
1822         return _project != null;
1823     }
1824 
1825     Collection getAttributesHolder() {
1826         return _state.getAttributesHolder();
1827     }
1828 
1829     ///////////////////////////////
1830     // Implementation of Annotated
1831     ///////////////////////////////
1832 
1833     BCClass getBCClass() {
1834         return this;
1835     }
1836 
1837     /***
1838      * Attempts to change the class name with the owning project. The project
1839      * can reject the change if a class with the given new name already
1840      * exists; therefore this method should be called before the change is
1841      * recorded in the class.
1842      */
1843     private void beforeRename(String oldName, String newName) {
1844         if ((_project != null) && (oldName != null))
1845             _project.renameClass(oldName, newName, this);
1846     }
1847 }