001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.net.*;
005    import java.util.*;
006    
007    import serp.bytecode.lowlevel.*;
008    import serp.bytecode.visitor.*;
009    import 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     */
031    public 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    }