001package serp.bytecode;
002
003import java.io.*;
004import java.net.*;
005import java.util.*;
006
007import serp.bytecode.lowlevel.*;
008import serp.bytecode.visitor.*;
009import serp.util.*;
010
011/**
012 * The BCClass represents a class object in the bytecode framework, in many
013 * ways mirroring the {@link Class} class of Java reflection. The represented
014 * class might be a primitive, array, existing object type, or some new user-
015 * defined type. As with most entities in the bytecode framework, the BCClass
016 * contains methods to manipulate the low-level state of the class (constant
017 * pool indexes, etc), but these can and should be ignored in
018 * favor of the available high-level methods.
019 *
020 * <p>A BCClass instance is loaded from a {@link Project} and remains
021 * attached to that project for its lifetime. If a BCClass is removed from
022 * its project, the result of any further operations on the class are
023 * undefined.</p>
024 *
025 * <p>Note that if a BCClass represents a primitive or array type, all of the
026 * available mutator methods and any methods that access the constant pool
027 * will throw {@link UnsupportedOperationException}s.</p>
028 *
029 * @author Abe White
030 */
031public class BCClass extends Annotated implements VisitAcceptor {
032    private Project _project = null;
033    private State _state = null;
034    private ClassLoader _loader = null;
035
036    /**
037     * Hide constructor. For use by the owning project only.
038     */
039    BCClass(Project project) {
040        _project = project;
041    }
042
043    /**
044     * Set the class state. For use by the owning project only.
045     */
046    void setState(State state) {
047        _state = state;
048    }
049
050    /**
051     * Invalidate this class.
052     */
053    void invalidate() {
054        _project = null;
055        _state = State.INVALID;
056    }
057
058    //////////////////
059    // I/O operations
060    //////////////////
061
062    /**
063     * Initialize from the class definition in the given file. For use by
064     * the owning project only.
065     */
066    void read(File classFile, ClassLoader loader) throws IOException {
067        InputStream in = new FileInputStream(classFile);
068        try {
069            read(in, loader);
070        } finally {
071            in.close();
072        }
073    }
074
075    /**
076     * Initialize from the class definition in the given stream. For use by
077     * the owning project only.
078     */
079    void read(InputStream instream, ClassLoader loader)
080        throws IOException {
081        DataInput in = new DataInputStream(instream);
082
083        // header information
084        _state.setMagic(in.readInt());
085        _state.setMinorVersion(in.readUnsignedShort());
086        _state.setMajorVersion(in.readUnsignedShort());
087
088        // constant pool
089        _state.getPool().read(in);
090
091        // access flags
092        _state.setAccessFlags(in.readUnsignedShort());
093
094        // class, super class, interfaces
095        _state.setIndex(in.readUnsignedShort());
096        _state.setSuperclassIndex(in.readUnsignedShort());
097
098        List interfaces = _state.getInterfacesHolder();
099        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}