1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.classfile;
20
21 import java.io.BufferedInputStream;
22 import java.io.DataInputStream;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.zip.ZipEntry;
27 import java.util.zip.ZipFile;
28
29 import org.apache.bcel.Const;
30 import org.apache.commons.io.IOUtils;
31
32
33
34
35
36
37
38
39
40
41 public final class ClassParser {
42
43 private static final int BUFSIZE = 8192;
44 private DataInputStream dataInputStream;
45 private final boolean fileOwned;
46 private final String fileName;
47 private String zipFile;
48 private int classNameIndex;
49 private int superclassNameIndex;
50 private int major;
51 private int minor;
52 private int accessFlags;
53 private int[] interfaces;
54 private ConstantPool constantPool;
55 private Field[] fields;
56 private Method[] methods;
57 private Attribute[] attributes;
58 private final boolean isZip;
59
60
61
62
63
64
65
66 public ClassParser(final InputStream inputStream, final String fileName) {
67 this.fileName = fileName;
68 this.fileOwned = false;
69 final String clazz = inputStream.getClass().getName();
70 this.isZip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
71 if (inputStream instanceof DataInputStream) {
72 this.dataInputStream = (DataInputStream) inputStream;
73 } else {
74 this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
75 }
76 }
77
78
79
80
81
82
83 public ClassParser(final String fileName) {
84 this.isZip = false;
85 this.fileName = fileName;
86 this.fileOwned = true;
87 }
88
89
90
91
92
93
94
95 public ClassParser(final String zipFile, final String fileName) {
96 this.isZip = true;
97 this.fileOwned = true;
98 this.zipFile = zipFile;
99 this.fileName = fileName;
100 }
101
102
103
104
105
106
107
108
109
110
111 public JavaClass parse() throws IOException, ClassFormatException {
112 ZipFile zip = null;
113 try {
114 if (fileOwned) {
115 if (isZip) {
116 zip = new ZipFile(zipFile);
117 final ZipEntry entry = zip.getEntry(fileName);
118
119 if (entry == null) {
120 throw new IOException("File " + fileName + " not found");
121 }
122
123 dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), BUFSIZE));
124 } else {
125 dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName), BUFSIZE));
126 }
127 }
128
129
130 readID();
131
132 readVersion();
133
134
135 readConstantPool();
136
137 readClassInfo();
138
139 readInterfaces();
140
141
142 readFields();
143
144 readMethods();
145
146 readAttributes();
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 } finally {
162
163 if (fileOwned) {
164 IOUtils.closeQuietly(dataInputStream);
165 }
166 IOUtils.closeQuietly(zip);
167 }
168
169 return new JavaClass(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes,
170 isZip ? JavaClass.ZIP : JavaClass.FILE);
171 }
172
173
174
175
176
177
178
179 private void readAttributes() throws IOException, ClassFormatException {
180 final int attributesCount = dataInputStream.readUnsignedShort();
181 attributes = new Attribute[attributesCount];
182 for (int i = 0; i < attributesCount; i++) {
183 attributes[i] = Attribute.readAttribute(dataInputStream, constantPool);
184 }
185 }
186
187
188
189
190
191
192
193 private void readClassInfo() throws IOException, ClassFormatException {
194 accessFlags = dataInputStream.readUnsignedShort();
195
196
197
198 if ((accessFlags & Const.ACC_INTERFACE) != 0) {
199 accessFlags |= Const.ACC_ABSTRACT;
200 }
201 if ((accessFlags & Const.ACC_ABSTRACT) != 0 && (accessFlags & Const.ACC_FINAL) != 0) {
202 throw new ClassFormatException("Class " + fileName + " can't be both final and abstract");
203 }
204 classNameIndex = dataInputStream.readUnsignedShort();
205 superclassNameIndex = dataInputStream.readUnsignedShort();
206 }
207
208
209
210
211
212
213
214 private void readConstantPool() throws IOException, ClassFormatException {
215 constantPool = new ConstantPool(dataInputStream);
216 }
217
218
219
220
221
222
223
224 private void readFields() throws IOException, ClassFormatException {
225 final int fieldsCount = dataInputStream.readUnsignedShort();
226 fields = new Field[fieldsCount];
227 for (int i = 0; i < fieldsCount; i++) {
228 fields[i] = new Field(dataInputStream, constantPool);
229 }
230 }
231
232
233
234
235
236
237
238
239 private void readID() throws IOException, ClassFormatException {
240 if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) {
241 throw new ClassFormatException(fileName + " is not a Java .class file");
242 }
243 }
244
245
246
247
248
249
250
251 private void readInterfaces() throws IOException, ClassFormatException {
252 final int interfacesCount = dataInputStream.readUnsignedShort();
253 interfaces = new int[interfacesCount];
254 for (int i = 0; i < interfacesCount; i++) {
255 interfaces[i] = dataInputStream.readUnsignedShort();
256 }
257 }
258
259
260
261
262
263
264
265 private void readMethods() throws IOException {
266 final int methodsCount = dataInputStream.readUnsignedShort();
267 methods = new Method[methodsCount];
268 for (int i = 0; i < methodsCount; i++) {
269 methods[i] = new Method(dataInputStream, constantPool);
270 }
271 }
272
273
274
275
276
277
278
279 private void readVersion() throws IOException, ClassFormatException {
280 minor = dataInputStream.readUnsignedShort();
281 major = dataInputStream.readUnsignedShort();
282 }
283 }