001: /*
002: * Copyright 2000-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017: package org.apache.bcel.classfile;
018:
019: import java.io.ByteArrayOutputStream;
020: import java.io.DataOutputStream;
021: import java.io.File;
022: import java.io.FileOutputStream;
023: import java.io.IOException;
024: import java.io.OutputStream;
025: import java.util.ArrayList;
026: import java.util.List;
027: import java.util.Set;
028: import java.util.StringTokenizer;
029: import java.util.TreeSet;
030: import org.apache.bcel.Constants;
031: import org.apache.bcel.generic.Type;
032: import org.apache.bcel.util.BCELComparator;
033: import org.apache.bcel.util.ClassQueue;
034: import org.apache.bcel.util.SyntheticRepository;
035:
036: /**
037: * Represents a Java class, i.e., the data structures, constant pool,
038: * fields, methods and commands contained in a Java .class file.
039: * See <a href="ftp://java.sun.com/docs/specs/">JVM specification</a> for details.
040: * The intent of this class is to represent a parsed or otherwise existing
041: * class file. Those interested in programatically generating classes
042: * should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
043:
044: * @version $Id: JavaClass.java 386056 2006-03-15 11:31:56Z tcurdt $
045: * @see org.apache.bcel.generic.ClassGen
046: * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
047: */
048: public class JavaClass extends AccessFlags implements Cloneable, Node,
049: Comparable {
050:
051: private String file_name;
052: private String package_name;
053: private String source_file_name = "<Unknown>";
054: private int class_name_index;
055: private int super class_name_index;
056: private String class_name;
057: private String super class_name;
058: private int major, minor; // Compiler version
059: private ConstantPool constant_pool; // Constant pool
060: private int[] interfaces; // implemented interfaces
061: private String[] interface_names;
062: private Field[] fields; // Fields, i.e., variables of class
063: private Method[] methods; // methods defined in the class
064: private Attribute[] attributes; // attributes defined in the class
065: private byte source = HEAP; // Generated in memory
066: public static final byte HEAP = 1;
067: public static final byte FILE = 2;
068: public static final byte ZIP = 3;
069: static boolean debug = false; // Debugging on/off
070: static char sep = '/'; // directory separator
071: private static BCELComparator _cmp = new BCELComparator() {
072:
073: public boolean equals(Object o1, Object o2) {
074: JavaClass THIS = (JavaClass) o1;
075: JavaClass THAT = (JavaClass) o2;
076: return THIS.getClassName().equals(THAT.getClassName());
077: }
078:
079: public int hashCode(Object o) {
080: JavaClass THIS = (JavaClass) o;
081: return THIS.getClassName().hashCode();
082: }
083: };
084: /**
085: * In cases where we go ahead and create something,
086: * use the default SyntheticRepository, because we
087: * don't know any better.
088: */
089: private transient org.apache.bcel.util.Repository repository = SyntheticRepository
090: .getInstance();
091:
092: /**
093: * Constructor gets all contents as arguments.
094: *
095: * @param class_name_index Index into constant pool referencing a
096: * ConstantClass that represents this class.
097: * @param superclass_name_index Index into constant pool referencing a
098: * ConstantClass that represents this class's superclass.
099: * @param file_name File name
100: * @param major Major compiler version
101: * @param minor Minor compiler version
102: * @param access_flags Access rights defined by bit flags
103: * @param constant_pool Array of constants
104: * @param interfaces Implemented interfaces
105: * @param fields Class fields
106: * @param methods Class methods
107: * @param attributes Class attributes
108: * @param source Read from file or generated in memory?
109: */
110: public JavaClass(int class_name_index, int super class_name_index,
111: String file_name, int major, int minor, int access_flags,
112: ConstantPool constant_pool, int[] interfaces,
113: Field[] fields, Method[] methods, Attribute[] attributes,
114: byte source) {
115: if (interfaces == null) {
116: interfaces = new int[0];
117: }
118: if (attributes == null) {
119: attributes = new Attribute[0];
120: }
121: if (fields == null) {
122: fields = new Field[0];
123: }
124: if (methods == null) {
125: methods = new Method[0];
126: }
127: this .class_name_index = class_name_index;
128: this .super class_name_index = super class_name_index;
129: this .file_name = file_name;
130: this .major = major;
131: this .minor = minor;
132: this .access_flags = access_flags;
133: this .constant_pool = constant_pool;
134: this .interfaces = interfaces;
135: this .fields = fields;
136: this .methods = methods;
137: this .attributes = attributes;
138: this .source = source;
139: // Get source file name if available
140: for (int i = 0; i < attributes.length; i++) {
141: if (attributes[i] instanceof SourceFile) {
142: source_file_name = ((SourceFile) attributes[i])
143: .getSourceFileName();
144: break;
145: }
146: }
147: /* According to the specification the following entries must be of type
148: * `ConstantClass' but we check that anyway via the
149: * `ConstPool.getConstant' method.
150: */
151: class_name = constant_pool.getConstantString(class_name_index,
152: Constants.CONSTANT_Class);
153: class_name = Utility.compactClassName(class_name, false);
154: int index = class_name.lastIndexOf('.');
155: if (index < 0) {
156: package_name = "";
157: } else {
158: package_name = class_name.substring(0, index);
159: }
160: if (super class_name_index > 0) {
161: // May be zero -> class is java.lang.Object
162: super class_name = constant_pool.getConstantString(
163: super class_name_index, Constants.CONSTANT_Class);
164: super class_name = Utility.compactClassName(super class_name,
165: false);
166: } else {
167: super class_name = "java.lang.Object";
168: }
169: interface_names = new String[interfaces.length];
170: for (int i = 0; i < interfaces.length; i++) {
171: String str = constant_pool.getConstantString(interfaces[i],
172: Constants.CONSTANT_Class);
173: interface_names[i] = Utility.compactClassName(str, false);
174: }
175: }
176:
177: /**
178: * Constructor gets all contents as arguments.
179: *
180: * @param class_name_index Class name
181: * @param superclass_name_index Superclass name
182: * @param file_name File name
183: * @param major Major compiler version
184: * @param minor Minor compiler version
185: * @param access_flags Access rights defined by bit flags
186: * @param constant_pool Array of constants
187: * @param interfaces Implemented interfaces
188: * @param fields Class fields
189: * @param methods Class methods
190: * @param attributes Class attributes
191: */
192: public JavaClass(int class_name_index, int super class_name_index,
193: String file_name, int major, int minor, int access_flags,
194: ConstantPool constant_pool, int[] interfaces,
195: Field[] fields, Method[] methods, Attribute[] attributes) {
196: this (class_name_index, super class_name_index, file_name, major,
197: minor, access_flags, constant_pool, interfaces, fields,
198: methods, attributes, HEAP);
199: }
200:
201: /**
202: * Called by objects that are traversing the nodes of the tree implicitely
203: * defined by the contents of a Java class. I.e., the hierarchy of methods,
204: * fields, attributes, etc. spawns a tree of objects.
205: *
206: * @param v Visitor object
207: */
208: public void accept(Visitor v) {
209: v.visitJavaClass(this );
210: }
211:
212: /* Print debug information depending on `JavaClass.debug'
213: */
214: static final void Debug(String str) {
215: if (debug) {
216: System.out.println(str);
217: }
218: }
219:
220: /**
221: * Dump class to a file.
222: *
223: * @param file Output file
224: * @throws IOException
225: */
226: public void dump(File file) throws IOException {
227: String parent = file.getParent();
228: if (parent != null) {
229: File dir = new File(parent);
230: dir.mkdirs();
231: }
232: DataOutputStream dos = null;
233: try {
234: dos = new DataOutputStream(new FileOutputStream(file));
235: dump(dos);
236: } finally {
237: if (dos != null) {
238: dos.close();
239: }
240: }
241: }
242:
243: /**
244: * Dump class to a file named file_name.
245: *
246: * @param _file_name Output file name
247: * @exception IOException
248: */
249: public void dump(String _file_name) throws IOException {
250: dump(new File(_file_name));
251: }
252:
253: /**
254: * @return class in binary format
255: */
256: public byte[] getBytes() {
257: ByteArrayOutputStream s = new ByteArrayOutputStream();
258: DataOutputStream ds = new DataOutputStream(s);
259: try {
260: dump(ds);
261: } catch (IOException e) {
262: e.printStackTrace();
263: } finally {
264: try {
265: ds.close();
266: } catch (IOException e2) {
267: e2.printStackTrace();
268: }
269: }
270: return s.toByteArray();
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: * Dump Java class to output stream in binary format.
285: *
286: * @param file Output stream
287: * @exception IOException
288: */
289: public void dump(DataOutputStream file) throws IOException {
290: file.writeInt(0xcafebabe);
291: file.writeShort(minor);
292: file.writeShort(major);
293: constant_pool.dump(file);
294: file.writeShort(access_flags);
295: file.writeShort(class_name_index);
296: file.writeShort(super class_name_index);
297: file.writeShort(interfaces.length);
298: for (int i = 0; i < interfaces.length; i++) {
299: file.writeShort(interfaces[i]);
300: }
301: file.writeShort(fields.length);
302: for (int i = 0; i < fields.length; i++) {
303: fields[i].dump(file);
304: }
305: file.writeShort(methods.length);
306: for (int i = 0; i < methods.length; i++) {
307: methods[i].dump(file);
308: }
309: if (attributes != null) {
310: file.writeShort(attributes.length);
311: for (int i = 0; i < attributes.length; i++) {
312: attributes[i].dump(file);
313: }
314: } else {
315: file.writeShort(0);
316: }
317: file.flush();
318: }
319:
320: /**
321: * @return Attributes of the class.
322: */
323: public Attribute[] getAttributes() {
324: return attributes;
325: }
326:
327: /**
328: * @return Class name.
329: */
330: public String getClassName() {
331: return class_name;
332: }
333:
334: /**
335: * @return Package name.
336: */
337: public String getPackageName() {
338: return package_name;
339: }
340:
341: /**
342: * @return Class name index.
343: */
344: public int getClassNameIndex() {
345: return class_name_index;
346: }
347:
348: /**
349: * @return Constant pool.
350: */
351: public ConstantPool getConstantPool() {
352: return constant_pool;
353: }
354:
355: /**
356: * @return Fields, i.e., variables of the class. Like the JVM spec
357: * mandates for the classfile format, these fields are those specific to
358: * this class, and not those of the superclass or superinterfaces.
359: */
360: public Field[] getFields() {
361: return fields;
362: }
363:
364: /**
365: * @return File name of class, aka SourceFile attribute value
366: */
367: public String getFileName() {
368: return file_name;
369: }
370:
371: /**
372: * @return Names of implemented interfaces.
373: */
374: public String[] getInterfaceNames() {
375: return interface_names;
376: }
377:
378: /**
379: * @return Indices in constant pool of implemented interfaces.
380: */
381: public int[] getInterfaceIndices() {
382: return interfaces;
383: }
384:
385: /**
386: * @return Major number of class file version.
387: */
388: public int getMajor() {
389: return major;
390: }
391:
392: /**
393: * @return Methods of the class.
394: */
395: public Method[] getMethods() {
396: return methods;
397: }
398:
399: /**
400: * @return A org.apache.bcel.classfile.Method corresponding to
401: * java.lang.reflect.Method if any
402: */
403: public Method getMethod(java.lang.reflect.Method m) {
404: for (int i = 0; i < methods.length; i++) {
405: Method method = methods[i];
406: if (m.getName().equals(method.getName())
407: && (m.getModifiers() == method.getModifiers())
408: && Type.getSignature(m).equals(
409: method.getSignature())) {
410: return method;
411: }
412: }
413: return null;
414: }
415:
416: /**
417: * @return Minor number of class file version.
418: */
419: public int getMinor() {
420: return minor;
421: }
422:
423: /**
424: * @return sbsolute path to file where this class was read from
425: */
426: public String getSourceFileName() {
427: return source_file_name;
428: }
429:
430: /**
431: * @return Superclass name.
432: */
433: public String getSuperclassName() {
434: return super class_name;
435: }
436:
437: /**
438: * @return Class name index.
439: */
440: public int getSuperclassNameIndex() {
441: return super class_name_index;
442: }
443:
444: static {
445: // Debugging ... on/off
446: debug = Boolean.getBoolean("JavaClass.debug");
447: // Get path separator either / or \ usually
448: String _sep = System.getProperty("file.separator");
449: if (_sep != null) {
450: try {
451: JavaClass.sep = _sep.charAt(0);
452: } catch (StringIndexOutOfBoundsException e) {
453: } // Never reached
454: }
455: }
456:
457: /**
458: * @param attributes .
459: */
460: public void setAttributes(Attribute[] attributes) {
461: this .attributes = attributes;
462: }
463:
464: /**
465: * @param class_name .
466: */
467: public void setClassName(String class_name) {
468: this .class_name = class_name;
469: }
470:
471: /**
472: * @param class_name_index .
473: */
474: public void setClassNameIndex(int class_name_index) {
475: this .class_name_index = class_name_index;
476: }
477:
478: /**
479: * @param constant_pool .
480: */
481: public void setConstantPool(ConstantPool constant_pool) {
482: this .constant_pool = constant_pool;
483: }
484:
485: /**
486: * @param fields .
487: */
488: public void setFields(Field[] fields) {
489: this .fields = fields;
490: }
491:
492: /**
493: * Set File name of class, aka SourceFile attribute value
494: */
495: public void setFileName(String file_name) {
496: this .file_name = file_name;
497: }
498:
499: /**
500: * @param interface_names .
501: */
502: public void setInterfaceNames(String[] interface_names) {
503: this .interface_names = interface_names;
504: }
505:
506: /**
507: * @param interfaces .
508: */
509: public void setInterfaces(int[] interfaces) {
510: this .interfaces = interfaces;
511: }
512:
513: /**
514: * @param major .
515: */
516: public void setMajor(int major) {
517: this .major = major;
518: }
519:
520: /**
521: * @param methods .
522: */
523: public void setMethods(Method[] methods) {
524: this .methods = methods;
525: }
526:
527: /**
528: * @param minor .
529: */
530: public void setMinor(int minor) {
531: this .minor = minor;
532: }
533:
534: /**
535: * Set absolute path to file this class was read from.
536: */
537: public void setSourceFileName(String source_file_name) {
538: this .source_file_name = source_file_name;
539: }
540:
541: /**
542: * @param superclass_name .
543: */
544: public void setSuperclassName(String super class_name) {
545: this .super class_name = super class_name;
546: }
547:
548: /**
549: * @param superclass_name_index .
550: */
551: public void setSuperclassNameIndex(int super class_name_index) {
552: this .super class_name_index = super class_name_index;
553: }
554:
555: /**
556: * @return String representing class contents.
557: */
558: public String toString() {
559: String access = Utility.accessToString(access_flags, true);
560: access = access.equals("") ? "" : (access + " ");
561: StringBuffer buf = new StringBuffer(128);
562: buf.append(access).append(
563: Utility.classOrInterface(access_flags)).append(" ")
564: .append(class_name).append(" extends ").append(
565: Utility
566: .compactClassName(super class_name,
567: false)).append('\n');
568: int size = interfaces.length;
569: if (size > 0) {
570: buf.append("implements\t\t");
571: for (int i = 0; i < size; i++) {
572: buf.append(interface_names[i]);
573: if (i < size - 1) {
574: buf.append(", ");
575: }
576: }
577: buf.append('\n');
578: }
579: buf.append("filename\t\t").append(file_name).append('\n');
580: buf.append("compiled from\t\t").append(source_file_name)
581: .append('\n');
582: buf.append("compiler version\t").append(major).append(".")
583: .append(minor).append('\n');
584: buf.append("access flags\t\t").append(access_flags)
585: .append('\n');
586: buf.append("constant pool\t\t").append(
587: constant_pool.getLength()).append(" entries\n");
588: buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
589: if (attributes.length > 0) {
590: buf.append("\nAttribute(s):\n");
591: for (int i = 0; i < attributes.length; i++) {
592: buf.append(indent(attributes[i]));
593: }
594: }
595: if (fields.length > 0) {
596: buf.append("\n").append(fields.length).append(" fields:\n");
597: for (int i = 0; i < fields.length; i++) {
598: buf.append("\t").append(fields[i]).append('\n');
599: }
600: }
601: if (methods.length > 0) {
602: buf.append("\n").append(methods.length).append(
603: " methods:\n");
604: for (int i = 0; i < methods.length; i++) {
605: buf.append("\t").append(methods[i]).append('\n');
606: }
607: }
608: return buf.toString();
609: }
610:
611: private static final String indent(Object obj) {
612: StringTokenizer tok = new StringTokenizer(obj.toString(), "\n");
613: StringBuffer buf = new StringBuffer();
614: while (tok.hasMoreTokens()) {
615: buf.append("\t").append(tok.nextToken()).append("\n");
616: }
617: return buf.toString();
618: }
619:
620: /**
621: * @return deep copy of this class
622: */
623: public JavaClass copy() {
624: JavaClass c = null;
625: try {
626: c = (JavaClass) clone();
627: c.constant_pool = constant_pool.copy();
628: c.interfaces = (int[]) interfaces.clone();
629: c.interface_names = (String[]) interface_names.clone();
630: c.fields = new Field[fields.length];
631: for (int i = 0; i < fields.length; i++) {
632: c.fields[i] = fields[i].copy(c.constant_pool);
633: }
634: c.methods = new Method[methods.length];
635: for (int i = 0; i < methods.length; i++) {
636: c.methods[i] = methods[i].copy(c.constant_pool);
637: }
638: c.attributes = new Attribute[attributes.length];
639: for (int i = 0; i < attributes.length; i++) {
640: c.attributes[i] = attributes[i].copy(c.constant_pool);
641: }
642: } catch (CloneNotSupportedException e) {
643: }
644: return c;
645: }
646:
647: public final boolean isSuper() {
648: return (access_flags & Constants.ACC_SUPER) != 0;
649: }
650:
651: public final boolean isClass() {
652: return (access_flags & Constants.ACC_INTERFACE) == 0;
653: }
654:
655: /** @return returns either HEAP (generated), FILE, or ZIP
656: */
657: public final byte getSource() {
658: return source;
659: }
660:
661: /********************* New repository functionality *********************/
662: /**
663: * Gets the ClassRepository which holds its definition. By default
664: * this is the same as SyntheticRepository.getInstance();
665: */
666: public org.apache.bcel.util.Repository getRepository() {
667: return repository;
668: }
669:
670: /**
671: * Sets the ClassRepository which loaded the JavaClass.
672: * Should be called immediately after parsing is done.
673: */
674: public void setRepository(org.apache.bcel.util.Repository repository) {
675: this .repository = repository;
676: }
677:
678: /** Equivalent to runtime "instanceof" operator.
679: *
680: * @return true if this JavaClass is derived from the super class
681: * @throws ClassNotFoundException if superclasses or superinterfaces
682: * of this object can't be found
683: */
684: public final boolean instanceOf(JavaClass super _class)
685: throws ClassNotFoundException {
686: if (this .equals(super _class)) {
687: return true;
688: }
689: JavaClass[] super _classes = getSuperClasses();
690: for (int i = 0; i < super _classes.length; i++) {
691: if (super _classes[i].equals(super _class)) {
692: return true;
693: }
694: }
695: if (super _class.isInterface()) {
696: return implementationOf(super _class);
697: }
698: return false;
699: }
700:
701: /**
702: * @return true, if this class is an implementation of interface inter
703: * @throws ClassNotFoundException if superclasses or superinterfaces
704: * of this class can't be found
705: */
706: public boolean implementationOf(JavaClass inter)
707: throws ClassNotFoundException {
708: if (!inter.isInterface()) {
709: throw new IllegalArgumentException(inter.getClassName()
710: + " is no interface");
711: }
712: if (this .equals(inter)) {
713: return true;
714: }
715: JavaClass[] super _interfaces = getAllInterfaces();
716: for (int i = 0; i < super _interfaces.length; i++) {
717: if (super _interfaces[i].equals(inter)) {
718: return true;
719: }
720: }
721: return false;
722: }
723:
724: /**
725: * @return the superclass for this JavaClass object, or null if this
726: * is java.lang.Object
727: * @throws ClassNotFoundException if the superclass can't be found
728: */
729: public JavaClass getSuperClass() throws ClassNotFoundException {
730: if ("java.lang.Object".equals(getClassName())) {
731: return null;
732: }
733: return repository.loadClass(getSuperclassName());
734: }
735:
736: /**
737: * @return list of super classes of this class in ascending order, i.e.,
738: * java.lang.Object is always the last element
739: * @throws ClassNotFoundException if any of the superclasses can't be found
740: */
741: public JavaClass[] getSuperClasses() throws ClassNotFoundException {
742: JavaClass clazz = this ;
743: List allSuperClasses = new ArrayList();
744: for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz
745: .getSuperClass()) {
746: allSuperClasses.add(clazz);
747: }
748: return (JavaClass[]) allSuperClasses
749: .toArray(new JavaClass[allSuperClasses.size()]);
750: }
751:
752: /**
753: * Get interfaces directly implemented by this JavaClass.
754: */
755: public JavaClass[] getInterfaces() throws ClassNotFoundException {
756: String[] _interfaces = getInterfaceNames();
757: JavaClass[] classes = new JavaClass[_interfaces.length];
758: for (int i = 0; i < _interfaces.length; i++) {
759: classes[i] = repository.loadClass(_interfaces[i]);
760: }
761: return classes;
762: }
763:
764: /**
765: * Get all interfaces implemented by this JavaClass (transitively).
766: */
767: public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
768: ClassQueue queue = new ClassQueue();
769: Set allInterfaces = new TreeSet();
770: queue.enqueue(this );
771: while (!queue.empty()) {
772: JavaClass clazz = queue.dequeue();
773: JavaClass souper = clazz.getSuperClass();
774: JavaClass[] _interfaces = clazz.getInterfaces();
775: if (clazz.isInterface()) {
776: allInterfaces.add(clazz);
777: } else {
778: if (souper != null) {
779: queue.enqueue(souper);
780: }
781: }
782: for (int i = 0; i < _interfaces.length; i++) {
783: queue.enqueue(_interfaces[i]);
784: }
785: }
786: return (JavaClass[]) allInterfaces
787: .toArray(new JavaClass[allInterfaces.size()]);
788: }
789:
790: /**
791: * @return Comparison strategy object
792: */
793: public static BCELComparator getComparator() {
794: return _cmp;
795: }
796:
797: /**
798: * @param comparator Comparison strategy object
799: */
800: public static void setComparator(BCELComparator comparator) {
801: _cmp = comparator;
802: }
803:
804: /**
805: * Return value as defined by given BCELComparator strategy.
806: * By default two JavaClass objects are said to be equal when
807: * their class names are equal.
808: *
809: * @see java.lang.Object#equals(java.lang.Object)
810: */
811: public boolean equals(Object obj) {
812: return _cmp.equals(this , obj);
813: }
814:
815: /**
816: * Return the natural ordering of two JavaClasses.
817: * This ordering is based on the class name
818: */
819: public int compareTo(Object obj) {
820: return getClassName().compareTo(
821: ((JavaClass) obj).getClassName());
822: }
823:
824: /**
825: * Return value as defined by given BCELComparator strategy.
826: * By default return the hashcode of the class name.
827: *
828: * @see java.lang.Object#hashCode()
829: */
830: public int hashCode() {
831: return _cmp.hashCode(this);
832: }
833: }
|