KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cojen > classfile > ClassFile


1 /*
2  * Copyright 2004 Brian S O'Neill
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.cojen.classfile;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.Set JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.io.DataInput JavaDoc;
28 import java.io.DataInputStream JavaDoc;
29 import java.io.DataOutput JavaDoc;
30 import java.io.DataOutputStream JavaDoc;
31 import java.io.InputStream JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.io.OutputStream JavaDoc;
34 import org.cojen.classfile.attribute.Annotation;
35 import org.cojen.classfile.attribute.AnnotationsAttr;
36 import org.cojen.classfile.attribute.DeprecatedAttr;
37 import org.cojen.classfile.attribute.InnerClassesAttr;
38 import org.cojen.classfile.attribute.RuntimeInvisibleAnnotationsAttr;
39 import org.cojen.classfile.attribute.RuntimeVisibleAnnotationsAttr;
40 import org.cojen.classfile.attribute.SignatureAttr;
41 import org.cojen.classfile.attribute.SourceFileAttr;
42 import org.cojen.classfile.attribute.SyntheticAttr;
43 import org.cojen.classfile.constant.ConstantClassInfo;
44
45 /**
46  * A class used to create Java class files. Call the writeTo method
47  * to produce a class file.
48  *
49  * <p>See <i>The Java Virtual Machine Specification</i> (ISBN 0-201-63452-X)
50  * for information on how class files are structured. Section 4.1 describes
51  * the ClassFile structure.
52  *
53  * @author Brian S O'Neill
54  */

