Source code: jreversepro/reflect/JClassInfo.java
1 /*
2 * @(#)JClassInfo.java
3 *
4 * JReversePro - Java Decompiler / Disassembler.
5 * Copyright (C) 2000 Karthik Kumar.
6 * EMail: akkumar@users.sourceforge.net
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it , under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program.If not, write to
19 * The Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 package jreversepro.reflect;
24
25
26
27 import java.util.List;
28 import java.util.ArrayList;
29
30 import jreversepro.common.KeyWords;
31 import jreversepro.common.Helper;
32 import jreversepro.common.AppConstants;
33
34 import jreversepro.parser.ClassParserException;
35
36 import jreversepro.revengine.JReverseEngineer;
37 import jreversepro.revengine.JDecompiler;
38 import jreversepro.revengine.JDisAssembler;
39 import jreversepro.revengine.RevEngineException;
40
41 /**
42 * <b>JClassInfo</b> is the abstract representation of the Class File.
43 * The names of the methods are self explanatory.
44 *
45 * @author Karthik Kumar
46 */
47 public class JClassInfo implements KeyWords {
48
49 /**
50 * ACC_SUPER bit required to be set on all
51 * modern classes.
52 **/
53 public static final int ACC_SUPER = 0x0020;
54
55 /**
56 * ACC_INTERFACE bit required to be set if it is an
57 * interface and not a class.
58 **/
59 public static final int ACC_INTERFACE = 0x0200;
60
61 //Generic Info about a class File.
62 /**
63 * Absolute path where the class' source file was located.
64 **/
65 private String absPath;
66
67 /**
68 * Major number of the JVM version that this class file
69 * is compiled for.
70 **/
71 private short majorNumber;
72
73 /**
74 * Minor number of the JVM version that this class file
75 * is compiled for.
76 **/
77 private short minorNumber;
78
79
80 /**
81 * Name of the current class in the JVM format.
82 * That is, if the class is String then the name would be
83 * java/lang/String.
84 **/
85 private String thisClass;
86
87
88 /**
89 * Name of the current class' superclass in the JVM format.
90 * That is, if the class is String then the name would be
91 * java/lang/String.
92 **/
93 private String superClass;
94
95 /**
96 * Name of the package of the current class in the JVM format.
97 * That is the fully qualified name of the class is
98 * java.lang.String. then the package name would contain
99 * java/lang.
100 **/
101 private String packageName;
102
103 /**
104 * Name of the source file in which this class files' code
105 * is present.
106 **/
107 private String srcFile;
108
109 /**
110 * ConstantPool information contained in the class.
111 **/
112 private JConstantPool cpInfo;
113
114 /**
115 * TRUE if the class was decompiled.
116 * False if disasembled.
117 **/
118 private boolean decompiled;
119
120 /**
121 * List of fields present in the class.
122 * All the members in the list are JField.
123 **/
124 private List memFields;
125
126 /**
127 * List of methods present in the class.
128 * All the members in the list are JMethod.
129 **/
130 private List memMethods;
131
132 /**
133 * List of interfaces present in the class.
134 * All the members in the list are String.
135 * For example if the class implements
136 * java.awt.event.ActionListener then the list would
137 * contain java/awt/event/ActionListener as its member.
138 * The class file name would be in the JVM format as mentioned
139 * above.
140 **/
141 private List interfaces;
142
143 /**
144 * An integer referring to the access permission of the
145 * class.
146 * Like if a class is public static void main ()
147 * then the accessflag would have appropriate bits
148 * set to say if it public static.
149 **/
150 private int accessFlag;
151
152 /**
153 * Empty constructor
154 **/
155 public JClassInfo() {
156 memFields = new ArrayList();
157 memMethods = new ArrayList();
158 interfaces = new ArrayList();
159 cpInfo = new JConstantPool(2);
160
161 decompiled = false;
162 }
163
164 /**
165 * Adds a new interface that is implemented by this class.
166 * @param interfaceName Name of the interface.
167 **/
168 public void addInterface(String interfaceName) {
169 interfaces.add(interfaceName);
170 }
171
172 /**
173 * Adds a new field present in the class.
174 * @param rhsField contains the field-related information.
175 **/
176 public void addField(JField rhsField) {
177 memFields.add(rhsField);
178 }
179
180 /**
181 * Adds a new method present in the class.
182 * @param rhsMethod contains the method-related information.
183 **/
184 public void addMethod(JMethod rhsMethod) {
185 memMethods.add(rhsMethod);
186 }
187
188 /**
189 * Sets the pathname of this class.
190 * @param classPath Path to this class.
191 **/
192 public void setPathName(String classPath) {
193 absPath = classPath;
194 }
195
196 /**
197 * Sets the ConstantPool information of this class.
198 * @param cpInfo contains the constant pool information
199 * of this class.
200 **/
201 public void setConstantPool(JConstantPool cpInfo) {
202 this.cpInfo = cpInfo;
203 }
204
205 /**
206 * Returns the constantpool reference
207 * @return Returns the ConstantPool reference.
208 **/
209 public JConstantPool getConstantPool() {
210 return this.cpInfo;
211 }
212
213 /**
214 * Sets the major and minor number of the JVM
215 * for which this class file is compiled for.
216 * @param rhsMajor Major number
217 * @param rhsMinor Minor number
218 **/
219 public void setMajorMinor(short rhsMajor, short rhsMinor) {
220 majorNumber = rhsMajor;
221 minorNumber = rhsMinor;
222 }
223
224 /**
225 * Sets the access flag of the class.
226 * @param rhsAccess Access flag of the class.
227 **/
228 public void setAccess(int rhsAccess) {
229 accessFlag = rhsAccess;
230 }
231
232 /**
233 * Sets the name of the current class.
234 * @param rhsName Name of this class.
235 **/
236 public void setThisClass(String rhsName) {
237 thisClass = rhsName;
238 }
239
240 /**
241 * Sets the name of the current class' superclass.
242 * @param rhsName Name of this class; superclass.
243 **/
244 public void setSuperClass(String rhsName) {
245 superClass = rhsName;
246 }
247
248 /**
249 * Sets the package to which this class belongs to.
250 * @param packageName name of the package to be set.
251 **/
252 public void setPackageName(String packageName) {
253 this.packageName = packageName;
254 }
255
256 /**
257 * Sets the name of the source file to which this
258 * was contained in.
259 * @param rhsSrcFile Name of the source file.
260 **/
261 public void setSourceFile(String rhsSrcFile) {
262 srcFile = rhsSrcFile;
263 }
264
265
266 /**
267 * Returns the path name of this class.
268 * @return Absolute path of this class.
269 **/
270 public String getPathName() {
271 return absPath;
272 }
273
274 /**
275 * Returns the major number of the JVM.
276 * @return JVM
277 **/
278 public int getMajor() {
279 return majorNumber;
280 }
281
282 /**
283 * Returns the minor number of the JVM.
284 * @return JVM minor version
285 **/
286 public int getMinor() {
287 return minorNumber;
288 }
289
290 /**
291 * @param fullyQualified Parameter to indicate if to return
292 * the fully qualified name.
293 * Yes - Fully qualified name along with the package name.
294 * No - Just the class name only.
295 * @return Returns Thisclass name only.
296 **/
297 public String getThisClass(boolean fullyQualified) {
298 if (fullyQualified) {
299 return thisClass;
300 } else {
301 int lastIndex = thisClass.lastIndexOf('/');
302 if (lastIndex != -1) {
303 return thisClass.substring(lastIndex + 1);
304 } else {
305 return thisClass;
306 }
307 }
308 }
309
310 /**
311 * Returns the class name of this class.
312 * @return name of the current class.
313 **/
314 public String getThisClass() {
315 return thisClass;
316 }
317
318 /**
319 * Returns the class name of this class' super class.
320 * @return name of the current class' super-class.
321 **/
322 public String getSuperClass() {
323 return superClass;
324 }
325
326 /**
327 * Returns the source file of the current class.
328 * @return source file of the current class.
329 **/
330 public String getSourceFile() {
331 return srcFile;
332 }
333
334 /**
335 * Returns the List of interfaces of the current class.
336 * @return interfaces of the current class.
337 **/
338 public List getInterfaces() {
339 return interfaces;
340 }
341
342 /**
343 * Returns the fields present in the class.
344 * @return Returns a List of JField
345 **/
346 public List getFields() {
347 return memFields;
348 }
349
350 /**
351 * Returns the methods of this class.
352 * @return Returns a list of JMethods
353 **/
354 public List getMethods() {
355 return memMethods;
356 }
357
358 /**
359 * Returns the access string of this class.
360 * @return Returns the access string of this class.
361 **/
362 public String getAccessString() {
363 StringBuffer accString = new StringBuffer();
364 accString.append(JMember.getStringRep(accessFlag, false));
365
366 if (isClass()) {
367 accString.append(CLASS);
368 } else {
369 accString.append(INTERFACE);
370 }
371 return accString.toString();
372 }
373
374 /**
375 * Returns if this is a class or an interface
376 * @return Returns true if this is a class,
377 * false, if this is an interface.
378 **/
379 public boolean isClass() {
380 return ((accessFlag & ACC_INTERFACE) == 0);
381 }
382
383 /**
384 * Process the methods.
385 * @param getBytecode TRUE - disassemble.
386 * FALSE - disassemble.
387 **/
388 public void processMethods(boolean getBytecode) {
389
390 for (int i = 0 ; i < this.getMethods().size(); i++) {
391
392 JMethod method = (JMethod) this.getMethods().get(i);
393 JReverseEngineer jre;
394
395 jre = getBytecode
396 ?
397 (JReverseEngineer)
398 new JDisAssembler(method,
399 this.getConstantPool())
400 :
401 (JReverseEngineer)
402 new JDecompiler(
403 method,
404 this.getConstantPool());
405 try {
406 jre.genCode();
407 } catch (Exception ex) {
408 ex.printStackTrace();
409 }
410 }
411 }
412
413 /**
414 * Returns the stringified disassembled/decompiled class.
415 * @param getBytecode If TRUE, returns the disassembled code
416 * IF the class has already been disassembled. If FALSE,
417 * returns the decompiled code IF the class has been
418 * decompiled. Otherwise, returns null;
419 * @return Stringified class
420 **/
421 public String getStringifiedClass(boolean getBytecode) {
422 return getStringifiedClass(getBytecode, false);
423 }
424
425 /**
426 * Returns the stringified disassembled/decompiled class, optionally with
427 * metadata.
428 * @param getBytecode If TRUE, returns the disassembled code
429 * IF the class has already been disassembled. If FALSE,
430 * returns the decompiled code IF the class has been
431 * decompiled. Otherwise, returns null;
432 * @param includeMetadata - TRUE if method stack & exception data should be
433 * output.
434 * @return Stringified class
435 **/
436 public String getStringifiedClass(boolean getBytecode,
437 boolean includeMetadata) {
438
439 StringBuffer sb = new StringBuffer();
440
441 sb.append(getHeaders());
442 sb.append(getPackageImports());
443 sb.append(getThisSuperClasses());
444
445 sb.append(getStringifiedInterfaces() + "{");
446 sb.append(getStringifiedFields());
447 sb.append(getStringifiedMethods(getBytecode, includeMetadata));
448
449 sb.append("\n}");
450 return sb.toString();
451 }
452
453 /**
454 * Returns the stringified disassembled/decompiled method.
455 * @param getBytecode If TRUE, returns the disassembled code
456 * IF the method has already been disassembled. If FALSE,
457 * returns the decompiled code IF the method has been
458 * decompiled. Otherwise, returns null;
459 * @param includeMetadata - TRUE if method stack & exception data should be
460 * output
461 * @return Stringified methods in this class
462 **/
463 public String getStringifiedMethods(boolean getBytecode,
464 boolean includeMetadata) {
465
466 StringBuffer sb = new StringBuffer();
467
468 for (int i = 0 ; i < this.getMethods().size() ; i++) {
469 JMethod method = (JMethod) this.getMethods().get(i);
470 sb.append(method.getMethodAsString(getBytecode, includeMetadata));
471 }
472 return sb.toString();
473 }
474
475
476 /**
477 * @return Returns a StringBuffer containing the headers for the reverse
478 * engineered code.
479 **/
480 private StringBuffer getHeaders() {
481 StringBuffer init = new StringBuffer();
482 init.append("// Decompiled by JReversePro " + AppConstants.VERSION);
483 init.append("\n// Home : http://jrevpro.sourceforge.net ");
484 init.append("\n// JVM VERSiON: "
485 + majorNumber + "."
486 + minorNumber);
487 init.append("\n// SOURCEFILE: "
488 + srcFile);
489 return init;
490 }
491
492 /**
493 * @return Returns a StringBuffer containing the package and import
494 * information of the .class file.
495 **/
496 private StringBuffer getPackageImports() {
497 StringBuffer result = new StringBuffer();
498 String packageName = Helper.getPackageName(thisClass);
499
500 if (packageName.length() != 0) {
501 result.append("\npackage "
502 + packageName + ";");
503 }
504
505 result.append("\n\n"
506 + cpInfo.getImportedClasses().getImportClasses(packageName));
507 return result;
508 }
509
510 /**
511 * @return Returns a StringBuffer containing the current class name
512 * and the super class name.
513 **/
514 private StringBuffer getThisSuperClasses() {
515 StringBuffer sb = new StringBuffer();
516 sb.append("\n\n" + getAccessString() + " ");
517
518 sb.append(cpInfo.getImportedClasses().
519 getClassName(
520 thisClass));
521
522
523 if (!superClass.equals(LANG_OBJECT)) {
524 sb.append(" extends ");
525 sb.append(
526 cpInfo.getImportedClasses().
527 getClassName(superClass) + " ");
528 }
529 return sb;
530 }
531
532 /**
533 * @return Returns a StringBuffer containing the information
534 * of the interfaces implemented by the class.
535 **/
536 private StringBuffer getStringifiedInterfaces() {
537 StringBuffer sb = new StringBuffer();
538 if (interfaces.size() != 0) {
539 sb.append("\n\t\timplements ");
540 for (int i = 0; i < interfaces.size(); i++) {
541 if (i != 0) {
542 sb.append(" ,");
543 }
544 sb.append(
545 cpInfo.getImportedClasses().
546 getClassName(
547 (String) interfaces.get(i)));
548 }
549 }
550 return sb;
551 }
552
553 /**
554 * @return Returns a StringBuffer containing the information
555 * of fields present in this class.
556 **/
557 private StringBuffer getStringifiedFields() {
558 StringBuffer sb = new StringBuffer("\n");
559 for (int i = 0; i < memFields.size(); i++) {
560 JField field = (JField) memFields.get(i);
561 String datatype =
562 cpInfo.getImportedClasses().
563 getClassName(
564 Helper.getJavaDataType(
565 field.getDatatype(), false));
566
567 String access = field.getQualifierName();
568
569 sb.append("\n\t" + access);
570 sb.append(datatype);
571 sb.append(" " + field.getName());
572 String val = field.getValue();
573 if (field.isFinal() && val.length() != 0) {
574 sb.append(" = " + val);
575 }
576 sb.append(";");
577 }
578 return sb;
579 }
580
581 /**
582 * Reverse Engineer the Class file.
583 * @param getBytecode True disassembler, false - decompile.
584 * @throws ClassParserException Thrown if class file not in proper format.
585 * @throws RevEngineException Thrown if error occured in reverse
586 * engineering file.
587 **/
588 public void reverseEngineer(boolean getBytecode)
589 throws ClassParserException,
590 RevEngineException {
591 //Reverse Engineer here
592 processMethods(getBytecode);
593 }
594 }