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><init></code>
1126 * and static initializers are named <code><clinit></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><init></code>
1140 * and static initializers are named <code><clinit></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><init></code>
1155 * and static initializers are named <code><clinit></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><init></code>
1189 * and static initializers are named <code><clinit></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><init></code>
1205 * and static initializers are named <code><clinit></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><init></code>
1240 * and static initializers are named <code><clinit></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><init></code>
1255 * and static initializers are named <code><clinit></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><init></code>
1290 * and static initializers are named <code><clinit></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><init></code>
1306 * and static initializers are named <code><clinit></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><init></code>
1345 * and static initializers are named <code><clinit></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><init></code>
1361 * and static initializers are named <code><clinit></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><init></code>
1380 * and static initializers are named <code><clinit></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><init></code>
1414 * and static initializers are named <code><clinit></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><init></code>
1448 * and static initializers are named <code><clinit></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><init></code>
1489 * and static initializers are named <code><clinit></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><init></code>
1507 * and static initializers are named <code><clinit></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 }