55 public class ClassFile {
56     private static final int MAGIC = 0xCAFEBABE;
57
58     /**
59      * Reads a ClassFile from the given InputStream. With this method, inner
60      * classes cannot be loaded, and custom attributes cannot be defined.
61      *
62      * @param in source of class file data
63      * @throws IOException for I/O error or if classfile is invalid.
64      * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
65      * of range.
66      * @throws ClassCastException if a constant pool index references the
67      * wrong type.
68      */

69     public static ClassFile readFrom(InputStream JavaDoc in) throws IOException JavaDoc {
70         return readFrom(in, null, null);
71     }
72
73     /**
74      * Reads a ClassFile from the given DataInput. With this method, inner
75      * classes cannot be loaded, and custom attributes cannot be defined.
76      *
77      * @param din source of class file data
78      * @throws IOException for I/O error or if classfile is invalid.
79      * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
80      * of range.
81      * @throws ClassCastException if a constant pool index references the
82      * wrong type.
83      */

84     public static ClassFile readFrom(DataInput JavaDoc din) throws IOException JavaDoc {
85         return readFrom(din, null, null);
86     }
87
88     /**
89      * Reads a ClassFile from the given InputStream. A
90      * {@link ClassFileDataLoader} may be provided, which allows inner class
91      * definitions to be loaded. Also, an {@link AttributeFactory} may be
92      * provided, which allows non-standard attributes to be read. All
93      * remaining unknown attribute types are captured, but are not decoded.
94      *
95      * @param in source of class file data
96      * @param loader optional loader for reading inner class definitions
97      * @param attrFactory optional factory for reading custom attributes
98      * @throws IOException for I/O error or if classfile is invalid.
99      * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
100      * of range.
101      * @throws ClassCastException if a constant pool index references the
102      * wrong type.
103      */

104     public static ClassFile readFrom(InputStream JavaDoc in,
105                                      ClassFileDataLoader loader,
106                                      AttributeFactory attrFactory)
107         throws IOException JavaDoc
108     {
109         if (!(in instanceof DataInput JavaDoc)) {
110             in = new DataInputStream JavaDoc(in);
111         }
112         return readFrom((DataInput JavaDoc)in, loader, attrFactory);
113     }
114
115     /**
116      * Reads a ClassFile from the given DataInput. A
117      * {@link ClassFileDataLoader} may be provided, which allows inner class
118      * definitions to be loaded. Also, an {@link AttributeFactory} may be
119      * provided, which allows non-standard attributes to be read. All
120      * remaining unknown attribute types are captured, but are not decoded.
121      *
122      * @param din source of class file data
123      * @param loader optional loader for reading inner class definitions
124      * @param attrFactory optional factory for reading custom attributes
125      * @throws IOException for I/O error or if classfile is invalid.
126      * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
127      * of range.
128      * @throws ClassCastException if a constant pool index references the
129      * wrong type.
130      */

131     public static ClassFile readFrom(DataInput JavaDoc din,
132                                      ClassFileDataLoader loader,
133                                      AttributeFactory attrFactory)
134         throws IOException JavaDoc
135     {
136         return readFrom(din, loader, attrFactory, new HashMap JavaDoc(11), null);
137     }
138
139     /**
140      * @param loadedClassFiles Maps name to ClassFiles for classes already
141      * loaded. This prevents infinite loop: inner loads outer loads inner...
142      */

143     private static ClassFile readFrom(DataInput JavaDoc din,
144                                       ClassFileDataLoader loader,
145                                       AttributeFactory attrFactory,
146                                       Map JavaDoc loadedClassFiles,
147                                       ClassFile outerClass)
148         throws IOException JavaDoc
149     {
150         int magic = din.readInt();
151         if (magic != MAGIC) {
152             throw new IOException JavaDoc("Incorrect magic number: 0x" +
153                                   Integer.toHexString(magic));
154         }
155
156         short minor = din.readShort();
157         short major = din.readShort();
158
159         ConstantPool cp = ConstantPool.readFrom(din);
160         Modifiers modifiers = Modifiers.getInstance(din.readUnsignedShort())
161             .toSynchronized(false);
162
163         int index = din.readUnsignedShort();
164         ConstantClassInfo thisClass = (ConstantClassInfo)cp.getConstant(index);
165
166         index = din.readUnsignedShort();
167         ConstantClassInfo superClass = null;
168         if (index > 0) {
169             superClass = (ConstantClassInfo)cp.getConstant(index);
170         }
171
172         ClassFile cf = new ClassFile(cp, modifiers, thisClass, superClass, outerClass);
173         cf.setVersion(major, minor);
174         loadedClassFiles.put(cf.getClassName(), cf);
175
176         // Read interfaces.
177
int size = din.readUnsignedShort();
178         for (int i=0; i<size; i++) {
179             index = din.readUnsignedShort();
180             ConstantClassInfo info = (ConstantClassInfo)cp.getConstant(index);
181             cf.addInterface(info.getType().getRootName());
182         }
183         
184         // Read fields.
185
size = din.readUnsignedShort();
186         for (int i=0; i<size; i++) {
187             cf.mFields.add(FieldInfo.readFrom(cf, din, attrFactory));
188         }
189         
190         // Read methods.
191
size = din.readUnsignedShort();
192         for (int i=0; i<size; i++) {
193             cf.mMethods.add(MethodInfo.readFrom(cf, din, attrFactory));
194         }
195
196         // Read attributes.
197
size = din.readUnsignedShort();
198         for (int i=0; i<size; i++) {
199             Attribute attr = Attribute.readFrom(cp, din, attrFactory);
200             cf.addAttribute(attr);
201             if (attr instanceof InnerClassesAttr) {
202                 cf.mInnerClassesAttr = (InnerClassesAttr)attr;
203             }
204         }
205
206         // Load inner and outer classes.
207
if (cf.mInnerClassesAttr != null && loader != null) {
208             InnerClassesAttr.Info[] infos = cf.mInnerClassesAttr.getInnerClassesInfo();
209             for (int i=0; i<infos.length; i++) {
210                 InnerClassesAttr.Info info = infos[i];
211
212                 if (thisClass.equals(info.getInnerClass())) {
213                     // This class is an inner class.
214
if (info.getInnerClassName() != null) {
215                         cf.mInnerClassName = info.getInnerClassName().getValue();
216                     }
217                     ConstantClassInfo outer = info.getOuterClass();
218                     if (cf.mOuterClass == null && outer != null) {
219                         cf.mOuterClass = readOuterClass
220                             (outer, loader, attrFactory, loadedClassFiles);
221                     }
222                     Modifiers innerFlags = info.getModifiers();
223                     cf.mModifiers = cf.mModifiers
224                         .toStatic(innerFlags.isStatic())
225                         .toPrivate(innerFlags.isPrivate())
226                         .toProtected(innerFlags.isProtected())
227                         .toPublic(innerFlags.isPublic());
228                 } else if (info.getOuterClass() == null ||
229                            thisClass.equals(info.getOuterClass())) {
230
231                     // This class is an outer class.
232
ConstantClassInfo inner = info.getInnerClass();
233                     if (inner != null) {
234                         ClassFile innerClass = readInnerClass
235                             (inner, loader, attrFactory, loadedClassFiles, cf);
236                         
237                         if (innerClass != null) {
238                             if (innerClass.getInnerClassName() != null) {
239                                 innerClass.mInnerClassName = info.getInnerClassName().getValue();
240                             }
241                             if (cf.mInnerClasses == null) {
242                                 cf.mInnerClasses = new ArrayList JavaDoc();
243                             }
244                             cf.mInnerClasses.add(innerClass);
245                         }
246                     }
247                 }
248             }
249         }
250
251         return cf;
252     }
253
254     private static ClassFile readOuterClass(ConstantClassInfo outer,
255                                             ClassFileDataLoader loader,
256                                             AttributeFactory attrFactory,
257                                             Map JavaDoc loadedClassFiles)
258         throws IOException JavaDoc
259     {
260         String JavaDoc name = outer.getType().getRootName();
261
262         ClassFile outerClass = (ClassFile)loadedClassFiles.get(name);
263         if (outerClass != null) {
264             return outerClass;
265         }
266
267         InputStream JavaDoc in = loader.getClassData(name);
268         if (in == null) {
269             return null;
270         }
271
272         if (!(in instanceof DataInput JavaDoc)) {
273             in = new DataInputStream JavaDoc(in);
274         }
275
276         return readFrom((DataInput JavaDoc)in, loader, attrFactory, loadedClassFiles, null);
277     }
278
279     private static ClassFile readInnerClass(ConstantClassInfo inner,
280                                             ClassFileDataLoader loader,
281                                             AttributeFactory attrFactory,
282                                             Map JavaDoc loadedClassFiles,
283                                             ClassFile outerClass)
284         throws IOException JavaDoc
285     {
286         String JavaDoc name = inner.getType().getRootName();
287
288         // Prevent cycles in inner class structure.
289
for (ClassFile outer = outerClass; outer != null; outer = outer.getOuterClass()) {
290             if (name.equals(outer.getClassName())) {
291                 // Cycle prevented.
292
return null;
293             }
294         }
295
296         // Prevent classes from being loaded multiple times.
297
ClassFile innerClass = (ClassFile)loadedClassFiles.get(name);
298         if (innerClass != null) {
299             return innerClass;
300         }
301
302         InputStream JavaDoc in = loader.getClassData(name);
303         if (in == null) {
304             return null;
305         }
306
307         if (!(in instanceof DataInput JavaDoc)) {
308             in = new DataInputStream JavaDoc(in);
309         }
310
311         return readFrom((DataInput JavaDoc)in, loader, attrFactory, loadedClassFiles, outerClass);
312     }
313
314     private int mVersion;
315     private String JavaDoc mTarget;
316     {
317         setTarget(null);
318     }
319
320     private final String JavaDoc mClassName;
321     private final String JavaDoc mSuperClassName;
322     private String JavaDoc mInnerClassName;
323     private TypeDesc mType;
324
325     private ConstantPool mCp;
326     
327     private Modifiers mModifiers;
328
329     private ConstantClassInfo mThisClass;
330     private ConstantClassInfo mSuperClass;
331     
332     // Holds ConstantInfo objects.
333
private List JavaDoc mInterfaces = new ArrayList JavaDoc(2);
334     private Set JavaDoc mInterfaceSet = new HashSet JavaDoc(7);
335     
336     // Holds objects.
337
private List JavaDoc mFields = new ArrayList JavaDoc();
338     private List JavaDoc mMethods = new ArrayList JavaDoc();
339     private List JavaDoc mAttributes = new ArrayList JavaDoc();
340     
341     private SourceFileAttr mSource;
342
343     private List JavaDoc mInnerClasses;
344     private int mAnonymousInnerClassCount = 0;
345     private InnerClassesAttr mInnerClassesAttr;
346
347     // Is non-null for inner classes.
348
private ClassFile mOuterClass;
349
350     /**
351      * By default, the ClassFile defines public, non-final, concrete classes.
352      * This constructor creates a ClassFile for a class that extends
353      * java.lang.Object.
354      * <p>
355      * Use the {@link #setModifiers} method to change the access modifiers of
356      * this class or to turn it into an interface.
357      *
358      * @param className Full class name of the form ex: "java.lang.String".
359      */

360     public ClassFile(String JavaDoc className) {
361         this(className, (String JavaDoc)null);
362     }
363     
364     /**
365      * By default, the ClassFile defines public, non-final, concrete classes.
366      * <p>
367      * Use the {@link #setModifiers} method to change the access modifiers of
368      * this class or to turn it into an interface.
369      *
370      * @param className Full class name of the form ex: "java.lang.String".
371      * @param superClass Super class or interface.
372      */

373     public ClassFile(String JavaDoc className, Class JavaDoc superClass) {
374         this(className, superClass.isInterface() ? null : superClass.getName());
375         if (superClass.isInterface()) {
376             addInterface(superClass);
377         }
378     }
379
380     /**
381      * By default, the ClassFile defines public, non-final, concrete classes.
382      * <p>
383      * Use the {@link #setModifiers} method to change the access modifiers of
384      * this class or to turn it into an interface.
385      *
386      * @param className Full class name of the form ex: "java.lang.String".
387      * @param superClassName Full super class name.
388      */

389     public ClassFile(String JavaDoc className, String JavaDoc superClassName) {
390         if (superClassName == null) {
391             if (!className.equals(Object JavaDoc.class.getName())) {
392                 superClassName = Object JavaDoc.class.getName();
393             }
394         }
395
396         mCp = new ConstantPool();
397
398         // public, non-final, concrete class
399
mModifiers = Modifiers.PUBLIC;
400
401         mThisClass = mCp.addConstantClass(className);
402         mSuperClass = mCp.addConstantClass(superClassName);
403
404         mClassName = className;
405         mSuperClassName = superClassName;
406     }
407
408     /**
409      * Used to construct a ClassFile when read from a stream.
410      */

411     private ClassFile(ConstantPool cp, Modifiers modifiers,
412                       ConstantClassInfo thisClass,
413                       ConstantClassInfo superClass,
414                       ClassFile outerClass) {
415
416         mCp = cp;
417
418         mModifiers = modifiers;
419
420         mThisClass = thisClass;
421         mSuperClass = superClass;
422
423         mClassName = thisClass.getType().getRootName();
424         if (superClass == null) {
425             mSuperClassName = null;
426         } else {
427             mSuperClassName = superClass.getType().getRootName();
428         }
429
430         mOuterClass = outerClass;
431     }
432
433     public String JavaDoc getClassName() {
434         return mClassName;
435     }
436
437     public String JavaDoc getSuperClassName() {
438         return mSuperClassName;
439     }
440
441     /**
442      * Returns a TypeDesc for the type of this ClassFile.
443      */

444     public TypeDesc getType() {
445         if (mType == null) {
446             mType = TypeDesc.forClass(mClassName);
447         }
448         return mType;
449     }
450
451     public Modifiers getModifiers() {
452         return mModifiers;
453     }
454
455     /**
456      * Returns the names of all the interfaces that this class implements.
457      */

458     public String JavaDoc[] getInterfaces() {
459         int size = mInterfaces.size();
460         String JavaDoc[] names = new String JavaDoc[size];
461
462         for (int i=0; i<size; i++) {
463             names[i] = ((ConstantClassInfo)mInterfaces.get(i))
464                 .getType().getRootName();
465         }
466
467         return names;
468     }
469
470     /**
471      * Returns all the fields defined in this class.
472      */

473     public FieldInfo[] getFields() {
474         FieldInfo[] fields = new FieldInfo[mFields.size()];
475         return (FieldInfo[])mFields.toArray(fields);
476     }
477
478     /**
479      * Returns all the methods defined in this class, not including
480      * constructors and static initializers.
481      */

482     public MethodInfo[] getMethods() {
483         int size = mMethods.size();
484         List JavaDoc methodsOnly = new ArrayList JavaDoc(size);
485
486         for (int i=0; i<size; i++) {
487             MethodInfo method = (MethodInfo)mMethods.get(i);
488             String JavaDoc name = method.getName();
489             if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
490                 methodsOnly.add(method);
491             }
492         }
493
494         MethodInfo[] methodsArray = new MethodInfo[methodsOnly.size()];
495         return (MethodInfo[])methodsOnly.toArray(methodsArray);
496     }
497
498     /**
499      * Returns all the constructors defined in this class.
500      */

