0001: /*
0002: * Copyright 2004 Brian S O'Neill
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.cojen.classfile;
0018:
0019: import java.lang.reflect.Method;
0020: import java.lang.reflect.Modifier;
0021: import java.util.Set;
0022: import java.util.HashSet;
0023: import java.util.List;
0024: import java.util.ArrayList;
0025: import java.util.Map;
0026: import java.util.HashMap;
0027: import java.io.DataInput;
0028: import java.io.DataInputStream;
0029: import java.io.DataOutput;
0030: import java.io.DataOutputStream;
0031: import java.io.InputStream;
0032: import java.io.IOException;
0033: import java.io.OutputStream;
0034: import org.cojen.classfile.attribute.Annotation;
0035: import org.cojen.classfile.attribute.AnnotationsAttr;
0036: import org.cojen.classfile.attribute.DeprecatedAttr;
0037: import org.cojen.classfile.attribute.InnerClassesAttr;
0038: import org.cojen.classfile.attribute.RuntimeInvisibleAnnotationsAttr;
0039: import org.cojen.classfile.attribute.RuntimeVisibleAnnotationsAttr;
0040: import org.cojen.classfile.attribute.SignatureAttr;
0041: import org.cojen.classfile.attribute.SourceFileAttr;
0042: import org.cojen.classfile.attribute.SyntheticAttr;
0043: import org.cojen.classfile.constant.ConstantClassInfo;
0044:
0045: /**
0046: * A class used to create Java class files. Call the writeTo method
0047: * to produce a class file.
0048: *
0049: * <p>See <i>The Java Virtual Machine Specification</i> (ISBN 0-201-63452-X)
0050: * for information on how class files are structured. Section 4.1 describes
0051: * the ClassFile structure.
0052: *
0053: * @author Brian S O'Neill
0054: */
0055: public class ClassFile {
0056: private static final int MAGIC = 0xCAFEBABE;
0057:
0058: /**
0059: * Reads a ClassFile from the given InputStream. With this method, inner
0060: * classes cannot be loaded, and custom attributes cannot be defined.
0061: *
0062: * @param in source of class file data
0063: * @throws IOException for I/O error or if classfile is invalid.
0064: * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
0065: * of range.
0066: * @throws ClassCastException if a constant pool index references the
0067: * wrong type.
0068: */
0069: public static ClassFile readFrom(InputStream in) throws IOException {
0070: return readFrom(in, null, null);
0071: }
0072:
0073: /**
0074: * Reads a ClassFile from the given DataInput. With this method, inner
0075: * classes cannot be loaded, and custom attributes cannot be defined.
0076: *
0077: * @param din source of class file data
0078: * @throws IOException for I/O error or if classfile is invalid.
0079: * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
0080: * of range.
0081: * @throws ClassCastException if a constant pool index references the
0082: * wrong type.
0083: */
0084: public static ClassFile readFrom(DataInput din) throws IOException {
0085: return readFrom(din, null, null);
0086: }
0087:
0088: /**
0089: * Reads a ClassFile from the given InputStream. A
0090: * {@link ClassFileDataLoader} may be provided, which allows inner class
0091: * definitions to be loaded. Also, an {@link AttributeFactory} may be
0092: * provided, which allows non-standard attributes to be read. All
0093: * remaining unknown attribute types are captured, but are not decoded.
0094: *
0095: * @param in source of class file data
0096: * @param loader optional loader for reading inner class definitions
0097: * @param attrFactory optional factory for reading custom attributes
0098: * @throws IOException for I/O error or if classfile is invalid.
0099: * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
0100: * of range.
0101: * @throws ClassCastException if a constant pool index references the
0102: * wrong type.
0103: */
0104: public static ClassFile readFrom(InputStream in,
0105: ClassFileDataLoader loader, AttributeFactory attrFactory)
0106: throws IOException {
0107: if (!(in instanceof DataInput)) {
0108: in = new DataInputStream(in);
0109: }
0110: return readFrom((DataInput) in, loader, attrFactory);
0111: }
0112:
0113: /**
0114: * Reads a ClassFile from the given DataInput. A
0115: * {@link ClassFileDataLoader} may be provided, which allows inner class
0116: * definitions to be loaded. Also, an {@link AttributeFactory} may be
0117: * provided, which allows non-standard attributes to be read. All
0118: * remaining unknown attribute types are captured, but are not decoded.
0119: *
0120: * @param din source of class file data
0121: * @param loader optional loader for reading inner class definitions
0122: * @param attrFactory optional factory for reading custom attributes
0123: * @throws IOException for I/O error or if classfile is invalid.
0124: * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
0125: * of range.
0126: * @throws ClassCastException if a constant pool index references the
0127: * wrong type.
0128: */
0129: public static ClassFile readFrom(DataInput din,
0130: ClassFileDataLoader loader, AttributeFactory attrFactory)
0131: throws IOException {
0132: return readFrom(din, loader, attrFactory, new HashMap(11), null);
0133: }
0134:
0135: /**
0136: * @param loadedClassFiles Maps name to ClassFiles for classes already
0137: * loaded. This prevents infinite loop: inner loads outer loads inner...
0138: */
0139: private static ClassFile readFrom(DataInput din,
0140: ClassFileDataLoader loader, AttributeFactory attrFactory,
0141: Map loadedClassFiles, ClassFile outerClass)
0142: throws IOException {
0143: int magic = din.readInt();
0144: if (magic != MAGIC) {
0145: throw new IOException("Incorrect magic number: 0x"
0146: + Integer.toHexString(magic));
0147: }
0148:
0149: short minor = din.readShort();
0150: short major = din.readShort();
0151:
0152: ConstantPool cp = ConstantPool.readFrom(din);
0153: Modifiers modifiers = Modifiers.getInstance(
0154: din.readUnsignedShort()).toSynchronized(false);
0155:
0156: int index = din.readUnsignedShort();
0157: ConstantClassInfo this Class = (ConstantClassInfo) cp
0158: .getConstant(index);
0159:
0160: index = din.readUnsignedShort();
0161: ConstantClassInfo super Class = null;
0162: if (index > 0) {
0163: super Class = (ConstantClassInfo) cp.getConstant(index);
0164: }
0165:
0166: ClassFile cf = new ClassFile(cp, modifiers, this Class,
0167: super Class, outerClass);
0168: cf.setVersion(major, minor);
0169: loadedClassFiles.put(cf.getClassName(), cf);
0170:
0171: // Read interfaces.
0172: int size = din.readUnsignedShort();
0173: for (int i = 0; i < size; i++) {
0174: index = din.readUnsignedShort();
0175: ConstantClassInfo info = (ConstantClassInfo) cp
0176: .getConstant(index);
0177: cf.addInterface(info.getType().getRootName());
0178: }
0179:
0180: // Read fields.
0181: size = din.readUnsignedShort();
0182: for (int i = 0; i < size; i++) {
0183: cf.mFields.add(FieldInfo.readFrom(cf, din, attrFactory));
0184: }
0185:
0186: // Read methods.
0187: size = din.readUnsignedShort();
0188: for (int i = 0; i < size; i++) {
0189: cf.mMethods.add(MethodInfo.readFrom(cf, din, attrFactory));
0190: }
0191:
0192: // Read attributes.
0193: size = din.readUnsignedShort();
0194: for (int i = 0; i < size; i++) {
0195: Attribute attr = Attribute.readFrom(cp, din, attrFactory);
0196: cf.addAttribute(attr);
0197: if (attr instanceof InnerClassesAttr) {
0198: cf.mInnerClassesAttr = (InnerClassesAttr) attr;
0199: }
0200: }
0201:
0202: // Load inner and outer classes.
0203: if (cf.mInnerClassesAttr != null && loader != null) {
0204: InnerClassesAttr.Info[] infos = cf.mInnerClassesAttr
0205: .getInnerClassesInfo();
0206: for (int i = 0; i < infos.length; i++) {
0207: InnerClassesAttr.Info info = infos[i];
0208:
0209: if (this Class.equals(info.getInnerClass())) {
0210: // This class is an inner class.
0211: if (info.getInnerClassName() != null) {
0212: cf.mInnerClassName = info.getInnerClassName()
0213: .getValue();
0214: }
0215: ConstantClassInfo outer = info.getOuterClass();
0216: if (cf.mOuterClass == null && outer != null) {
0217: cf.mOuterClass = readOuterClass(outer, loader,
0218: attrFactory, loadedClassFiles);
0219: }
0220: Modifiers innerFlags = info.getModifiers();
0221: cf.mModifiers = cf.mModifiers.toStatic(
0222: innerFlags.isStatic()).toPrivate(
0223: innerFlags.isPrivate()).toProtected(
0224: innerFlags.isProtected()).toPublic(
0225: innerFlags.isPublic());
0226: } else if (info.getOuterClass() == null
0227: || this Class.equals(info.getOuterClass())) {
0228:
0229: // This class is an outer class.
0230: ConstantClassInfo inner = info.getInnerClass();
0231: if (inner != null) {
0232: ClassFile innerClass = readInnerClass(inner,
0233: loader, attrFactory, loadedClassFiles,
0234: cf);
0235:
0236: if (innerClass != null) {
0237: if (innerClass.getInnerClassName() != null) {
0238: innerClass.mInnerClassName = info
0239: .getInnerClassName().getValue();
0240: }
0241: if (cf.mInnerClasses == null) {
0242: cf.mInnerClasses = new ArrayList();
0243: }
0244: cf.mInnerClasses.add(innerClass);
0245: }
0246: }
0247: }
0248: }
0249: }
0250:
0251: return cf;
0252: }
0253:
0254: private static ClassFile readOuterClass(ConstantClassInfo outer,
0255: ClassFileDataLoader loader, AttributeFactory attrFactory,
0256: Map loadedClassFiles) throws IOException {
0257: String name = outer.getType().getRootName();
0258:
0259: ClassFile outerClass = (ClassFile) loadedClassFiles.get(name);
0260: if (outerClass != null) {
0261: return outerClass;
0262: }
0263:
0264: InputStream in = loader.getClassData(name);
0265: if (in == null) {
0266: return null;
0267: }
0268:
0269: if (!(in instanceof DataInput)) {
0270: in = new DataInputStream(in);
0271: }
0272:
0273: return readFrom((DataInput) in, loader, attrFactory,
0274: loadedClassFiles, null);
0275: }
0276:
0277: private static ClassFile readInnerClass(ConstantClassInfo inner,
0278: ClassFileDataLoader loader, AttributeFactory attrFactory,
0279: Map loadedClassFiles, ClassFile outerClass)
0280: throws IOException {
0281: String name = inner.getType().getRootName();
0282:
0283: // Prevent cycles in inner class structure.
0284: for (ClassFile outer = outerClass; outer != null; outer = outer
0285: .getOuterClass()) {
0286: if (name.equals(outer.getClassName())) {
0287: // Cycle prevented.
0288: return null;
0289: }
0290: }
0291:
0292: // Prevent classes from being loaded multiple times.
0293: ClassFile innerClass = (ClassFile) loadedClassFiles.get(name);
0294: if (innerClass != null) {
0295: return innerClass;
0296: }
0297:
0298: InputStream in = loader.getClassData(name);
0299: if (in == null) {
0300: return null;
0301: }
0302:
0303: if (!(in instanceof DataInput)) {
0304: in = new DataInputStream(in);
0305: }
0306:
0307: return readFrom((DataInput) in, loader, attrFactory,
0308: loadedClassFiles, outerClass);
0309: }
0310:
0311: private int mVersion;
0312: private String mTarget;
0313: {
0314: setTarget(null);
0315: }
0316:
0317: private final String mClassName;
0318: private final String mSuperClassName;
0319: private String mInnerClassName;
0320: private TypeDesc mType;
0321:
0322: private ConstantPool mCp;
0323:
0324: private Modifiers mModifiers;
0325:
0326: private ConstantClassInfo mThisClass;
0327: private ConstantClassInfo mSuperClass;
0328:
0329: // Holds ConstantInfo objects.
0330: private List mInterfaces = new ArrayList(2);
0331: private Set mInterfaceSet = new HashSet(7);
0332:
0333: // Holds objects.
0334: private List mFields = new ArrayList();
0335: private List mMethods = new ArrayList();
0336: private List mAttributes = new ArrayList();
0337:
0338: private SourceFileAttr mSource;
0339:
0340: private List mInnerClasses;
0341: private int mAnonymousInnerClassCount = 0;
0342: private InnerClassesAttr mInnerClassesAttr;
0343:
0344: // Is non-null for inner classes.
0345: private ClassFile mOuterClass;
0346:
0347: /**
0348: * By default, the ClassFile defines public, non-final, concrete classes.
0349: * This constructor creates a ClassFile for a class that extends
0350: * java.lang.Object.
0351: * <p>
0352: * Use the {@link #setModifiers} method to change the access modifiers of
0353: * this class or to turn it into an interface.
0354: *
0355: * @param className Full class name of the form ex: "java.lang.String".
0356: */
0357: public ClassFile(String className) {
0358: this (className, (String) null);
0359: }
0360:
0361: /**
0362: * By default, the ClassFile defines public, non-final, concrete classes.
0363: * <p>
0364: * Use the {@link #setModifiers} method to change the access modifiers of
0365: * this class or to turn it into an interface.
0366: *
0367: * @param className Full class name of the form ex: "java.lang.String".
0368: * @param superClass Super class or interface.
0369: */
0370: public ClassFile(String className, Class super Class) {
0371: this (className, super Class.isInterface() ? null : super Class
0372: .getName());
0373: if (super Class.isInterface()) {
0374: addInterface(super Class);
0375: }
0376: }
0377:
0378: /**
0379: * By default, the ClassFile defines public, non-final, concrete classes.
0380: * <p>
0381: * Use the {@link #setModifiers} method to change the access modifiers of
0382: * this class or to turn it into an interface.
0383: *
0384: * @param className Full class name of the form ex: "java.lang.String".
0385: * @param superClassName Full super class name.
0386: */
0387: public ClassFile(String className, String super ClassName) {
0388: if (super ClassName == null) {
0389: if (!className.equals(Object.class.getName())) {
0390: super ClassName = Object.class.getName();
0391: }
0392: }
0393:
0394: mCp = new ConstantPool();
0395:
0396: // public, non-final, concrete class
0397: mModifiers = Modifiers.PUBLIC;
0398:
0399: mThisClass = mCp.addConstantClass(className);
0400: mSuperClass = mCp.addConstantClass(super ClassName);
0401:
0402: mClassName = className;
0403: mSuperClassName = super ClassName;
0404: }
0405:
0406: /**
0407: * Used to construct a ClassFile when read from a stream.
0408: */
0409: private ClassFile(ConstantPool cp, Modifiers modifiers,
0410: ConstantClassInfo this Class, ConstantClassInfo super Class,
0411: ClassFile outerClass) {
0412:
0413: mCp = cp;
0414:
0415: mModifiers = modifiers;
0416:
0417: mThisClass = this Class;
0418: mSuperClass = super Class;
0419:
0420: mClassName = this Class.getType().getRootName();
0421: if (super Class == null) {
0422: mSuperClassName = null;
0423: } else {
0424: mSuperClassName = super Class.getType().getRootName();
0425: }
0426:
0427: mOuterClass = outerClass;
0428: }
0429:
0430: public String getClassName() {
0431: return mClassName;
0432: }
0433:
0434: public String getSuperClassName() {
0435: return mSuperClassName;
0436: }
0437:
0438: /**
0439: * Returns a TypeDesc for the type of this ClassFile.
0440: */
0441: public TypeDesc getType() {
0442: if (mType == null) {
0443: mType = TypeDesc.forClass(mClassName);
0444: }
0445: return mType;
0446: }
0447:
0448: public Modifiers getModifiers() {
0449: return mModifiers;
0450: }
0451:
0452: /**
0453: * Returns the names of all the interfaces that this class implements.
0454: */
0455: public String[] getInterfaces() {
0456: int size = mInterfaces.size();
0457: String[] names = new String[size];
0458:
0459: for (int i = 0; i < size; i++) {
0460: names[i] = ((ConstantClassInfo) mInterfaces.get(i))
0461: .getType().getRootName();
0462: }
0463:
0464: return names;
0465: }
0466:
0467: /**
0468: * Returns all the fields defined in this class.
0469: */
0470: public FieldInfo[] getFields() {
0471: FieldInfo[] fields = new FieldInfo[mFields.size()];
0472: return (FieldInfo[]) mFields.toArray(fields);
0473: }
0474:
0475: /**
0476: * Returns all the methods defined in this class, not including
0477: * constructors and static initializers.
0478: */
0479: public MethodInfo[] getMethods() {
0480: int size = mMethods.size();
0481: List methodsOnly = new ArrayList(size);
0482:
0483: for (int i = 0; i < size; i++) {
0484: MethodInfo method = (MethodInfo) mMethods.get(i);
0485: String name = method.getName();
0486: if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
0487: methodsOnly.add(method);
0488: }
0489: }
0490:
0491: MethodInfo[] methodsArray = new MethodInfo[methodsOnly.size()];
0492: return (MethodInfo[]) methodsOnly.toArray(methodsArray);
0493: }
0494:
0495: /**
0496: * Returns all the constructors defined in this class.
0497: */
0498: public MethodInfo[] getConstructors() {
0499: int size = mMethods.size();
0500: List ctorsOnly = new ArrayList(size);
0501:
0502: for (int i = 0; i < size; i++) {
0503: MethodInfo method = (MethodInfo) mMethods.get(i);
0504: if ("<init>".equals(method.getName())) {
0505: ctorsOnly.add(method);
0506: }
0507: }
0508:
0509: MethodInfo[] ctorsArray = new MethodInfo[ctorsOnly.size()];
0510: return (MethodInfo[]) ctorsOnly.toArray(ctorsArray);
0511: }
0512:
0513: /**
0514: * Returns the static initializer defined in this class or null if there
0515: * isn't one.
0516: */
0517: public MethodInfo getInitializer() {
0518: int size = mMethods.size();
0519:
0520: for (int i = 0; i < size; i++) {
0521: MethodInfo method = (MethodInfo) mMethods.get(i);
0522: if ("<clinit>".equals(method.getName())) {
0523: return method;
0524: }
0525: }
0526:
0527: return null;
0528: }
0529:
0530: /**
0531: * Returns all the inner classes defined in this class. If no inner classes
0532: * are defined, then an array of length zero is returned.
0533: */
0534: public ClassFile[] getInnerClasses() {
0535: if (mInnerClasses == null) {
0536: return new ClassFile[0];
0537: }
0538:
0539: ClassFile[] innerClasses = new ClassFile[mInnerClasses.size()];
0540: return (ClassFile[]) mInnerClasses.toArray(innerClasses);
0541: }
0542:
0543: /**
0544: * Returns true if this ClassFile represents an inner class.
0545: */
0546: public boolean isInnerClass() {
0547: return mOuterClass != null;
0548: }
0549:
0550: /**
0551: * If this ClassFile represents a non-anonymous inner class, returns its
0552: * short inner class name.
0553: */
0554: public String getInnerClassName() {
0555: return mInnerClassName;
0556: }
0557:
0558: /**
0559: * Returns null if this ClassFile does not represent an inner class.
0560: *
0561: * @see #isInnerClass()
0562: */
0563: public ClassFile getOuterClass() {
0564: return mOuterClass;
0565: }
0566:
0567: /**
0568: * Returns a value indicating how deeply nested an inner class is with
0569: * respect to its outermost enclosing class. For top level classes, 0
0570: * is returned. For first level inner classes, 1 is returned, etc.
0571: */
0572: public int getClassDepth() {
0573: int depth = 0;
0574:
0575: ClassFile outer = mOuterClass;
0576: while (outer != null) {
0577: depth++;
0578: outer = outer.mOuterClass;
0579: }
0580:
0581: return depth;
0582: }
0583:
0584: /**
0585: * Returns the source file of this class file or null if not set.
0586: */
0587: public String getSourceFile() {
0588: if (mSource == null) {
0589: return null;
0590: } else {
0591: return mSource.getFileName().getValue();
0592: }
0593: }
0594:
0595: public boolean isSynthetic() {
0596: for (int i = mAttributes.size(); --i >= 0;) {
0597: Object obj = mAttributes.get(i);
0598: if (obj instanceof SyntheticAttr) {
0599: return true;
0600: }
0601: }
0602: return false;
0603: }
0604:
0605: public boolean isDeprecated() {
0606: for (int i = mAttributes.size(); --i >= 0;) {
0607: Object obj = mAttributes.get(i);
0608: if (obj instanceof DeprecatedAttr) {
0609: return true;
0610: }
0611: }
0612: return false;
0613: }
0614:
0615: /**
0616: * Returns all the runtime invisible annotations defined for this class
0617: * file, or an empty array if none.
0618: */
0619: public Annotation[] getRuntimeInvisibleAnnotations() {
0620: for (int i = mAttributes.size(); --i >= 0;) {
0621: Object obj = mAttributes.get(i);
0622: if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
0623: return ((AnnotationsAttr) obj).getAnnotations();
0624: }
0625: }
0626: return new Annotation[0];
0627: }
0628:
0629: /**
0630: * Returns all the runtime visible annotations defined for this class file,
0631: * or an empty array if none.
0632: */
0633: public Annotation[] getRuntimeVisibleAnnotations() {
0634: for (int i = mAttributes.size(); --i >= 0;) {
0635: Object obj = mAttributes.get(i);
0636: if (obj instanceof RuntimeVisibleAnnotationsAttr) {
0637: return ((AnnotationsAttr) obj).getAnnotations();
0638: }
0639: }
0640: return new Annotation[0];
0641: }
0642:
0643: /**
0644: * Add a runtime invisible annotation.
0645: */
0646: public Annotation addRuntimeInvisibleAnnotation(TypeDesc type) {
0647: AnnotationsAttr attr = null;
0648: for (int i = mAttributes.size(); --i >= 0;) {
0649: Object obj = mAttributes.get(i);
0650: if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
0651: attr = (AnnotationsAttr) obj;
0652: }
0653: }
0654: if (attr == null) {
0655: attr = new RuntimeInvisibleAnnotationsAttr(mCp);
0656: addAttribute(attr);
0657: }
0658: Annotation ann = new Annotation(mCp);
0659: ann.setType(type);
0660: attr.addAnnotation(ann);
0661: return ann;
0662: }
0663:
0664: /**
0665: * Add a runtime visible annotation.
0666: */
0667: public Annotation addRuntimeVisibleAnnotation(TypeDesc type) {
0668: AnnotationsAttr attr = null;
0669: for (int i = mAttributes.size(); --i >= 0;) {
0670: Object obj = mAttributes.get(i);
0671: if (obj instanceof RuntimeVisibleAnnotationsAttr) {
0672: attr = (AnnotationsAttr) obj;
0673: }
0674: }
0675: if (attr == null) {
0676: attr = new RuntimeVisibleAnnotationsAttr(mCp);
0677: addAttribute(attr);
0678: }
0679: Annotation ann = new Annotation(mCp);
0680: ann.setType(type);
0681: attr.addAnnotation(ann);
0682: return ann;
0683: }
0684:
0685: /**
0686: * Returns the signature attribute of this classfile, or null if none is
0687: * defined.
0688: */
0689: // TODO: Eventually remove this method
0690: public SignatureAttr getSignatureAttr() {
0691: for (int i = mAttributes.size(); --i >= 0;) {
0692: Object obj = mAttributes.get(i);
0693: if (obj instanceof SignatureAttr) {
0694: return (SignatureAttr) obj;
0695: }
0696: }
0697: return null;
0698: }
0699:
0700: /**
0701: * Provides access to the ClassFile's ContantPool.
0702: *
0703: * @return The constant pool for this class file.
0704: */
0705: public ConstantPool getConstantPool() {
0706: return mCp;
0707: }
0708:
0709: public void setModifiers(Modifiers modifiers) {
0710: mModifiers = modifiers;
0711: }
0712:
0713: /**
0714: * Add an interface that this class implements.
0715: *
0716: * @param interfaceName Full interface name.
0717: */
0718: public void addInterface(String interfaceName) {
0719: if (!mInterfaceSet.contains(interfaceName)) {
0720: mInterfaces.add(mCp.addConstantClass(interfaceName));
0721: mInterfaceSet.add(interfaceName);
0722: }
0723: }
0724:
0725: /**
0726: * Add an interface that this class implements.
0727: */
0728: public void addInterface(Class i) {
0729: addInterface(i.getName());
0730: }
0731:
0732: /**
0733: * Add a field to this class.
0734: */
0735: public FieldInfo addField(Modifiers modifiers, String fieldName,
0736: TypeDesc type) {
0737: FieldInfo fi = new FieldInfo(this , modifiers, fieldName, type);
0738: mFields.add(fi);
0739: return fi;
0740: }
0741:
0742: /**
0743: * Add a method to this class.
0744: *
0745: * @param ret Is null if method returns void.
0746: * @param params May be null if method accepts no parameters.
0747: */
0748: public MethodInfo addMethod(Modifiers modifiers, String methodName,
0749: TypeDesc ret, TypeDesc[] params) {
0750: MethodDesc md = MethodDesc.forArguments(ret, params);
0751: return addMethod(modifiers, methodName, md);
0752: }
0753:
0754: /**
0755: * Add a method to this class.
0756: */
0757: public MethodInfo addMethod(Modifiers modifiers, String methodName,
0758: MethodDesc md) {
0759: MethodInfo mi = new MethodInfo(this , modifiers, methodName, md);
0760: mMethods.add(mi);
0761: return mi;
0762: }
0763:
0764: /**
0765: * Add a method to this class. This method is handy for implementing
0766: * methods defined by a pre-existing interface.
0767: */
0768: public MethodInfo addMethod(Method method) {
0769: Modifiers modifiers = Modifiers.getInstance(
0770: method.getModifiers()).toAbstract(false);
0771: MethodInfo mi = addMethod(modifiers, method.getName(),
0772: MethodDesc.forMethod(method));
0773:
0774: // exception stuff...
0775: Class[] exceptions = method.getExceptionTypes();
0776: for (int i = 0; i < exceptions.length; i++) {
0777: mi.addException(TypeDesc.forClass(exceptions[i]));
0778: }
0779:
0780: return mi;
0781: }
0782:
0783: /**
0784: * Add a method to this class by declaration.
0785: *
0786: * @throws IllegalArgumentException if declaration syntax is wrong
0787: * @see MethodDeclarationParser
0788: */
0789: public MethodInfo addMethod(String declaration) {
0790: MethodDeclarationParser p = new MethodDeclarationParser(
0791: declaration);
0792: return addMethod(p.getModifiers(), p.getMethodName(), p
0793: .getReturnType(), p.getParameters());
0794: }
0795:
0796: /**
0797: * Add a constructor to this class.
0798: *
0799: * @param params May be null if constructor accepts no parameters.
0800: */
0801: public MethodInfo addConstructor(Modifiers modifiers,
0802: TypeDesc[] params) {
0803: MethodDesc md = MethodDesc.forArguments(null, params);
0804: MethodInfo mi = new MethodInfo(this , modifiers, "<init>", md);
0805: mMethods.add(mi);
0806: return mi;
0807: }
0808:
0809: /**
0810: * Adds a public, no-arg constructor with the code buffer properly defined.
0811: */
0812: public MethodInfo addDefaultConstructor() {
0813: MethodInfo mi = addConstructor(Modifiers.PUBLIC, null);
0814: CodeBuilder builder = new CodeBuilder(mi);
0815: builder.loadThis();
0816: builder.invokeSuperConstructor(null);
0817: builder.returnVoid();
0818: return mi;
0819: }
0820:
0821: /**
0822: * Add a static initializer to this class.
0823: */
0824: public MethodInfo addInitializer() {
0825: MethodDesc md = MethodDesc.forArguments(null, null);
0826: Modifiers af = Modifiers.NONE.toStatic(true);
0827: MethodInfo mi = new MethodInfo(this , af, "<clinit>", md);
0828: mMethods.add(mi);
0829: return mi;
0830: }
0831:
0832: /**
0833: * Add an inner class to this class. By default, inner classes are private
0834: * static.
0835: *
0836: * @param fullInnerClassName Optional full inner class name.
0837: * @param innerClassName Optional short inner class name.
0838: */
0839: public ClassFile addInnerClass(String fullInnerClassName,
0840: String innerClassName) {
0841: return addInnerClass(fullInnerClassName, innerClassName,
0842: (String) null);
0843: }
0844:
0845: /**
0846: * Add an inner class to this class. By default, inner classes are private
0847: * static.
0848: *
0849: * @param fullInnerClassName Optional full inner class name.
0850: * @param innerClassName Optional short inner class name.
0851: * @param superClass Super class.
0852: */
0853: public ClassFile addInnerClass(String fullInnerClassName,
0854: String innerClassName, Class super Class) {
0855: return addInnerClass(innerClassName, super Class.getName());
0856: }
0857:
0858: /**
0859: * Add an inner class to this class. By default, inner classes are private
0860: * static.
0861: *
0862: * @param fullInnerClassName Optional full inner class name.
0863: * @param innerClassName Optional short inner class name.
0864: * @param superClassName Full super class name.
0865: */
0866: public ClassFile addInnerClass(String fullInnerClassName,
0867: String innerClassName, String super ClassName) {
0868: if (fullInnerClassName == null) {
0869: if (innerClassName == null) {
0870: fullInnerClassName = mClassName + '$'
0871: + (++mAnonymousInnerClassCount);
0872: } else {
0873: fullInnerClassName = mClassName + '$' + innerClassName;
0874: }
0875: }
0876:
0877: ClassFile inner = new ClassFile(fullInnerClassName,
0878: super ClassName);
0879: Modifiers modifiers = inner.getModifiers().toPrivate(true)
0880: .toStatic(true);
0881: inner.setModifiers(modifiers);
0882: inner.mInnerClassName = innerClassName;
0883: inner.mOuterClass = this ;
0884:
0885: if (mInnerClasses == null) {
0886: mInnerClasses = new ArrayList();
0887: }
0888:
0889: mInnerClasses.add(inner);
0890:
0891: // Record the inner class in this, the outer class.
0892: if (mInnerClassesAttr == null) {
0893: addAttribute(new InnerClassesAttr(mCp));
0894: }
0895:
0896: // TODO: Anonymous inner classes and method scoped classes do not have
0897: // an outer class listed.
0898:
0899: mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName,
0900: innerClassName, modifiers);
0901:
0902: // Record the inner class in itself.
0903: inner
0904: .addAttribute(new InnerClassesAttr(inner
0905: .getConstantPool()));
0906: inner.mInnerClassesAttr.addInnerClass(fullInnerClassName,
0907: mClassName, innerClassName, modifiers);
0908:
0909: return inner;
0910: }
0911:
0912: /**
0913: * Set the source file of this class file by adding a source file
0914: * attribute. The source doesn't actually have to be a file,
0915: * but the virtual machine spec names the attribute "SourceFile_attribute".
0916: */
0917: public void setSourceFile(String fileName) {
0918: addAttribute(new SourceFileAttr(mCp, fileName));
0919: }
0920:
0921: /**
0922: * Mark this class as being synthetic by adding a special attribute.
0923: */
0924: public void markSynthetic() {
0925: addAttribute(new SyntheticAttr(mCp));
0926: }
0927:
0928: /**
0929: * Mark this class as being deprecated by adding a special attribute.
0930: */
0931: public void markDeprecated() {
0932: addAttribute(new DeprecatedAttr(mCp));
0933: }
0934:
0935: /**
0936: * Add an attribute to this class.
0937: */
0938: public void addAttribute(Attribute attr) {
0939: if (attr instanceof SourceFileAttr) {
0940: if (mSource != null) {
0941: mAttributes.remove(mSource);
0942: }
0943: mSource = (SourceFileAttr) attr;
0944: } else if (attr instanceof InnerClassesAttr) {
0945: if (mInnerClassesAttr != null) {
0946: mAttributes.remove(mInnerClassesAttr);
0947: }
0948: mInnerClassesAttr = (InnerClassesAttr) attr;
0949: }
0950:
0951: mAttributes.add(attr);
0952: }
0953:
0954: public Attribute[] getAttributes() {
0955: Attribute[] attrs = new Attribute[mAttributes.size()];
0956: return (Attribute[]) mAttributes.toArray(attrs);
0957: }
0958:
0959: /**
0960: * Specify what target virtual machine version classfile should generate
0961: * for. Calling this method changes the major and minor version of the
0962: * classfile format.
0963: *
0964: * @param target VM version, 1.0, 1.1, etc.
0965: * @throws IllegalArgumentException if target is not supported
0966: */
0967: public void setTarget(String target)
0968: throws IllegalArgumentException {
0969: int major, minor;
0970:
0971: if (target == null || "1.0".equals(target)
0972: || "1.1".equals(target)) {
0973: major = 45;
0974: minor = 3;
0975: if (target == null) {
0976: target = "1.0";
0977: }
0978: } else if ("1.2".equals(target)) {
0979: major = 46;
0980: minor = 0;
0981: } else if ("1.3".equals(target)) {
0982: major = 47;
0983: minor = 0;
0984: } else if ("1.4".equals(target)) {
0985: major = 48;
0986: minor = 0;
0987: } else if ("1.5".equals(target)) {
0988: major = 49;
0989: minor = 0;
0990: } else if ("1.6".equals(target)) {
0991: major = 50;
0992: minor = 0;
0993: } else {
0994: throw new IllegalArgumentException(
0995: "Unsupported target version: " + target);
0996: }
0997:
0998: mVersion = (minor << 16) | (major & 0xffff);
0999: mTarget = target.intern();
1000: }
1001:
1002: /**
1003: * Returns the target virtual machine version, or null if unknown.
1004: */
1005: public String getTarget() {
1006: return mTarget;
1007: }
1008:
1009: /**
1010: * Sets the version to use when writing the generated classfile, overriding
1011: * the target.
1012: */
1013: public void setVersion(int major, int minor) {
1014: if (major > 65535 || minor > 65535) {
1015: throw new IllegalArgumentException(
1016: "Version number element cannot exceed 65535");
1017: }
1018:
1019: mVersion = (minor << 16) | (major & 0xffff);
1020:
1021: String target;
1022: switch (major) {
1023: default:
1024: target = null;
1025: break;
1026: case 45:
1027: target = minor == 3 ? "1.0" : null;
1028: break;
1029: case 46:
1030: target = minor == 0 ? "1.2" : null;
1031: break;
1032: case 47:
1033: target = minor == 0 ? "1.3" : null;
1034: break;
1035: case 48:
1036: target = minor == 0 ? "1.4" : null;
1037: break;
1038: case 49:
1039: target = minor == 0 ? "1.5" : null;
1040: break;
1041: case 50:
1042: target = minor == 0 ? "1.6" : null;
1043: break;
1044: }
1045:
1046: mTarget = target;
1047: }
1048:
1049: /**
1050: * Returns the major version number of the classfile format.
1051: */
1052: public int getMajorVersion() {
1053: return mVersion & 0xffff;
1054: }
1055:
1056: /**
1057: * Returns the minor version number of the classfile format.
1058: */
1059: public int getMinorVersion() {
1060: return (mVersion >> 16) & 0xffff;
1061: }
1062:
1063: /**
1064: * Writes the ClassFile to the given OutputStream.
1065: */
1066: public void writeTo(OutputStream out) throws IOException {
1067: if (!(out instanceof DataOutput)) {
1068: out = new DataOutputStream(out);
1069: }
1070: writeTo((DataOutput) out);
1071: }
1072:
1073: /**
1074: * Writes the ClassFile to the given DataOutput.
1075: */
1076: public void writeTo(DataOutput dout) throws IOException {
1077: dout.writeInt(MAGIC);
1078: dout.writeInt(mVersion);
1079:
1080: mCp.writeTo(dout);
1081:
1082: {
1083: int flags = mModifiers.getBitmask();
1084: if (!mModifiers.isInterface()) {
1085: // Set the ACC_SUPER flag for classes only.
1086: flags |= Modifier.SYNCHRONIZED;
1087: }
1088: dout.writeShort(flags);
1089: }
1090:
1091: dout.writeShort(mThisClass.getIndex());
1092: if (mSuperClass != null) {
1093: dout.writeShort(mSuperClass.getIndex());
1094: } else {
1095: dout.writeShort(0);
1096: }
1097:
1098: int size = mInterfaces.size();
1099: if (size > 65535) {
1100: throw new IllegalStateException(
1101: "Interfaces count cannot exceed 65535: " + size);
1102: }
1103: dout.writeShort(size);
1104: for (int i = 0; i < size; i++) {
1105: int index = ((ConstantInfo) mInterfaces.get(i)).getIndex();
1106: dout.writeShort(index);
1107: }
1108:
1109: size = mFields.size();
1110: if (size > 65535) {
1111: throw new IllegalStateException(
1112: "Field count cannot exceed 65535: " + size);
1113: }
1114: dout.writeShort(size);
1115: for (int i = 0; i < size; i++) {
1116: FieldInfo field = (FieldInfo) mFields.get(i);
1117: field.writeTo(dout);
1118: }
1119:
1120: size = mMethods.size();
1121: if (size > 65535) {
1122: throw new IllegalStateException(
1123: "Method count cannot exceed 65535: " + size);
1124: }
1125: dout.writeShort(size);
1126: for (int i = 0; i < size; i++) {
1127: MethodInfo method = (MethodInfo) mMethods.get(i);
1128: method.writeTo(dout);
1129: }
1130:
1131: size = mAttributes.size();
1132: if (size > 65535) {
1133: throw new IllegalStateException(
1134: "Attribute count cannot exceed 65535: " + size);
1135: }
1136: dout.writeShort(size);
1137: for (int i = 0; i < size; i++) {
1138: Attribute attr = (Attribute) mAttributes.get(i);
1139: attr.writeTo(dout);
1140: }
1141: }
1142:
1143: public String toString() {
1144: StringBuffer buf = new StringBuffer();
1145: String modStr = mModifiers.toString();
1146: if (modStr.length() > 0) {
1147: buf.append(modStr);
1148: buf.append(' ');
1149: }
1150: if (getModifiers().isInterface()) {
1151: buf.append("interface");
1152: } else {
1153: buf.append("class");
1154: }
1155: buf.append(' ');
1156: buf.append(getClassName());
1157:
1158: return buf.toString();
1159: }
1160: }
|