001: /*
002: * Copyright (c) 2001-2007, Jean Tessier
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * * Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in the
014: * documentation and/or other materials provided with the distribution.
015: *
016: * * Neither the name of Jean Tessier nor the names of his contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032:
033: package com.jeantessier.classreader;
034:
035: import java.io.*;
036: import java.util.*;
037:
038: import org.apache.log4j.*;
039:
040: public class Classfile implements Deprecatable, Visitable {
041: public static final int ACC_PUBLIC = 0x0001;
042: public static final int ACC_FINAL = 0x0010;
043: public static final int ACC_SUPER = 0x0020;
044: public static final int ACC_INTERFACE = 0x0200;
045: public static final int ACC_ABSTRACT = 0x0400;
046:
047: private ClassfileLoader loader;
048:
049: private int magicNumber;
050: private int minorVersion;
051: private int majorVersion;
052: private ConstantPool constantPool;
053: private int accessFlag;
054: private int classIndex;
055: private int super classIndex;
056: private Map<String, Class_info> interfaces = new TreeMap<String, Class_info>();
057: private Map<String, Field_info> fields = new TreeMap<String, Field_info>();
058: private Map<String, Method_info> methods = new TreeMap<String, Method_info>();
059: private Collection<Attribute_info> attributes = new LinkedList<Attribute_info>();
060:
061: /**
062: * Parses the input stream and extracts the class description.
063: * You should only call this constructor from a ClassfileLoader.
064: */
065: public Classfile(ClassfileLoader loader, DataInputStream in)
066: throws IOException {
067: this .loader = loader;
068:
069: magicNumber = in.readInt();
070: Logger.getLogger(getClass()).debug(
071: "magic number = " + magicNumber);
072:
073: if (magicNumber != 0xCAFEBABE) {
074: throw new IOException("Bad magic number");
075: }
076:
077: // Reading the file format's version number
078: minorVersion = in.readUnsignedShort();
079: Logger.getLogger(getClass()).debug(
080: "minor version = " + minorVersion);
081: majorVersion = in.readUnsignedShort();
082: Logger.getLogger(getClass()).debug(
083: "major version = " + majorVersion);
084:
085: // Reading the constant pool
086: Logger.getLogger(getClass()).debug(
087: "Reading the constant pool ...");
088: constantPool = new ConstantPool(this , in);
089: Logger.getLogger(getClass()).debug(constantPool);
090:
091: // Skipping the access flag
092: accessFlag = in.readUnsignedShort();
093: Logger.getLogger(getClass())
094: .debug("accessFlag = " + accessFlag);
095:
096: // Retrieving this class's name
097: classIndex = in.readUnsignedShort();
098: Logger.getLogger(getClass()).debug(
099: "thisClass = " + classIndex + " (" + getClassName()
100: + ")");
101:
102: // Retrieving this class's superclass
103: super classIndex = in.readUnsignedShort();
104: Logger.getLogger(getClass()).debug(
105: "superclass = " + super classIndex + " ("
106: + getSuperclassName() + ")");
107:
108: // Retrieving the inferfaces
109: int interfaceCount = in.readUnsignedShort();
110: Logger.getLogger(getClass()).debug(
111: "Reading " + interfaceCount + " interface(s)");
112: for (int i = 0; i < interfaceCount; i++) {
113: Class_info interfaceInfo = (Class_info) constantPool.get(in
114: .readUnsignedShort());
115: Logger.getLogger(getClass()).debug(
116: " " + interfaceInfo.getName());
117: interfaces.put(interfaceInfo.getName(), interfaceInfo);
118: }
119:
120: // Retrieving the fields
121: int fieldCount = in.readUnsignedShort();
122: Logger.getLogger(getClass()).debug(
123: "Reading " + fieldCount + " field(s)");
124: for (int i = 0; i < fieldCount; i++) {
125: Logger.getLogger(getClass()).debug("Field " + i + ":");
126: Field_info fieldInfo = new Field_info(this , in);
127: fields.put(fieldInfo.getName(), fieldInfo);
128: }
129:
130: // Retrieving the methods
131: int methodCount = in.readUnsignedShort();
132: Logger.getLogger(getClass()).debug(
133: "Reading " + methodCount + " method(s)");
134: for (int i = 0; i < methodCount; i++) {
135: Logger.getLogger(getClass()).debug("Method " + i + ":");
136: Method_info methodInfo = new Method_info(this , in);
137: methods.put(methodInfo.getSignature(), methodInfo);
138: }
139:
140: // Retrieving the attributes
141: int attributeCount = in.readUnsignedShort();
142: Logger.getLogger(getClass()).debug(
143: "Reading " + attributeCount + " class attribute(s)");
144: for (int i = 0; i < attributeCount; i++) {
145: Logger.getLogger(getClass()).debug("Attribute " + i + ":");
146: attributes.add(AttributeFactory.create(this , this , in));
147: }
148: }
149:
150: public ClassfileLoader getLoader() {
151: return loader;
152: }
153:
154: public int getMagicNumber() {
155: return magicNumber;
156: }
157:
158: public int getMinorVersion() {
159: return minorVersion;
160: }
161:
162: public int getMajorVersion() {
163: return majorVersion;
164: }
165:
166: public ConstantPool getConstantPool() {
167: return constantPool;
168: }
169:
170: public int getAccessFlag() {
171: return accessFlag;
172: }
173:
174: public int getClassIndex() {
175: return classIndex;
176: }
177:
178: public Class_info getRawClass() {
179: return (Class_info) getConstantPool().get(getClassIndex());
180: }
181:
182: public String getClassName() {
183: return getRawClass().toString();
184: }
185:
186: public String getSimpleName() {
187: return getClassName().substring(
188: getClassName().lastIndexOf(".") + 1);
189: }
190:
191: public int getSuperclassIndex() {
192: return super classIndex;
193: }
194:
195: public Class_info getRawSuperclass() {
196: return (Class_info) getConstantPool().get(getSuperclassIndex());
197: }
198:
199: public String getSuperclassName() {
200: String result = "";
201:
202: if (getSuperclassIndex() != 0) {
203: result = getRawSuperclass().toString();
204: }
205:
206: return result;
207: }
208:
209: public Class_info getInterface(String name) {
210: return interfaces.get(name);
211: }
212:
213: public Collection<Class_info> getAllInterfaces() {
214: return interfaces.values();
215: }
216:
217: public Collection<Field_info> getAllFields() {
218: return fields.values();
219: }
220:
221: public Field_info getField(String name) {
222: return fields.get(name);
223: }
224:
225: public Field_info locateField(String name) {
226: Field_info result = getField(name);
227:
228: if (result == null) {
229: Classfile classfile = getLoader().getClassfile(
230: getSuperclassName());
231: if (classfile != null) {
232: Field_info attempt = classfile.locateField(name);
233: if (attempt != null
234: && (attempt.isPublic() || attempt.isProtected())) {
235: result = attempt;
236: }
237: }
238: }
239:
240: Iterator i = getAllInterfaces().iterator();
241: while (result == null && i.hasNext()) {
242: Classfile classfile = getLoader().getClassfile(
243: i.next().toString());
244: if (classfile != null) {
245: Field_info attempt = classfile.locateField(name);
246: if (attempt != null
247: && (attempt.isPublic() || attempt.isProtected())) {
248: result = attempt;
249: }
250: }
251: }
252:
253: return result;
254: }
255:
256: public Collection<Method_info> getAllMethods() {
257: return methods.values();
258: }
259:
260: public Method_info getMethod(String signature) {
261: return methods.get(signature);
262: }
263:
264: public Method_info locateMethod(String signature) {
265: Method_info result = getMethod(signature);
266:
267: if (result == null) {
268: Classfile classfile = getLoader().getClassfile(
269: getSuperclassName());
270: if (classfile != null) {
271: Method_info attempt = classfile.locateMethod(signature);
272: if (attempt != null
273: && (attempt.isPublic() || attempt.isProtected())) {
274: result = attempt;
275: }
276: }
277: }
278:
279: Iterator i = getAllInterfaces().iterator();
280: while (result == null && i.hasNext()) {
281: Classfile classfile = getLoader().getClassfile(
282: i.next().toString());
283: if (classfile != null) {
284: Method_info attempt = classfile.locateMethod(signature);
285: if (attempt != null
286: && (attempt.isPublic() || attempt.isProtected())) {
287: result = attempt;
288: }
289: }
290: }
291:
292: return result;
293: }
294:
295: public Collection<Attribute_info> getAttributes() {
296: return attributes;
297: }
298:
299: public boolean isPublic() {
300: return (getAccessFlag() & ACC_PUBLIC) != 0;
301: }
302:
303: public boolean isPackage() {
304: return (getAccessFlag() & ACC_PUBLIC) == 0;
305: }
306:
307: public boolean isFinal() {
308: return (getAccessFlag() & ACC_FINAL) != 0;
309: }
310:
311: public boolean isSuper() {
312: return (getAccessFlag() & ACC_SUPER) != 0;
313: }
314:
315: public boolean isInterface() {
316: return (getAccessFlag() & ACC_INTERFACE) != 0;
317: }
318:
319: public boolean isAbstract() {
320: return (getAccessFlag() & ACC_ABSTRACT) != 0;
321: }
322:
323: public boolean isSynthetic() {
324: boolean result = false;
325:
326: Iterator i = getAttributes().iterator();
327: while (!result && i.hasNext()) {
328: result = i.next() instanceof Synthetic_attribute;
329: }
330:
331: return result;
332: }
333:
334: public boolean isDeprecated() {
335: boolean result = false;
336:
337: Iterator i = getAttributes().iterator();
338: while (!result && i.hasNext()) {
339: result = i.next() instanceof Deprecated_attribute;
340: }
341:
342: return result;
343: }
344:
345: public String getDeclaration() {
346: StringBuffer result = new StringBuffer();
347:
348: if (isPublic())
349: result.append("public ");
350: if (isFinal())
351: result.append("final ");
352:
353: if (isInterface()) {
354: result.append("interface ").append(getClassName());
355:
356: if (getAllInterfaces().size() != 0) {
357: result.append(" extends ");
358: Iterator i = getAllInterfaces().iterator();
359: while (i.hasNext()) {
360: result.append(i.next());
361: if (i.hasNext()) {
362: result.append(", ");
363: }
364: }
365: }
366: } else {
367: if (isAbstract())
368: result.append("abstract ");
369: result.append("class ").append(getClassName());
370:
371: if (getSuperclassIndex() != 0) {
372: result.append(" extends ").append(getSuperclassName());
373: }
374:
375: if (getAllInterfaces().size() != 0) {
376: result.append(" implements ");
377: Iterator i = getAllInterfaces().iterator();
378: while (i.hasNext()) {
379: result.append(i.next());
380: if (i.hasNext()) {
381: result.append(", ");
382: }
383: }
384: }
385: }
386:
387: return result.toString();
388: }
389:
390: public void accept(Visitor visitor) {
391: visitor.visitClassfile(this );
392: }
393:
394: public String toString() {
395: return getClassName();
396: }
397: }
|