501     public MethodInfo[] getConstructors() {
502         int size = mMethods.size();
503         List JavaDoc ctorsOnly = new ArrayList JavaDoc(size);
504
505         for (int i=0; i<size; i++) {
506             MethodInfo method = (MethodInfo)mMethods.get(i);
507             if ("<init>".equals(method.getName())) {
508                 ctorsOnly.add(method);
509             }
510         }
511
512         MethodInfo[] ctorsArray = new MethodInfo[ctorsOnly.size()];
513         return (MethodInfo[])ctorsOnly.toArray(ctorsArray);
514     }
515
516     /**
517      * Returns the static initializer defined in this class or null if there
518      * isn't one.
519      */

520     public MethodInfo getInitializer() {
521         int size = mMethods.size();
522
523         for (int i=0; i<size; i++) {
524             MethodInfo method = (MethodInfo)mMethods.get(i);
525             if ("<clinit>".equals(method.getName())) {
526                 return method;
527             }
528         }
529
530         return null;
531     }
532
533     /**
534      * Returns all the inner classes defined in this class. If no inner classes
535      * are defined, then an array of length zero is returned.
536      */

537     public ClassFile[] getInnerClasses() {
538         if (mInnerClasses == null) {
539             return new ClassFile[0];
540         }
541
542         ClassFile[] innerClasses = new ClassFile[mInnerClasses.size()];
543         return (ClassFile[])mInnerClasses.toArray(innerClasses);
544     }
545
546     /**
547      * Returns true if this ClassFile represents an inner class.
548      */

