1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.classfile;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.DataOutputStream;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.StringTokenizer;
29 import java.util.TreeSet;
30 import org.apache.bcel.Constants;
31 import org.apache.bcel.generic.Type;
32 import org.apache.bcel.util.BCELComparator;
33 import org.apache.bcel.util.ClassQueue;
34 import org.apache.bcel.util.SyntheticRepository;
35
36 /***
37 * Represents a Java class, i.e., the data structures, constant pool,
38 * fields, methods and commands contained in a Java .class file.
39 * See <a href="ftp://java.sun.com/docs/specs/">JVM specification</a> for details.
40 * The intent of this class is to represent a parsed or otherwise existing
41 * class file. Those interested in programatically generating classes
42 * should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
43
44 * @version $Id: JavaClass.java 386056 2006-03-15 11:31:56Z tcurdt $
45 * @see org.apache.bcel.generic.ClassGen
46 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
47 */
48 public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable {
49
50 private String file_name;
51 private String package_name/package-summary.html">ong> String package_name;
52 private String source_file_name = "<Unknown>";
53 private int class_name_index;
54 private int superclass_name_index;
55 private String class_name;
56 private String superclass_name;
57 private int major, minor;
58 private ConstantPool constant_pool;
59 private int[] interfaces;
60 private String[] interface_names;
61 private Field[] fields;
62 private Method[] methods;
63 private Attribute[] attributes;
64 private byte source = HEAP;
65 public static final byte HEAP = 1;
66 public static final byte FILE = 2;
67 public static final byte ZIP = 3;
68 static boolean debug = false;
69 static char sep = '/';
70 private static BCELComparator _cmp = new BCELComparator() {
71
72 public boolean equals( Object o1, Object o2 ) {
73 JavaClass THIS = (JavaClass) o1;
74 JavaClass THAT = (JavaClass) o2;
75 return THIS.getClassName().equals(THAT.getClassName());
76 }
77
78
79 public int hashCode( Object o ) {
80 JavaClass THIS = (JavaClass) o;
81 return THIS.getClassName().hashCode();
82 }
83 };
84 /***
85 * In cases where we go ahead and create something,
86 * use the default SyntheticRepository, because we
87 * don't know any better.
88 */
89 private transient org.apache.bcel.util.Repository repository = SyntheticRepository
90 .getInstance();
91
92
93 /***
94 * Constructor gets all contents as arguments.
95 *
96 * @param class_name_index Index into constant pool referencing a
97 * ConstantClass that represents this class.
98 * @param superclass_name_index Index into constant pool referencing a
99 * ConstantClass that represents this class's superclass.
100 * @param file_name File name
101 * @param major Major compiler version
102 * @param minor Minor compiler version
103 * @param access_flags Access rights defined by bit flags
104 * @param constant_pool Array of constants
105 * @param interfaces Implemented interfaces
106 * @param fields Class fields
107 * @param methods Class methods
108 * @param attributes Class attributes
109 * @param source Read from file or generated in memory?
110 */
111 public JavaClass(int class_name_index, int superclass_name_index, String file_name, int major,
112 int minor, int access_flags, ConstantPool constant_pool, int[] interfaces,
113 Field[] fields, Method[] methods, Attribute[] attributes, byte source) {
114 if (interfaces == null) {
115 interfaces = new int[0];
116 }
117 if (attributes == null) {
118 attributes = new Attribute[0];
119 }
120 if (fields == null) {
121 fields = new Field[0];
122 }
123 if (methods == null) {
124 methods = new Method[0];
125 }
126 this.class_name_index = class_name_index;
127 this.superclass_name_index = superclass_name_index;
128 this.file_name = file_name;
129 this.major = major;
130 this.minor = minor;
131 this.access_flags = access_flags;
132 this.constant_pool = constant_pool;
133 this.interfaces = interfaces;
134 this.fields = fields;
135 this.methods = methods;
136 this.attributes = attributes;
137 this.source = source;
138
139 for (int i = 0; i < attributes.length; i++) {
140 if (attributes[i] instanceof SourceFile) {
141 source_file_name = ((SourceFile) attributes[i]).getSourceFileName();
142 break;
143 }
144 }
145
146
147
148
149 class_name = constant_pool.getConstantString(class_name_index, Constants.CONSTANT_Class);
150 class_name = Utility.compactClassName(class_name, false);
151 int index = class_name.lastIndexOf('.');
152 if (index < 0) {
153 packagestrong>_name = "";
154 } else {
155 packagestrong>_name = class_name.substring(0, index);
156 }
157 if (superclass_name_index > 0) {
158
159 superclass_name = constant_pool.getConstantString(superclass_name_index,
160 Constants.CONSTANT_Class);
161 superclass_name = Utility.compactClassName(superclass_name, false);
162 } else {
163 superclass_name = "java.lang.Object";
164 }
165 interface_names = new String[interfaces.length];
166 for (int i = 0; i < interfaces.length; i++) {
167 String str = constant_pool.getConstantString(interfaces[i], Constants.CONSTANT_Class);
168 interface_names[i] = Utility.compactClassName(str, false);
169 }
170 }
171
172
173 /***
174 * Constructor gets all contents as arguments.
175 *
176 * @param class_name_index Class name
177 * @param superclass_name_index Superclass name
178 * @param file_name File name
179 * @param major Major compiler version
180 * @param minor Minor compiler version
181 * @param access_flags Access rights defined by bit flags
182 * @param constant_pool Array of constants
183 * @param interfaces Implemented interfaces
184 * @param fields Class fields
185 * @param methods Class methods
186 * @param attributes Class attributes
187 */
188 public JavaClass(int class_name_index, int superclass_name_index, String file_name, int major,
189 int minor, int access_flags, ConstantPool constant_pool, int[] interfaces,
190 Field[] fields, Method[] methods, Attribute[] attributes) {
191 this(class_name_index, superclass_name_index, file_name, major, minor, access_flags,
192 constant_pool, interfaces, fields, methods, attributes, HEAP);
193 }
194
195
196 /***
197 * Called by objects that are traversing the nodes of the tree implicitely
198 * defined by the contents of a Java class. I.e., the hierarchy of methods,
199 * fields, attributes, etc. spawns a tree of objects.
200 *
201 * @param v Visitor object
202 */
203 public void accept( Visitor v ) {
204 v.visitJavaClass(this);
205 }
206
207
208
209
210 static final void Debug( String str ) {
211 if (debug) {
212 System.out.println(str);
213 }
214 }
215
216
217 /***
218 * Dump class to a file.
219 *
220 * @param file Output file
221 * @throws IOException
222 */
223 public void dump( File file ) throws IOException {
224 String parent = file.getParent();
225 if (parent != null) {
226 File dir = new File(parent);
227 dir.mkdirs();
228 }
229 DataOutputStream dos = null;
230 try {
231 dos = new DataOutputStream(new FileOutputStream(file));
232 dump(dos);
233 } finally {
234 if (dos != null) {
235 dos.close();
236 }
237 }
238 }
239
240
241 /***
242 * Dump class to a file named file_name.
243 *
244 * @param _file_name Output file name
245 * @exception IOException
246 */
247 public void dump( String _file_name ) throws IOException {
248 dump(new File(_file_name));
249 }
250
251
252 /***
253 * @return class in binary format
254 */
255 public byte[] getBytes() {
256 ByteArrayOutputStream s = new ByteArrayOutputStream();
257 DataOutputStream ds = new DataOutputStream(s);
258 try {
259 dump(ds);
260 } catch (IOException e) {
261 e.printStackTrace();
262 } finally {
263 try {
264 ds.close();
265 } catch (IOException e2) {
266 e2.printStackTrace();
267 }
268 }
269 return s.toByteArray();
270 }
271
272
273 /***
274 * Dump Java class to output stream in binary format.
275 *
276 * @param file Output stream
277 * @exception IOException
278 */
279 public void dump( OutputStream file ) throws IOException {
280 dump(new DataOutputStream(file));
281 }
282
283
284 /***
285 * Dump Java class to output stream in binary format.
286 *
287 * @param file Output stream
288 * @exception IOException
289 */
290 public void dump( DataOutputStream file ) throws IOException {
291 file.writeInt(0xcafebabe);
292 file.writeShort(minor);
293 file.writeShort(major);
294 constant_pool.dump(file);
295 file.writeShort(access_flags);
296 file.writeShort(class_name_index);
297 file.writeShort(superclass_name_index);
298 file.writeShort(interfaces.length);
299 for (int i = 0; i < interfaces.length; i++) {
300 file.writeShort(interfaces[i]);
301 }
302 file.writeShort(fields.length);
303 for (int i = 0; i < fields.length; i++) {
304 fields[i].dump(file);
305 }
306 file.writeShort(methods.length);
307 for (int i = 0; i < methods.length; i++) {
308 methods[i].dump(file);
309 }
310 if (attributes != null) {
311 file.writeShort(attributes.length);
312 for (int i = 0; i < attributes.length; i++) {
313 attributes[i].dump(file);
314 }
315 } else {
316 file.writeShort(0);
317 }
318 file.flush();
319 }
320
321
322 /***
323 * @return Attributes of the class.
324 */
325 public Attribute[] getAttributes() {
326 return attributes;
327 }
328
329
330 /***
331 * @return Class name.
332 */
333 public String getClassName() {
334 return class_name;
335 }
336
337
338 /***
339 * @return Package name.
340 */
341 public String getPackageName() {
342 return</strong> package_name;
343 }
344
345
346 /***
347 * @return Class name index.
348 */
349 public int getClassNameIndex() {
350 return class_name_index;
351 }
352
353
354 /***
355 * @return Constant pool.
356 */
357 public ConstantPool getConstantPool() {
358 return constant_pool;
359 }
360
361
362 /***
363 * @return Fields, i.e., variables of the class. Like the JVM spec
364 * mandates for the classfile format, these fields are those specific to
365 * this class, and not those of the superclass or superinterfaces.
366 */
367 public Field[] getFields() {
368 return fields;
369 }
370
371
372 /***
373 * @return File name of class, aka SourceFile attribute value
374 */
375 public String getFileName() {
376 return file_name;
377 }
378
379
380 /***
381 * @return Names of implemented interfaces.
382 */
383 public String[] getInterfaceNames() {
384 return interface_names;
385 }
386
387
388 /***
389 * @return Indices in constant pool of implemented interfaces.
390 */
391 public int[] getInterfaceIndices() {
392 return interfaces;
393 }
394
395
396 /***
397 * @return Major number of class file version.
398 */
399 public int getMajor() {
400 return major;
401 }
402
403
404 /***
405 * @return Methods of the class.
406 */
407 public Method[] getMethods() {
408 return methods;
409 }
410
411
412 /***
413 * @return A org.apache.bcel.classfile.Method corresponding to
414 * java.lang.reflect.Method if any
415 */
416 public Method getMethod( java.lang.reflect.Method m ) {
417 for (int i = 0; i < methods.length; i++) {
418 Method method = methods[i];
419 if (m.getName().equals(method.getName()) && (m.getModifiers() == method.getModifiers())
420 && Type.getSignature(m).equals(method.getSignature())) {
421 return method;
422 }
423 }
424 return null;
425 }
426
427
428 /***
429 * @return Minor number of class file version.
430 */
431 public int getMinor() {
432 return minor;
433 }
434
435
436 /***
437 * @return sbsolute path to file where this class was read from
438 */
439 public String getSourceFileName() {
440 return source_file_name;
441 }
442
443
444 /***
445 * @return Superclass name.
446 */
447 public String getSuperclassName() {
448 return superclass_name;
449 }
450
451
452 /***
453 * @return Class name index.
454 */
455 public int getSuperclassNameIndex() {
456 return superclass_name_index;
457 }
458
459 static {
460
461 debug = Boolean.getBoolean("JavaClass.debug");
462
463 String _sep = System.getProperty("file.separator");
464 if (_sep != null) {
465 try {
466 JavaClass.sep = _sep.charAt(0);
467 } catch (StringIndexOutOfBoundsException e) {
468 }
469 }
470 }
471
472
473 /***
474 * @param attributes .
475 */
476 public void setAttributes( Attribute[] attributes ) {
477 this.attributes = attributes;
478 }
479
480
481 /***
482 * @param class_name .
483 */
484 public void setClassName( String class_name ) {
485 this.class_name = class_name;
486 }
487
488
489 /***
490 * @param class_name_index .
491 */
492 public void setClassNameIndex( int class_name_index ) {
493 this.class_name_index = class_name_index;
494 }
495
496
497 /***
498 * @param constant_pool .
499 */
500 public void setConstantPool( ConstantPool constant_pool ) {
501 this.constant_pool = constant_pool;
502 }
503
504
505 /***
506 * @param fields .
507 */
508 public void setFields( Field[] fields ) {
509 this.fields = fields;
510 }
511
512
513 /***
514 * Set File name of class, aka SourceFile attribute value
515 */
516 public void setFileName( String file_name ) {
517 this.file_name = file_name;
518 }
519
520
521 /***
522 * @param interface_names .
523 */
524 public void setInterfaceNames( String[] interface_names ) {
525 this.interface_names = interface_names;
526 }
527
528
529 /***
530 * @param interfaces .
531 */
532 public void setInterfaces( int[] interfaces ) {
533 this.interfaces = interfaces;
534 }
535
536
537 /***
538 * @param major .
539 */
540 public void setMajor( int major ) {
541 this.major = major;
542 }
543
544
545 /***
546 * @param methods .
547 */
548 public void setMethods( Method[] methods ) {
549 this.methods = methods;
550 }
551
552
553 /***
554 * @param minor .
555 */
556 public void setMinor( int minor ) {
557 this.minor = minor;
558 }
559
560
561 /***
562 * Set absolute path to file this class was read from.
563 */
564 public void setSourceFileName( String source_file_name ) {
565 this.source_file_name = source_file_name;
566 }
567
568
569 /***
570 * @param superclass_name .
571 */
572 public void setSuperclassName( String superclass_name ) {
573 this.superclass_name = superclass_name;
574 }
575
576
577 /***
578 * @param superclass_name_index .
579 */
580 public void setSuperclassNameIndex( int superclass_name_index ) {
581 this.superclass_name_index = superclass_name_index;
582 }
583
584
585 /***
586 * @return String representing class contents.
587 */
588 public String toString() {
589 String access = Utility.accessToString(access_flags, true);
590 access = access.equals("") ? "" : (access + " ");
591 StringBuffer buf = new StringBuffer(128);
592 buf.append(access).append(Utility.classOrInterface(access_flags)).append(" ").append(
593 class_name).append(" extends ").append(
594 Utility.compactClassName(superclass_name, false)).append('\n');
595 int size = interfaces.length;
596 if (size > 0) {
597 buf.append("implements\t\t");
598 for (int i = 0; i < size; i++) {
599 buf.append(interface_names[i]);
600 if (i < size - 1) {
601 buf.append(", ");
602 }
603 }
604 buf.append('\n');
605 }
606 buf.append("filename\t\t").append(file_name).append('\n');
607 buf.append("compiled from\t\t").append(source_file_name).append('\n');
608 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
609 buf.append("access flags\t\t").append(access_flags).append('\n');
610 buf.append("constant pool\t\t").append(constant_pool.getLength()).append(" entries\n");
611 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
612 if (attributes.length > 0) {
613 buf.append("\nAttribute(s):\n");
614 for (int i = 0; i < attributes.length; i++) {
615 buf.append(indent(attributes[i]));
616 }
617 }
618 if (fields.length > 0) {
619 buf.append("\n").append(fields.length).append(" fields:\n");
620 for (int i = 0; i < fields.length; i++) {
621 buf.append("\t").append(fields[i]).append('\n');
622 }
623 }
624 if (methods.length > 0) {
625 buf.append("\n").append(methods.length).append(" methods:\n");
626 for (int i = 0; i < methods.length; i++) {
627 buf.append("\t").append(methods[i]).append('\n');
628 }
629 }
630 return buf.toString();
631 }
632
633
634 private static final String indent( Object obj ) {
635 StringTokenizer tok = new StringTokenizer(obj.toString(), "\n");
636 StringBuffer buf = new StringBuffer();
637 while (tok.hasMoreTokens()) {
638 buf.append("\t").append(tok.nextToken()).append("\n");
639 }
640 return buf.toString();
641 }
642
643
644 /***
645 * @return deep copy of this class
646 */
647 public JavaClass copy() {
648 JavaClass c = null;
649 try {
650 c = (JavaClass) clone();
651 c.constant_pool = constant_pool.copy();
652 c.interfaces = (int[]) interfaces.clone();
653 c.interface_names = (String[]) interface_names.clone();
654 c.fields = new Field[fields.length];
655 for (int i = 0; i < fields.length; i++) {
656 c.fields[i] = fields[i].copy(c.constant_pool);
657 }
658 c.methods = new Method[methods.length];
659 for (int i = 0; i < methods.length; i++) {
660 c.methods[i] = methods[i].copy(c.constant_pool);
661 }
662 c.attributes = new Attribute[attributes.length];
663 for (int i = 0; i < attributes.length; i++) {
664 c.attributes[i] = attributes[i].copy(c.constant_pool);
665 }
666 } catch (CloneNotSupportedException e) {
667 }
668 return c;
669 }
670
671
672 public final boolean isSuper() {
673 return (access_flags & Constants.ACC_SUPER) != 0;
674 }
675
676
677 public final boolean isClass() {
678 return (access_flags & Constants.ACC_INTERFACE) == 0;
679 }
680
681
682 /*** @return returns either HEAP (generated), FILE, or ZIP
683 */
684 public final byte getSource() {
685 return source;
686 }
687
688
689 /********************** New repository functionality *********************/
690 /***
691 * Gets the ClassRepository which holds its definition. By default
692 * this is the same as SyntheticRepository.getInstance();
693 */
694 public org.apache.bcel.util.Repository getRepository() {
695 return repository;
696 }
697
698
699 /***
700 * Sets the ClassRepository which loaded the JavaClass.
701 * Should be called immediately after parsing is done.
702 */
703 public void setRepository( org.apache.bcel.util.Repository repository ) {
704 this.repository = repository;
705 }
706
707
708 /*** Equivalent to runtime "instanceof" operator.
709 *
710 * @return true if this JavaClass is derived from the super class
711 * @throws ClassNotFoundException if superclasses or superinterfaces
712 * of this object can't be found
713 */
714 public final boolean instanceOf( JavaClass super_class ) throws ClassNotFoundException {
715 if (this.equals(super_class)) {
716 return true;
717 }
718 JavaClass[] super_classes = getSuperClasses();
719 for (int i = 0; i < super_classes.length; i++) {
720 if (super_classes[i].equals(super_class)) {
721 return true;
722 }
723 }
724 if (super_class.isInterface()) {
725 return implementationOf(super_class);
726 }
727 return false;
728 }
729
730
731 /***
732 * @return true, if this class is an implementation of interface inter
733 * @throws ClassNotFoundException if superclasses or superinterfaces
734 * of this class can't be found
735 */
736 public boolean implementationOf( JavaClass inter ) throws ClassNotFoundException {
737 if (!inter.isInterface()) {
738 throw new IllegalArgumentException(inter.getClassName() + " is no interface");
739 }
740 if (this.equals(inter)) {
741 return true;
742 }
743 JavaClass[] super_interfaces = getAllInterfaces();
744 for (int i = 0; i < super_interfaces.length; i++) {
745 if (super_interfaces[i].equals(inter)) {
746 return true;
747 }
748 }
749 return false;
750 }
751
752
753 /***
754 * @return the superclass for this JavaClass object, or null if this
755 * is java.lang.Object
756 * @throws ClassNotFoundException if the superclass can't be found
757 */
758 public JavaClass getSuperClass() throws ClassNotFoundException {
759 if ("java.lang.Object".equals(getClassName())) {
760 return null;
761 }
762 return repository.loadClass(getSuperclassName());
763 }
764
765
766 /***
767 * @return list of super classes of this class in ascending order, i.e.,
768 * java.lang.Object is always the last element
769 * @throws ClassNotFoundException if any of the superclasses can't be found
770 */
771 public JavaClass[] getSuperClasses() throws ClassNotFoundException {
772 JavaClass clazz = this;
773 List allSuperClasses = new ArrayList();
774 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
775 allSuperClasses.add(clazz);
776 }
777 return (JavaClass[]) allSuperClasses.toArray(new JavaClass[allSuperClasses.size()]);
778 }
779
780
781 /***
782 * Get interfaces directly implemented by this JavaClass.
783 */
784 public JavaClass[] getInterfaces() throws ClassNotFoundException {
785 String[] _interfaces = getInterfaceNames();
786 JavaClass[] classes = new JavaClass[_interfaces.length];
787 for (int i = 0; i < _interfaces.length; i++) {
788 classes[i] = repository.loadClass(_interfaces[i]);
789 }
790 return classes;
791 }
792
793
794 /***
795 * Get all interfaces implemented by this JavaClass (transitively).
796 */
797 public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
798 ClassQueue queue = new ClassQueue();
799 Set allInterfaces = new TreeSet();
800 queue.enqueue(this);
801 while (!queue.empty()) {
802 JavaClass clazz = queue.dequeue();
803 JavaClass souper = clazz.getSuperClass();
804 JavaClass[] _interfaces = clazz.getInterfaces();
805 if (clazz.isInterface()) {
806 allInterfaces.add(clazz);
807 } else {
808 if (souper != null) {
809 queue.enqueue(souper);
810 }
811 }
812 for (int i = 0; i < _interfaces.length; i++) {
813 queue.enqueue(_interfaces[i]);
814 }
815 }
816 return (JavaClass[]) allInterfaces.toArray(new JavaClass[allInterfaces.size()]);
817 }
818
819
820 /***
821 * @return Comparison strategy object
822 */
823 public static BCELComparator getComparator() {
824 return _cmp;
825 }
826
827
828 /***
829 * @param comparator Comparison strategy object
830 */
831 public static void setComparator( BCELComparator comparator ) {
832 _cmp = comparator;
833 }
834
835
836 /***
837 * Return value as defined by given BCELComparator strategy.
838 * By default two JavaClass objects are said to be equal when
839 * their class names are equal.
840 *
841 * @see java.lang.Object#equals(java.lang.Object)
842 */
843 public boolean equals( Object obj ) {
844 return _cmp.equals(this, obj);
845 }
846
847
848 /***
849 * Return the natural ordering of two JavaClasses.
850 * This ordering is based on the class name
851 */
852 public int compareTo( Object obj ) {
853 return getClassName().compareTo(((JavaClass) obj).getClassName());
854 }
855
856
857 /***
858 * Return value as defined by given BCELComparator strategy.
859 * By default return the hashcode of the class name.
860 *
861 * @see java.lang.Object#hashCode()
862 */
863 public int hashCode() {
864 return _cmp.hashCode(this);
865 }
866 }