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