549     public boolean isInnerClass() {
550         return mOuterClass != null;
551     }
552
553     /**
554      * If this ClassFile represents a non-anonymous inner class, returns its
555      * short inner class name.
556      */

557     public String JavaDoc getInnerClassName() {
558         return mInnerClassName;
559     }
560
561     /**
562      * Returns null if this ClassFile does not represent an inner class.
563      *
564      * @see #isInnerClass()
565      */

566     public ClassFile getOuterClass() {
567         return mOuterClass;
568     }
569
570     /**
571      * Returns a value indicating how deeply nested an inner class is with
572      * respect to its outermost enclosing class. For top level classes, 0
573      * is returned. For first level inner classes, 1 is returned, etc.
574      */

575     public int getClassDepth() {
576         int depth = 0;
577
578         ClassFile outer = mOuterClass;
579         while (outer != null) {
580             depth++;
581             outer = outer.mOuterClass;
582         }
583
584         return depth;
585     }
586
587     /**
588      * Returns the source file of this class file or null if not set.
589      */

590     public String JavaDoc getSourceFile() {
591         if (mSource == null) {
592             return null;
593         } else {
594             return mSource.getFileName().getValue();
595         }
596     }
597
598     public boolean isSynthetic() {
599         for (int i = mAttributes.size(); --i >= 0; ) {
600             Object JavaDoc obj = mAttributes.get(i);
601             if (obj instanceof SyntheticAttr) {
602                 return true;
603             }
604         }
605         return false;
606     }
607
608     public boolean isDeprecated() {
609         for (int i = mAttributes.size(); --i >= 0; ) {
610             Object JavaDoc obj = mAttributes.get(i);
611             if (obj instanceof DeprecatedAttr) {
612                 return true;
613             }
614         }
615         return false;
616     }
617
618     /**
619      * Returns all the runtime invisible annotations defined for this class
620      * file, or an empty array if none.
621      */

