001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.file;
022:
023: import java.io.*;
024: import java.util.*;
025:
026: import EDU.purdue.cs.bloat.reflect.*;
027: import EDU.purdue.cs.bloat.util.*;
028:
029: /**
030: * ClassFile basically represents a Java classfile as it is found on disk. The
031: * classfile is modeled according to the Java Virtual Machine Specification.
032: * Methods are provided to edit the classfile at a very low level.
033: *
034: * @see Attribute
035: * @see EDU.purdue.cs.bloat.reflect.Constant
036: * @see Field
037: * @see Method
038: *
039: * @author Nate Nystrom (<a
040: * href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
041: */
042: public class ClassFile implements ClassInfo {
043: private ClassInfoLoader loader; // ClassInfoLoader that "owns" this class
044:
045: private List constants; // The constant pool
046:
047: private int modifiers; // This class's modifer bit field
048:
049: private int this Class;
050:
051: private int super Class;
052:
053: private int[] interfaces;
054:
055: private Field[] fields;
056:
057: private Method[] methods;
058:
059: private Attribute[] attrs;
060:
061: private File file; // (.class) File in which this class resides
062:
063: /**
064: * Constructor. This constructor parses the class file from the input
065: * stream.
066: *
067: * @param file
068: * The file in which the class resides.
069: * @param loader
070: * The class info loader which loaded the class.
071: * @param in
072: * The data stream containing the class.
073: * @exception ClassFormatError
074: * When the class could not be parsed.
075: */
076: public ClassFile(final File file, final ClassInfoLoader loader,
077: final DataInputStream in) {
078: this .loader = loader;
079: this .file = file;
080: // Assert.isTrue(file != null, "Null file for class file");
081:
082: // Read in file contents from stream
083: try {
084: if (ClassFileLoader.DEBUG) {
085: System.out.println("ClassFile: Reading header");
086: }
087: readHeader(in);
088:
089: if (ClassFileLoader.DEBUG) {
090: System.out.println("ClassFile: Reading constant pool");
091: }
092: readConstantPool(in);
093:
094: if (ClassFileLoader.DEBUG) {
095: System.out.println("ClassFile: Reading access flags");
096: }
097: readAccessFlags(in);
098:
099: if (ClassFileLoader.DEBUG) {
100: System.out.println("ClassFile: Reading class info");
101: }
102: readClassInfo(in);
103:
104: if (ClassFileLoader.DEBUG) {
105: System.out.println("ClassFile: Reading fields");
106: }
107: readFields(in);
108:
109: if (ClassFileLoader.DEBUG) {
110: System.out.println("ClassFile: Reading methods");
111: }
112: readMethods(in);
113:
114: if (ClassFileLoader.DEBUG) {
115: System.out.println("ClassFile: Reading Attributes");
116: }
117: readAttributes(in);
118: in.close();
119: } catch (final IOException e) {
120: throw new ClassFormatException(e.getMessage()
121: + " (in file " + file + ")");
122: }
123: }
124:
125: /**
126: * Creates a new <code>ClassFile</code> from scratch. It has no fields or
127: * methods.
128: *
129: * @param modifiers
130: * The modifiers describing the newly-created class
131: * @param classIndex
132: * The index of the type of the newly-created class in its
133: * constant pool
134: * @param superClassIndex
135: * The index of the type of the newly-created class's superclass
136: * in its constant pool
137: * @param interfaceIndexes
138: * The indexes of the types of the interfaces that the
139: * newly-created class implements
140: * @param constants
141: * The constant pool for the newly created class (a list of
142: * {@link Constant}s).
143: */
144: public ClassFile(final int modifiers, final int classIndex,
145: final int super ClassIndex, final int[] interfaceIndexes,
146: final List constants, final ClassInfoLoader loader) {
147: this .modifiers = modifiers;
148: this .this Class = classIndex;
149: this .super Class = super ClassIndex;
150: this .interfaces = interfaceIndexes;
151: this .constants = constants;
152: this .loader = loader;
153:
154: this .fields = new Field[0];
155: this .methods = new Method[0];
156: this .attrs = new Attribute[0];
157: }
158:
159: /**
160: * Get the class info loader for the class.
161: *
162: * @return The class info loader for the class.
163: */
164: public ClassInfoLoader loader() {
165: return loader;
166: }
167:
168: /**
169: * Get the name of the class, including the package name.
170: *
171: * @return The name of the class.
172: */
173: public String name() {
174: Constant c = (Constant) constants.get(this Class);
175: Assert.isNotNull(c, "Null constant for class name");
176: if (c.tag() == Constant.CLASS) {
177: final Integer nameIndex = (Integer) c.value();
178: if (nameIndex != null) {
179: c = (Constant) constants.get(nameIndex.intValue());
180: if (c.tag() == Constant.UTF8) {
181: return (String) c.value();
182: }
183: }
184: }
185:
186: throw new ClassFormatException(
187: "Couldn't find class name in file");
188: }
189:
190: /**
191: * Set the index into the constant pool of the name of the class.
192: *
193: * @param index
194: * The index of the name of the class.
195: */
196: public void setClassIndex(final int index) {
197: this .this Class = index;
198: }
199:
200: /**
201: * Set the index into the constant pool of the name of the class's
202: * superclass.
203: *
204: * @param index
205: * The index of the name of the superclass.
206: */
207: public void setSuperclassIndex(final int index) {
208: this .super Class = index;
209: }
210:
211: /**
212: * Set the indices into the constant pool of the names of the class's
213: * interfaces.
214: *
215: * @param indices
216: * The indices of the names of the interfaces.
217: */
218: public void setInterfaceIndices(final int[] indices) {
219: this .interfaces = indices;
220: }
221:
222: /**
223: * Get the index into the constant pool of the name of the class.
224: *
225: * @return The index of the name of the class.
226: */
227: public int classIndex() {
228: return this Class;
229: }
230:
231: /**
232: * Get the index into the constant pool of the name of the class's
233: * superclass.
234: *
235: * @return The index of the name of the superclass.
236: */
237: public int super classIndex() {
238: return super Class;
239: }
240:
241: /**
242: * Get the indices into the constant pool of the names of the class's
243: * interfaces.
244: *
245: * @return The indices of the names of the interfaces.
246: */
247: public int[] interfaceIndices() {
248: return interfaces;
249: }
250:
251: /**
252: * Set the modifiers of the class. The values correspond to the constants in
253: * the Modifiers class.
254: *
255: * @param modifiers
256: * A bit vector of modifier flags for the class.
257: * @see Modifiers
258: */
259: public void setModifiers(final int modifiers) {
260: this .modifiers = modifiers;
261: }
262:
263: /**
264: * Get the modifiers of the class. The values correspond to the constants in
265: * the Modifiers class.
266: *
267: * @return A bit vector of modifier flags for the class.
268: * @see Modifiers
269: */
270: public int modifiers() {
271: return modifiers;
272: }
273:
274: /**
275: * Get an array of FieldInfo structures for each field in the class.
276: *
277: * @return An array of FieldInfo structures.
278: */
279: public FieldInfo[] fields() {
280: return fields;
281: }
282:
283: /**
284: * Returns an array of MethodInfo structures for each method in the class.
285: */
286: public MethodInfo[] methods() {
287: return methods;
288: }
289:
290: /**
291: * Sets the methods in this class.
292: */
293: public void setMethods(final MethodInfo[] methods) {
294: this .methods = new Method[methods.length];
295: for (int i = 0; i < methods.length; i++) {
296: this .methods[i] = (Method) methods[i];
297: }
298:
299: }
300:
301: /**
302: * Get an array of the constants in the constant pool.
303: *
304: * @return An array of Constants.
305: */
306: public Constant[] constants() {
307: return (Constant[]) constants.toArray(new Constant[0]);
308: }
309:
310: /**
311: * Set all the constants in the constant pool.
312: *
313: * @param constants
314: * The array of Constants.
315: */
316: public void setConstants(final Constant[] constants) {
317: this .constants = new ArrayList(constants.length);
318: for (int i = 0; i < constants.length; i++) {
319: this .constants.add(i, constants[i]);
320: }
321: }
322:
323: /**
324: * Returns the File from which this <code>ClassFile</code> was created. If
325: * this <code>ClassFile</code> was created from scratch, <code>null</code>
326: * is returned.
327: */
328: public File file() {
329: return file;
330: }
331:
332: /**
333: * Creates a new File object to hold this class. It is placed in the output
334: * directory and has the name of the class represented by this ClassFile
335: * followed by the .class extension.
336: */
337: public File outputFile() {
338: final File outputDir = ((ClassFileLoader) loader).outputDir();
339: final String fileName = this .name().replace('/',
340: File.separatorChar);
341: return new File(outputDir, fileName + ".class");
342: }
343:
344: /**
345: * Commit any changes back to a file in the output directory. The output
346: * directory is determined from the ClassFileLoader.
347: */
348: public void commit() {
349: try {
350: commitTo(loader.outputStreamFor(this ));
351:
352: } catch (final IOException e) {
353: e.printStackTrace();
354: System.exit(1);
355: }
356: }
357:
358: /**
359: * Commit changes made to this class. Write changes to an OutputStream.
360: */
361: void commitTo(final OutputStream outStream) {
362: try {
363: final DataOutputStream out = new DataOutputStream(outStream);
364:
365: writeHeader(out);
366: writeConstantPool(out);
367: writeAccessFlags(out);
368: writeClassInfo(out);
369: writeFields(out, null);
370: writeMethods(out, null);
371: writeAttributes(out);
372:
373: out.close();
374:
375: } catch (final IOException e) {
376: e.printStackTrace();
377: System.exit(1);
378: }
379: }
380:
381: public void commitOnly(final Set methods, final Set fields) {
382: try {
383: final OutputStream outStream = loader.outputStreamFor(this );
384: final DataOutputStream out = new DataOutputStream(outStream);
385:
386: writeHeader(out);
387: writeConstantPool(out);
388: writeAccessFlags(out);
389: writeClassInfo(out);
390: writeFields(out, fields);
391: writeMethods(out, methods);
392: writeAttributes(out);
393:
394: out.close();
395:
396: } catch (final IOException e) {
397: e.printStackTrace();
398: System.exit(1);
399: }
400: }
401:
402: /**
403: * Write the class file header.
404: *
405: * @param out
406: * The stream to which to write.
407: * @exception IOException
408: * If an error occurs while writing.
409: */
410: private void writeHeader(final DataOutputStream out)
411: throws IOException {
412: out.writeInt(0xCAFEBABE);
413: out.writeShort(3);
414: out.writeShort(45);
415: }
416:
417: /**
418: * Write the class's constant pool.
419: *
420: * @param out
421: * The stream to which to write.
422: * @exception IOException
423: * If an error occurs while writing.
424: */
425: private void writeConstantPool(final DataOutputStream out)
426: throws IOException {
427: out.writeShort(constants.size());
428:
429: // Write the constants. The first constant is reserved for
430: // internal use by the JVM, so start at 1.
431: for (int i = 1; i < constants.size(); i++) {
432: writeConstant(out, (Constant) constants.get(i));
433:
434: switch (((Constant) constants.get(i)).tag()) {
435: case Constant.LONG:
436: case Constant.DOUBLE:
437: // Longs and doubles take up 2 constant pool entries.
438: i++;
439: break;
440: }
441: }
442: }
443:
444: /**
445: * Read a constant from the constant pool.
446: *
447: * @param in
448: * The stream from which to read.
449: * @return The constant.
450: * @exception IOException
451: * If an error occurs while reading.
452: */
453: private Constant readConstant(final DataInputStream in)
454: throws IOException {
455: final int tag = in.readUnsignedByte();
456: Object value;
457:
458: switch (tag) {
459: case Constant.CLASS:
460: case Constant.STRING:
461: value = new Integer(in.readUnsignedShort());
462: break;
463: case Constant.FIELD_REF:
464: case Constant.METHOD_REF:
465: case Constant.INTERFACE_METHOD_REF:
466: case Constant.NAME_AND_TYPE:
467: value = new int[2];
468: ((int[]) value)[0] = in.readUnsignedShort();
469: ((int[]) value)[1] = in.readUnsignedShort();
470: break;
471: case Constant.INTEGER:
472: value = new Integer(in.readInt());
473: break;
474: case Constant.FLOAT:
475: value = new Float(in.readFloat());
476: break;
477: case Constant.LONG:
478: // Longs take up 2 constant pool entries.
479: value = new Long(in.readLong());
480: break;
481: case Constant.DOUBLE:
482: // Doubles take up 2 constant pool entries.
483: value = new Double(in.readDouble());
484: break;
485: case Constant.UTF8:
486: value = in.readUTF();
487: break;
488: default:
489: throw new ClassFormatException(file.getPath()
490: + ": Invalid constant tag: " + tag);
491: }
492:
493: return new Constant(tag, value);
494: }
495:
496: /**
497: * Write a constant in the constant pool.
498: *
499: * @param out
500: * The stream to which to write.
501: * @param constant
502: * The constant.
503: * @exception IOException
504: * If an error occurs while writing.
505: */
506: private void writeConstant(final DataOutputStream out,
507: final Constant constant) throws IOException {
508: final int tag = constant.tag();
509: final Object value = constant.value();
510:
511: out.writeByte(tag);
512:
513: switch (tag) {
514: case Constant.CLASS:
515: case Constant.STRING:
516: out.writeShort(((Integer) value).intValue());
517: break;
518: case Constant.INTEGER:
519: out.writeInt(((Integer) value).intValue());
520: break;
521: case Constant.FLOAT:
522: out.writeFloat(((Float) value).floatValue());
523: break;
524: case Constant.LONG:
525: out.writeLong(((Long) value).longValue());
526: break;
527: case Constant.DOUBLE:
528: out.writeDouble(((Double) value).doubleValue());
529: break;
530: case Constant.UTF8:
531: out.writeUTF((String) value);
532: break;
533: case Constant.FIELD_REF:
534: case Constant.METHOD_REF:
535: case Constant.INTERFACE_METHOD_REF:
536: case Constant.NAME_AND_TYPE:
537: out.writeShort(((int[]) value)[0]);
538: out.writeShort(((int[]) value)[1]);
539: break;
540: }
541: }
542:
543: /**
544: * Write the class's access flags.
545: *
546: * @param out
547: * The stream to which to write.
548: * @exception IOException
549: * If an error occurs while writing.
550: */
551: private void writeAccessFlags(final DataOutputStream out)
552: throws IOException {
553: out.writeShort(modifiers);
554: }
555:
556: /**
557: * Write the class's name, superclass, and interfaces.
558: *
559: * @param out
560: * The stream to which to write.
561: * @exception IOException
562: * If an error occurs while writing.
563: */
564: private void writeClassInfo(final DataOutputStream out)
565: throws IOException {
566: out.writeShort(this Class);
567: out.writeShort(super Class);
568:
569: out.writeShort(interfaces.length);
570:
571: for (int i = 0; i < interfaces.length; i++) {
572: out.writeShort(interfaces[i]);
573: }
574: }
575:
576: /**
577: * Write the class's fields.
578: *
579: * @param out
580: * The stream to which to write.
581: * @exception IOException
582: * If an error occurs while writing.
583: */
584: private void writeFields(final DataOutputStream out,
585: final Set onlyFields) throws IOException {
586: out.writeShort(fields.length);
587:
588: for (int i = 0; i < fields.length; i++) {
589: if ((onlyFields != null) && onlyFields.contains(fields[i])) {
590: continue;
591: }
592: fields[i].write(out);
593: }
594: }
595:
596: /**
597: * Write the class's methods.
598: *
599: * @param out
600: * The stream to which to write.
601: * @exception IOException
602: * If an error occurs while writing.
603: */
604: private void writeMethods(final DataOutputStream out,
605: final Set onlyMethods) throws IOException {
606: if (onlyMethods != null) {
607: out.writeShort(onlyMethods.size());
608:
609: } else {
610: if (Method.DEBUG) {
611: System.out.println("Writing " + methods.length
612: + " methods");
613: }
614: out.writeShort(methods.length);
615: }
616:
617: for (int i = 0; i < methods.length; i++) {
618: if ((onlyMethods != null)
619: && onlyMethods.contains(methods[i])) {
620: continue;
621: }
622: methods[i].write(out);
623: }
624: }
625:
626: /**
627: * Write the class's attributes. No attributes are written by this method
628: * since none are required.
629: *
630: * @param out
631: * The stream to which to write.
632: * @exception IOException
633: * If an error occurs while writing.
634: */
635: private void writeAttributes(final DataOutputStream out)
636: throws IOException {
637: out.writeShort(attrs.length);
638:
639: for (int i = 0; i < attrs.length; i++) {
640: out.writeShort(attrs[i].nameIndex());
641: out.writeInt(attrs[i].length());
642: attrs[i].writeData(out);
643: }
644: }
645:
646: /**
647: * Read the class file header.
648: *
649: * @param in
650: * The stream from which to read.
651: * @exception IOException
652: * If an error occurs while reading.
653: */
654: private void readHeader(final DataInputStream in)
655: throws IOException {
656: final int magic = in.readInt();
657:
658: if (magic != 0xCAFEBABE) {
659: throw new ClassFormatError("Bad magic number.");
660: }
661:
662: in.readUnsignedShort(); // major
663: in.readUnsignedShort(); // minor
664: }
665:
666: /**
667: * Read the class's constant pool. Constants in the constant pool are
668: * modeled by an array of <tt>reflect.Constant</tt>/
669: *
670: * @param in
671: * The stream from which to read.
672: * @exception IOException
673: * If an error occurs while reading.
674: *
675: * @see EDU.purdue.cs.bloat.reflect.Constant
676: * @see #constants
677: */
678: private void readConstantPool(final DataInputStream in)
679: throws IOException {
680: final int count = in.readUnsignedShort();
681:
682: constants = new ArrayList(count);
683:
684: // The first constant is reserved for internal use by the JVM.
685: constants.add(0, null);
686:
687: // Read the constants.
688: for (int i = 1; i < count; i++) {
689: constants.add(i, readConstant(in));
690:
691: switch (((Constant) constants.get(i)).tag()) {
692: case Constant.LONG:
693: case Constant.DOUBLE:
694: // Longs and doubles take up 2 constant pool entries.
695: constants.add(++i, null);
696: break;
697: }
698: }
699: }
700:
701: /**
702: * Read the class's access flags.
703: *
704: * @param in
705: * The stream from which to read.
706: * @exception IOException
707: * If an error occurs while reading.
708: */
709: private void readAccessFlags(final DataInputStream in)
710: throws IOException {
711: modifiers = in.readUnsignedShort();
712: }
713:
714: /**
715: * Read the class's name, superclass, and interfaces.
716: *
717: * @param in
718: * The stream from which to read.
719: * @exception IOException
720: * If an error occurs while reading.
721: */
722: private void readClassInfo(final DataInputStream in)
723: throws IOException {
724: this Class = in.readUnsignedShort();
725: super Class = in.readUnsignedShort();
726:
727: final int numInterfaces = in.readUnsignedShort();
728:
729: interfaces = new int[numInterfaces];
730:
731: for (int i = 0; i < numInterfaces; i++) {
732: interfaces[i] = in.readUnsignedShort();
733: }
734: }
735:
736: /**
737: * Read the class's fields.
738: *
739: * @param in
740: * The stream from which to read.
741: * @exception IOException
742: * If an error occurs while reading.
743: */
744: private void readFields(final DataInputStream in)
745: throws IOException {
746: final int numFields = in.readUnsignedShort();
747:
748: fields = new Field[numFields];
749:
750: for (int i = 0; i < numFields; i++) {
751: fields[i] = new Field(in, this );
752: }
753: }
754:
755: /**
756: * Read the class's methods.
757: *
758: * @param in
759: * The stream from which to read.
760: * @exception IOException
761: * If an error occurs while reading.
762: */
763: private void readMethods(final DataInputStream in)
764: throws IOException {
765: final int numMethods = in.readUnsignedShort();
766:
767: methods = new Method[numMethods];
768:
769: for (int i = 0; i < numMethods; i++) {
770: methods[i] = new Method(in, this );
771: }
772: }
773:
774: /**
775: * Read the class's attributes. Since none of the attributes are required,
776: * just read the length of each attribute and skip that many bytes.
777: *
778: * @param in
779: * The stream from which to read.
780: * @exception IOException
781: * If an error occurs while reading.
782: */
783: private void readAttributes(final DataInputStream in)
784: throws IOException {
785: final int numAttributes = in.readUnsignedShort();
786:
787: attrs = new Attribute[numAttributes];
788:
789: for (int i = 0; i < numAttributes; i++) {
790: final int nameIndex = in.readUnsignedShort();
791: final int length = in.readInt();
792: attrs[i] = new GenericAttribute(in, nameIndex, length);
793: }
794: }
795:
796: /**
797: * Creates a new field in this classfile
798: */
799: public FieldInfo addNewField(final int modifiers,
800: final int typeIndex, final int nameIndex) {
801: final Field field = new Field(this , modifiers, typeIndex,
802: nameIndex);
803:
804: // Add the new field to the list of fields
805: final Field[] fields = new Field[this .fields.length + 1];
806: for (int i = 0; i < this .fields.length; i++) {
807: fields[i] = this .fields[i];
808: }
809: fields[this .fields.length] = field;
810: this .fields = fields;
811:
812: return (field);
813: }
814:
815: /**
816: * Creates a new field in this classfile
817: */
818: public FieldInfo addNewField(final int modifiers,
819: final int typeIndex, final int nameIndex,
820: final int cvNameIndex, final int constantValueIndex) {
821: final Field field = new Field(this , modifiers, typeIndex,
822: nameIndex, cvNameIndex, constantValueIndex);
823:
824: // Add the new field to the list of fields
825: final Field[] fields = new Field[this .fields.length + 1];
826: for (int i = 0; i < this .fields.length; i++) {
827: fields[i] = this .fields[i];
828: }
829: fields[this .fields.length] = field;
830: this .fields = fields;
831:
832: return (field);
833: }
834:
835: /**
836: * Removes the field whose name is at the given index in the constant pool.
837: *
838: * @throws IllegalArgumentException The class does not contain a field whose
839: * name is at the given index
840: */
841: public void deleteField(final int nameIndex) {
842: final List newFields = new ArrayList();
843:
844: boolean foundIt = false;
845: for (int i = 0; i < this .fields.length; i++) {
846: final Field field = this .fields[i];
847: if (field.nameIndex() == nameIndex) {
848: foundIt = true;
849:
850: } else {
851: newFields.add(field);
852: }
853: }
854:
855: if (!foundIt) {
856: final String s = "No field with name index " + nameIndex
857: + " in " + this .name();
858: throw new IllegalArgumentException(s);
859:
860: } else {
861: this .fields = (Field[]) newFields.toArray(new Field[0]);
862: }
863: }
864:
865: /**
866: * Deletes a method from this class
867: *
868: * @param nameIndex
869: * Index in the constant pool of the name of the method to be
870: * deleted
871: * @param typeIndex
872: * Index in the constant pool of the type of the method to be
873: * deleted
874: *
875: * @throws IllegalArgumentException The class modeled by this
876: * <code>ClassInfo</code> does not contain a method whose name and
877: * type are not at the given indices
878: */
879: public void deleteMethod(final int nameIndex, final int typeIndex) {
880: final List newMethods = new ArrayList();
881:
882: boolean foundIt = false;
883: for (int i = 0; i < this .methods.length; i++) {
884: final Method method = this .methods[i];
885: if ((method.nameIndex() == nameIndex)
886: && (method.typeIndex() == typeIndex)) {
887: foundIt = true;
888:
889: } else {
890: newMethods.add(method);
891: }
892: }
893:
894: if (!foundIt) {
895: final String s = "No method with name index " + nameIndex
896: + " and type index " + typeIndex + " in "
897: + this .name();
898: throw new IllegalArgumentException(s);
899:
900: } else {
901: this .methods = (Method[]) newMethods.toArray(new Method[0]);
902: }
903: }
904:
905: /**
906: * Creates a new method in this class
907: */
908: public MethodInfo addNewMethod(final int modifiers,
909: final int typeIndex, final int nameIndex,
910: final int exceptionIndex, final int[] exceptionTypeIndices,
911: final int codeIndex) {
912: final Exceptions exceptions = new Exceptions(this ,
913: exceptionIndex, exceptionTypeIndices);
914: final Code code = new Code(this , codeIndex); // code can't be null
915: final Attribute[] attributes = new Attribute[] { exceptions,
916: code };
917:
918: final Method method = new Method(this , modifiers, nameIndex,
919: typeIndex, attributes, code, exceptions);
920:
921: // Add the new method to the list of method
922: final Method[] methods = new Method[this .methods.length + 1];
923: for (int i = 0; i < this .methods.length; i++) {
924: methods[i] = this .methods[i];
925: }
926: methods[this .methods.length] = method;
927: this .methods = methods;
928:
929: return (method);
930: }
931:
932: /**
933: * Prints a textual representation of this classfile to a PrintStream. The
934: * text includes information such as the constants in the constant pool, the
935: * name of the class's superclass, its modifier flags, its fields, and its
936: * methods.
937: *
938: * @param out
939: * The stream to which to print.
940: */
941: public void print(final PrintStream out) {
942: print(new PrintWriter(out, true));
943: }
944:
945: public void print(final PrintWriter out) {
946: out.print("(constants");
947: for (int i = 0; i < constants.size(); i++) {
948: out.print("\n " + i + ": " + constants.get(i));
949: }
950: out.println(")");
951:
952: out.println("(class " + classIndex() + ")");
953: out.println("(super " + super classIndex() + ")");
954:
955: out.print("(interfaces");
956: for (int i = 0; i < interfaces.length; i++) {
957: out.print("\n " + i + ": " + interfaces[i]);
958: }
959: out.println(")");
960:
961: out.print("(modifiers");
962: if ((modifiers & Modifiers.PUBLIC) != 0) {
963: out.print(" PUBLIC");
964: }
965: if ((modifiers & Modifiers.FINAL) != 0) {
966: out.print(" FINAL");
967: }
968: if ((modifiers & Modifiers.SUPER) != 0) {
969: out.print(" SUPER");
970: }
971: if ((modifiers & Modifiers.INTERFACE) != 0) {
972: out.print(" INTERFACE");
973: }
974: if ((modifiers & Modifiers.ABSTRACT) != 0) {
975: out.print(" ABSTRACT");
976: }
977: out.println(")");
978:
979: out.print("(fields");
980: for (int i = 0; i < fields.length; i++) {
981: out.print("\n " + i + ": " + fields[i]);
982: }
983: out.println(")");
984:
985: out.print("(methods");
986: for (int i = 0; i < methods.length; i++) {
987: out.print("\n " + i + ": " + methods[i]);
988: }
989: out.println(")");
990: }
991:
992: public String toString() {
993: return "(ClassFile " + name() + ")";
994: }
995: }
|