622     public Annotation[] getRuntimeInvisibleAnnotations() {
623         for (int i = mAttributes.size(); --i >= 0; ) {
624             Object JavaDoc obj = mAttributes.get(i);
625             if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
626                 return ((AnnotationsAttr) obj).getAnnotations();
627             }
628         }
629         return new Annotation[0];
630     }
631
632     /**
633      * Returns all the runtime visible annotations defined for this class file,
634      * or an empty array if none.
635      */

636     public Annotation[] getRuntimeVisibleAnnotations() {
637         for (int i = mAttributes.size(); --i >= 0; ) {
638             Object JavaDoc obj = mAttributes.get(i);
639             if (obj instanceof RuntimeVisibleAnnotationsAttr) {
640                 return ((AnnotationsAttr) obj).getAnnotations();
641             }
642         }
643         return new Annotation[0];
644     }
645
646     /**
647      * Add a runtime invisible annotation.
648      */

649     public Annotation addRuntimeInvisibleAnnotation(TypeDesc type) {
650         AnnotationsAttr attr = null;
651         for (int i = mAttributes.size(); --i >= 0; ) {
652             Object JavaDoc obj = mAttributes.get(i);
653             if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
654                 attr = (AnnotationsAttr) obj;
655             }
656         }
657         if (attr == null) {
658             attr = new RuntimeInvisibleAnnotationsAttr(mCp);
659             addAttribute(attr);
660         }
661         Annotation ann = new Annotation(mCp);
662         ann.setType(type);
663         attr.addAnnotation(ann);
664         return ann;
665     }
666
667     /**
668      * Add a runtime visible annotation.
669      */

670     public Annotation addRuntimeVisibleAnnotation(TypeDesc type) {
671         AnnotationsAttr attr = null;
672         for (int i = mAttributes.size(); --i >= 0; ) {
673             Object JavaDoc obj = mAttributes.get(i);
674             if (obj instanceof RuntimeVisibleAnnotationsAttr) {
675                 attr = (AnnotationsAttr) obj;
676             }
677         }
678         if (attr == null) {
679             attr = new RuntimeVisibleAnnotationsAttr(mCp);
680             addAttribute(attr);
681         }
682         Annotation ann = new Annotation(mCp);
683         ann.setType(type);
684         attr.addAnnotation(ann);
685         return ann;
686     }
687
688     /**
689      * Returns the signature attribute of this classfile, or null if none is
690      * defined.
691      */

692     // TODO: Eventually remove this method
693
public SignatureAttr getSignatureAttr() {
694         for (int i = mAttributes.size(); --i >= 0; ) {
695             Object JavaDoc obj = mAttributes.get(i);
696             if (obj instanceof SignatureAttr) {
697                 return (SignatureAttr) obj;
698             }
699         }
700         return null;
701     }
702
703     /**
704      * Provides access to the ClassFile's ContantPool.
705      *
706      * @return The constant pool for this class file.
707      */

708     public ConstantPool getConstantPool() {
709         return mCp;
710     }
711
712     public void setModifiers(Modifiers modifiers) {
713         mModifiers = modifiers;
714     }
715
716     /**
717      * Add an interface that this class implements.
718      *
719      * @param interfaceName Full interface name.
720      */

721     public void addInterface(String JavaDoc interfaceName) {
722         if (!mInterfaceSet.contains(interfaceName)) {
723             mInterfaces.add(mCp.addConstantClass(interfaceName));
724             mInterfaceSet.add(interfaceName);
725         }
726     }
727     
728     /**
729      * Add an interface that this class implements.
730      */

731     public void addInterface(Class JavaDoc i) {
732         addInterface(i.getName());
733     }
734     
735     /**
736      * Add a field to this class.
737      */

738     public FieldInfo addField(Modifiers modifiers,
739                               String JavaDoc fieldName,
740                               TypeDesc type) {
741         FieldInfo fi = new FieldInfo(this, modifiers, fieldName, type);
742         mFields.add(fi);
743         return fi;
744     }
745     
746     /**
747      * Add a method to this class.
748      *
749      * @param ret Is null if method returns void.
750      * @param params May be null if method accepts no parameters.
751      */

752     public MethodInfo addMethod(Modifiers modifiers,
753                                 String JavaDoc methodName,
754                                 TypeDesc ret,
755                                 TypeDesc[] params) {
756         MethodDesc md = MethodDesc.forArguments(ret, params);
757         return addMethod(modifiers, methodName, md);
758     }
759
760     /**
761      * Add a method to this class.
762      */

763     public MethodInfo addMethod(Modifiers modifiers,
764                                 String JavaDoc methodName,
765                                 MethodDesc md) {
766         MethodInfo mi = new MethodInfo(this, modifiers, methodName, md);
767         mMethods.add(mi);
768         return mi;
769     }
770
771     /**
772      * Add a method to this class. This method is handy for implementing
773      * methods defined by a pre-existing interface.
774      */

775     public MethodInfo addMethod(Method JavaDoc method) {
776         Modifiers modifiers = Modifiers.getInstance(method.getModifiers()).toAbstract(false);
777         MethodInfo mi = addMethod(modifiers, method.getName(), MethodDesc.forMethod(method));
778         
779         // exception stuff...
780
Class JavaDoc[] exceptions = method.getExceptionTypes();
781         for (int i=0; i<exceptions.length; i++) {
782             mi.addException(TypeDesc.forClass(exceptions[i]));
783         }
784
785         return mi;
786     }
787
788     /**
789      * Add a method to this class by declaration.
790      *
791      * @throws IllegalArgumentException if declaration syntax is wrong
792      * @see MethodDeclarationParser
793      */

794     public MethodInfo addMethod(String JavaDoc declaration) {
795         MethodDeclarationParser p = new MethodDeclarationParser(declaration);
796         return addMethod(p.getModifiers(), p.getMethodName(),
797                          p.getReturnType(), p.getParameters());
798     }
799
800     /**
801      * Add a constructor to this class.
802      *
803      * @param params May be null if constructor accepts no parameters.
804      */

805     public MethodInfo addConstructor(Modifiers modifiers,
806                                      TypeDesc[] params) {
807         MethodDesc md = MethodDesc.forArguments(null, params);
808         MethodInfo mi = new MethodInfo(this, modifiers, "<init>", md);
809         mMethods.add(mi);
810         return mi;
811     }
812
813     /**
814      * Adds a public, no-arg constructor with the code buffer properly defined.
815      */

816     public MethodInfo addDefaultConstructor() {
817         MethodInfo mi = addConstructor(Modifiers.PUBLIC, null);
818         CodeBuilder builder = new CodeBuilder(mi);
819         builder.loadThis();
820         builder.invokeSuperConstructor(null);
821         builder.returnVoid();
822         return mi;
823     }
824
825     /**
826      * Add a static initializer to this class.
827      */

828     public MethodInfo addInitializer() {
829         MethodDesc md = MethodDesc.forArguments(null, null);
830         Modifiers af = Modifiers.NONE.toStatic(true);
831         MethodInfo mi = new MethodInfo(this, af, "<clinit>", md);
832         mMethods.add(mi);
833         return mi;
834     }
835
836     /**
837      * Add an inner class to this class. By default, inner classes are private
838      * static.
839      *
840      * @param fullInnerClassName Optional full inner class name.
841      * @param innerClassName Optional short inner class name.
842      */

843     public ClassFile addInnerClass(String JavaDoc fullInnerClassName, String JavaDoc innerClassName) {
844         return addInnerClass(fullInnerClassName, innerClassName, (String JavaDoc)null);
845     }
846
847     /**
848      * Add an inner class to this class. By default, inner classes are private
849      * static.
850      *
851      * @param fullInnerClassName Optional full inner class name.
852      * @param innerClassName Optional short inner class name.
853      * @param superClass Super class.
854      */

855     public ClassFile addInnerClass(String JavaDoc fullInnerClassName, String JavaDoc innerClassName,
856                                    Class JavaDoc superClass) {
857         return addInnerClass(innerClassName, superClass.getName());
858     }
859
860     /**
861      * Add an inner class to this class. By default, inner classes are private
862      * static.
863      *
864      * @param fullInnerClassName Optional full inner class name.
865      * @param innerClassName Optional short inner class name.
866      * @param superClassName Full super class name.
867      */

868     public ClassFile addInnerClass(String JavaDoc fullInnerClassName, String JavaDoc innerClassName,
869                                    String JavaDoc superClassName) {
870         if (fullInnerClassName == null) {
871             if (innerClassName == null) {
872                 fullInnerClassName = mClassName + '$' + (++mAnonymousInnerClassCount);
873             } else {
874                 fullInnerClassName = mClassName + '$' + innerClassName;
875             }
876         }
877
878         ClassFile inner = new ClassFile(fullInnerClassName, superClassName);
879         Modifiers modifiers = inner.getModifiers().toPrivate(true).toStatic(true);
880         inner.setModifiers(modifiers);
881         inner.mInnerClassName = innerClassName;
882         inner.mOuterClass = this;
883
884         if (mInnerClasses == null) {
885             mInnerClasses = new ArrayList JavaDoc();
886         }
887
888         mInnerClasses.add(inner);
889         
890         // Record the inner class in this, the outer class.
891
if (mInnerClassesAttr == null) {
892             addAttribute(new InnerClassesAttr(mCp));
893         }
894
895         // TODO: Anonymous inner classes and method scoped classes do not have
896
// an outer class listed.
897

898         mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName,
899                                         innerClassName, modifiers);
900
901         // Record the inner class in itself.
902
inner.addAttribute(new InnerClassesAttr(inner.getConstantPool()));
903         inner.mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName,
904                                               innerClassName, modifiers);
905
906         return inner;
907     }
908
909     /**
910      * Set the source file of this class file by adding a source file
911      * attribute. The source doesn't actually have to be a file,
912      * but the virtual machine spec names the attribute "SourceFile_attribute".
913      */

914     public void setSourceFile(String JavaDoc fileName) {
915         addAttribute(new SourceFileAttr(mCp, fileName));
916     }
917
918     /**
919      * Mark this class as being synthetic by adding a special attribute.
920      */

921     public void markSynthetic() {
922         addAttribute(new SyntheticAttr(mCp));
923     }
924
925     /**
926      * Mark this class as being deprecated by adding a special attribute.
927      */

928     public void markDeprecated() {
929         addAttribute(new DeprecatedAttr(mCp));
930     }
931
932     /**
933      * Add an attribute to this class.
934      */

935     public void addAttribute(Attribute attr) {
936         if (attr instanceof SourceFileAttr) {
937             if (mSource != null) {
938                 mAttributes.remove(mSource);
939             }
940             mSource = (SourceFileAttr)attr;
941         } else if (attr instanceof InnerClassesAttr) {
942             if (mInnerClassesAttr != null) {
943                 mAttributes.remove(mInnerClassesAttr);
944             }
945             mInnerClassesAttr = (InnerClassesAttr)attr;
946         }
947
948         mAttributes.add(attr);
949     }
950
951     public Attribute[] getAttributes() {
952         Attribute[] attrs = new Attribute[mAttributes.size()];
953         return (Attribute[])mAttributes.toArray(attrs);
954     }
955
956     /**
957      * Specify what target virtual machine version classfile should generate
958      * for. Calling this method changes the major and minor version of the
959      * classfile format.
960      *
961      * @param target VM version, 1.0, 1.1, etc.
962      * @throws IllegalArgumentException if target is not supported
963      */

964     public void setTarget(String JavaDoc target) throws IllegalArgumentException JavaDoc {
965         int major, minor;
966
967         if (target == null || "1.0".equals(target) || "1.1".equals(target)) {
968             major = 45; minor = 3;
969             if (target == null) {
970                 target = "1.0";
971             }
972         } else if ("1.2".equals(target)) {
973             major = 46; minor = 0;
974         } else if ("1.3".equals(target)) {
975             major = 47; minor = 0;
976         } else if ("1.4".equals(target)) {
977             major = 48; minor = 0;
978         } else if ("1.5".equals(target)) {
979             major = 49; minor = 0;
980         } else if ("1.6".equals(target)) {
981             major = 50; minor = 0;
982         } else {
983             throw new IllegalArgumentException JavaDoc
984                 ("Unsupported target version: " + target);
985         }
986         
987         mVersion = (minor << 16) | (major & 0xffff);
988         mTarget = target.intern();
989     }
990
991     /**
992      * Returns the target virtual machine version, or null if unknown.
993      */

994     public String JavaDoc getTarget() {
995         return mTarget;
996     }
997
998     /**
999      * Sets the version to use when writing the generated classfile, overriding
1000     * the target.
1001     */

1002    public void setVersion(int major, int minor) {
1003        if (major > 65535 || minor > 65535) {
1004            throw new IllegalArgumentException JavaDoc
1005                ("Version number element cannot exceed 65535");
1006        }
1007
1008        mVersion = (minor << 16) | (major & 0xffff);
1009
1010        String JavaDoc target;
1011        switch (major) {
1012        default:
1013            target = null;
1014            break;
1015        case 45:
1016            target = minor == 3 ? "1.0" : null;
1017            break;
1018        case 46:
1019            target = minor == 0 ? "1.2" : null;
1020            break;
1021        case 47:
1022            target = minor == 0 ? "1.3" : null;
1023            break;
1024        case 48:
1025            target = minor == 0 ? "1.4" : null;
1026            break;
1027        case 49:
1028            target = minor == 0 ? "1.5" : null;
1029            break;
1030        case 50:
1031            target = minor == 0 ? "1.6" : null;
1032            break;
1033        }
1034
1035        mTarget = target;
1036    }
1037
1038    /**
1039     * Returns the major version number of the classfile format.
1040     */

1041    public int getMajorVersion() {
1042        return mVersion & 0xffff;
1043    }
1044
1045    /**
1046     * Returns the minor version number of the classfile format.
1047     */

1048    public int getMinorVersion() {
1049        return (mVersion >> 16) & 0xffff;
1050    }
1051
1052    /**
1053     * Writes the ClassFile to the given OutputStream.
1054     */

1055    public void writeTo(OutputStream JavaDoc out) throws IOException JavaDoc {
1056        if (!(out instanceof DataOutput JavaDoc)) {
1057            out = new DataOutputStream JavaDoc(out);
1058        }
1059        writeTo((DataOutput JavaDoc)out);
1060    }
1061
1062    /**
1063     * Writes the ClassFile to the given DataOutput.
1064     */

1065    public void writeTo(DataOutput JavaDoc dout) throws IOException JavaDoc {
1066        dout.writeInt(MAGIC);
1067        dout.writeInt(mVersion);
1068
1069        mCp.writeTo(dout);
1070
1071        {
1072            int flags = mModifiers.getBitmask();
1073            if (!mModifiers.isInterface()) {
1074                // Set the ACC_SUPER flag for classes only.
1075
flags |= Modifier.SYNCHRONIZED;
1076            }
1077            dout.writeShort(flags);
1078        }
1079
1080        dout.writeShort(mThisClass.getIndex());
1081        if (mSuperClass != null) {
1082            dout.writeShort(mSuperClass.getIndex());
1083        } else {
1084            dout.writeShort(0);
1085        }
1086        
1087        int size = mInterfaces.size();
1088        if (size > 65535) {
1089            throw new IllegalStateException JavaDoc
1090                ("Interfaces count cannot exceed 65535: " + size);
1091        }
1092        dout.writeShort(size);
1093        for (int i=0; i<size; i++) {
1094            int index = ((ConstantInfo)mInterfaces.get(i)).getIndex();
1095            dout.writeShort(index);
1096        }
1097        
1098        size = mFields.size();
1099        if (size > 65535) {
1100            throw new IllegalStateException JavaDoc
1101                ("Field count cannot exceed 65535: " + size);
1102        }
1103        dout.writeShort(size);
1104        for (int i=0; i<size; i++) {
1105            FieldInfo field = (FieldInfo)mFields.get(i);
1106            field.writeTo(dout);
1107        }
1108        
1109        size = mMethods.size();
1110        if (size > 65535) {
1111            throw new IllegalStateException JavaDoc
1112                ("Method count cannot exceed 65535: " + size);
1113        }
1114        dout.writeShort(size);
1115        for (int i=0; i<size; i++) {
1116            MethodInfo method = (MethodInfo)mMethods.get(i);
1117            method.writeTo(dout);
1118        }
1119        
1120        size = mAttributes.size();
1121        if (size > 65535) {
1122            throw new IllegalStateException JavaDoc
1123                ("Attribute count cannot exceed 65535: " + size);
1124        }
1125        dout.writeShort(size);
1126        for (int i=0; i<size; i++) {
1127            Attribute attr = (Attribute)mAttributes.get(i);
1128            attr.writeTo(dout);
1129        }
1130    }
1131
1132    public String JavaDoc toString() {
1133        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1134        String JavaDoc modStr = mModifiers.toString();
1135        if (modStr.length() > 0) {
1136            buf.append(modStr);
1137            buf.append(' ');
1138        }
1139        if (getModifiers().isInterface()) {
1140            buf.append("interface");
1141        } else {
1142            buf.append("class");
1143        }
1144        buf.append(' ');
1145        buf.append(getClassName());
1146
1147        return buf.toString();
1148    }
1149}
1150
Popular Tags