1 /***
2 * Reader.java
3 *
4 * Project: Dependency Tool
5 *
6 * WHEN WHO WHAT
7 * 06.06.2003 pko initial public release
8 * 22.01.2002 ctr modification
9 * 11.12.2001 ctr creation
10 *
11 * Copyright 2003 ELCA Informatique SA
12 * Av. de la Harpe 22-24, 1000 Lausanne 13, Switzerland
13 * www.elca.ch
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public License
17 * as published by the Free Software Foundation; either version 2.1 of
18 * the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 * USA
29 */
30
31 package ch.elca.dependency.core.classinfo;
32
33 import java.io.DataInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.util.ArrayList;
37 import java.util.HashSet;
38 import java.util.ListIterator;
39
40 import org.apache.log4j.Logger;
41
42 import ch.elca.dependency.exception.InvalidClassFileException;
43
44 /***
45 * This class is used to read the dependency informations out of the
46 * .class files. That means all the classes and packages that are
47 * needed for compiling a java source file. All the informations in a
48 * java class file are read in the diverse helper clases and they are
49 * stored in the attributes of this class. In this manner all the
50 * stuff contained in a class file is accessible by the getter methods
51 * of this class.
52 *
53 * @see AttributeInfo
54 * @see ConstantPoolInfo
55 * @see MethodInfo
56 * @see FieldInfo
57 * @see ClassInfo
58 * @see InvalidClassFileException
59 *
60 * @tbd make the reader configurable --> the basic dptool needs
61 * probably the constant pool only, so reading just the constant pool
62 * would speed up execution. using BCEL is not appropriate, because
63 * it'd take too long.
64 *
65 * @author Christoph Trutmann
66 * @version 1.0-beta
67 */
68 public class Reader {
69
70 private int magic;
71 private short majorVersion;
72 private short minorVersion;
73 private ConstantPoolInfo constantPool[];
74 private short accessFlags;
75 private ConstantPoolInfo thisClass;
76 private ConstantPoolInfo superClass;
77 private ConstantPoolInfo interfaces[];
78 private FieldInfo fields[];
79 private MethodInfo methods[];
80 private AttributeInfo attributes[];
81 private boolean isValidClass = false;
82 public boolean dumpConstants = false;
83
84 public static final int ACC_PUBLIC = 0x1;
85 public static final int ACC_PRIVATE = 0x2;
86 public static final int ACC_PROTECTED = 0x4;
87 public static final int ACC_STATIC = 0x8;
88 public static final int ACC_FINAL = 0x10;
89 public static final int ACC_SYNCHRONIZED = 0x20;
90 public static final int ACC_THREADSAFE = 0x40;
91 public static final int ACC_TRANSIENT = 0x80;
92 public static final int ACC_NATIVE = 0x100;
93 public static final int ACC_INTERFACE = 0x200;
94 public static final int ACC_ABSTRACT = 0x400;
95
96 /***
97 * Log4j logger
98 */
99 private static final Logger LOG = Logger.getLogger(Reader.class);
100
101 /***
102 * Read a class from InputStream <i>in</i>.
103 *
104 * @param in InputStream for reading the content of the specified .class
105 * file.
106 * @exception IOException if an error occurs
107 * @return a <code>boolean</code> value indicating, whether the
108 * input were valid bytecodes.
109 */
110 public boolean read(InputStream in) throws IOException {
111
112 DataInputStream di = new DataInputStream(in);
113 int count;
114
115 magic = di.readInt();
116
117 if ( magic != (int) 0xCAFEBABE ) {
118 return (false);
119 }
120
121 majorVersion = di.readShort();
122 minorVersion = di.readShort();
123 count = di.readShort();
124 constantPool = new ConstantPoolInfo[count];
125
126 LOG.debug("Read header...");
127
128 constantPool[0] = new ConstantPoolInfo();
129 for ( int i = 1; i < constantPool.length; i++ ) {
130 constantPool[i] = new ConstantPoolInfo();
131 if ( ! constantPool[i].read(di) ) {
132 return (false);
133 }
134 // These two types take up "two" spots in the table
135 if ( (constantPool[i].type == ConstantPoolInfo.LONG) ||
136 (constantPool[i].type == ConstantPoolInfo.DOUBLE) )
137 i++;
138 }
139
140 /*
141 * Update pointers in the constant table. This turns the
142 * table into a real datastructure.
143 *
144 * TODO: Have it verify that the right arguments are present
145 */
146 for ( int i = 1 ; i < constantPool.length ; i++ ) {
147 if ( constantPool[i] == null ) {
148 continue;
149 }
150 if ( constantPool[i].index1 > 0 ) {
151 constantPool[i].arg1 = constantPool[constantPool[i].index1];
152 }
153 if ( constantPool[i].index2 > 0 ) {
154 constantPool[i].arg2 = constantPool[constantPool[i].index2];
155 }
156 }
157
158 if ( dumpConstants ) {
159 for ( int i = 1 ; i < constantPool.length ; i++ ) {
160 System.out.println("C"+i+" - "+constantPool[i]);
161 }
162 }
163 accessFlags = di.readShort();
164
165 thisClass = constantPool[di.readShort()];
166 superClass = constantPool[di.readShort()];
167
168 LOG.debug("Read class info for class: " + thisClass.arg1.strValue);
169 LOG.debug("Has a superclass: " + superClass.arg1.strValue);
170
171 LOG.debug("Reading interface info...");
172
173 /*
174 * Identify all of the interfaces implemented by this class
175 */
176 count = di.readShort();
177 if ( count != 0 ) {
178 interfaces = new ConstantPoolInfo[count];
179 for ( int i = 0 ; i < count ; i++ ) {
180 int iindex = di.readShort();
181 if ( (iindex < 1) || (iindex > constantPool.length - 1) )
182 return (false);
183 interfaces[i] = constantPool[iindex];
184 LOG.debug("Implemented interface: " + interfaces[i].arg1.strValue);
185 }
186 }
187
188 LOG.debug("Reading field info...");
189
190 /*
191 * Identify all fields in this class.
192 */
193 count = di.readShort();
194
195 if ( count != 0 ) {
196 fields = new FieldInfo[count];
197 for ( int i = 0 ; i < count ; i++ ) {
198 fields[i] = new FieldInfo();
199 if ( ! fields[i].read(di, constantPool) ) {
200 return (false);
201 }
202 LOG.debug("Field info: " + fields[i].signature.strValue);
203 }
204 }
205
206 LOG.debug("Reading method info...");
207
208 /*
209 * Identify all the methods in this class.
210 */
211 count = di.readShort();
212 if ( count != 0 ) {
213 methods = new MethodInfo[count];
214 for ( int i = 0 ; i < count ; i++ ) {
215 methods[i] = new MethodInfo();
216 if ( ! methods[i].read(di, constantPool) ) {
217 return (false);
218 }
219 LOG.debug("Method info: " + methods[i].name.strValue + ":" + methods[i].signature.strValue);
220 }
221 }
222
223 LOG.debug("Reading attribute info...");
224
225 /*
226 * Identify all of the attributes in this class
227 */
228 count = di.readShort();
229 if ( count != 0 ) {
230 attributes = new AttributeInfo[count];
231 for ( int i = 0; i < count; i++ ) {
232 attributes[i] = new AttributeInfo();
233 if ( ! attributes[i].read(di, constantPool) ) {
234 return (false);
235 }
236 LOG.debug("Attribute info: " + attributes[i].name.toString());
237 }
238 }
239
240 LOG.debug("done.");
241
242 isValidClass = true;
243 return(true);
244 }
245
246
247 /***
248 * Creates a ClassInfo object, fills in the required information and returns
249 * it.
250 *
251 * @return <code>ClassInfo</code> object with the dependency informations.
252 * @throws IllegalArgumentException If the constructor of the
253 * <code>ClassInfo</code> object has wrong
254 * arguments.
255 * @throws InvalidClassFileException If the there is something wrong with
256 * the .class file.
257 */
258 public ClassInfo getClassInfo()
259 throws IllegalArgumentException, InvalidClassFileException {
260
261 return new ClassInfo( getClassName(),
262 getSuperClass(),
263 getAllDependencies(),
264 getAllPackDepend(),
265 getImplementedInterfaces());
266 }
267
268 /***
269 * Takes a type signature and a string representing a variable
270 * name and returns a declaration for that variable name. For
271 * example, passing this the strings "[B" and "myArray" will
272 * return the string "byte myArray[]"
273 *
274 * @param typeString String representing the type.
275 * @param varName String representing the variable name.
276 */
277 public static String typeString(String typeString, String varName) {
278 int isArray = 0;
279 int ndx = 0;
280 StringBuffer x = new StringBuffer();
281
282 while ( typeString.charAt(ndx) == '[' ) {
283 isArray++;
284 ndx++;
285 }
286
287 switch ( typeString.charAt(ndx) ) {
288 case 'B' :
289 x.append("byte ");
290 break;
291 case 'C' :
292 x.append("char ");
293 break;
294 case 'D' :
295 x.append("double ");
296 break;
297 case 'F' :
298 x.append("float ");
299 break;
300 case 'I' :
301 x.append("int ");
302 break;
303 case 'J' :
304 x.append("long ");
305 break;
306 case 'L' :
307 for ( int i = ndx+1 ; i < typeString.indexOf(';'); i++ ) {
308 if ( typeString.charAt(i) != '/' )
309 x.append(typeString.charAt(i));
310 else
311 x.append('.');
312 }
313 x.append(" ");
314 break;
315 case 'V':
316 x.append("void ");
317 break;
318 case 'S' :
319 x.append("short ");
320 break;
321 case 'Z' :
322 x.append("boolean ");
323 break;
324 }
325 x.append(varName);
326 while ( isArray > 0 ) {
327 x.append("[]");
328 isArray--;
329 }
330 return (x.toString());
331 }
332
333
334 /***
335 * Returns a string that represents what the access flags
336 * are set for. So 0x14 returns "public final "
337 */
338 public static String accessString(short flags) {
339 StringBuffer x = new StringBuffer();
340
341 if ( (flags & ACC_PUBLIC) != 0 ) {
342 x.append("public ");
343 }
344
345 if ( (flags & ACC_PRIVATE) != 0 ) {
346 x.append("private ");
347 }
348
349 if ( (flags & ACC_PROTECTED) != 0 ) {
350 x.append("protected ");
351 }
352
353 if ( (flags & ACC_STATIC) != 0 ) {
354 x.append("static ");
355 }
356
357 if ( (flags & ACC_FINAL) != 0 ) {
358 x.append("final ");
359 }
360
361 if ( (flags & ACC_SYNCHRONIZED) != 0 ) {
362 x.append("synchronized ");
363 }
364
365 if ( (flags & ACC_THREADSAFE) != 0 ) {
366 x.append("threadsafe ");
367 }
368
369 if ( (flags & ACC_TRANSIENT) != 0 ) {
370 x.append("transient ");
371 }
372
373 if ( (flags & ACC_NATIVE) != 0 ) {
374 x.append("native ");
375 }
376
377 if ( (flags & ACC_INTERFACE) != 0 ) {
378 x.append("interface ");
379 }
380
381 if ( (flags & ACC_ABSTRACT) != 0 ) {
382 x.append("abstract ");
383 }
384 return (x.toString());
385 }
386
387 /***
388 * Returns the next signature from a string of concatenated signatures.
389 * For example if the signature was "[BII", this method would return
390 * "II"
391 */
392 public static String nextSig(String sig) {
393 int ndx = 0;
394 String x;
395
396 while ( sig.charAt(ndx) == '[' )
397 ndx++;
398
399 if ( sig.charAt(ndx) == 'L' ) {
400 while ( sig.charAt(ndx) != ';' )
401 ndx++;
402 }
403 ndx++;
404 x = (sig.substring(ndx));
405 return (x);
406 }
407
408 /***
409 * Get the name of the class.
410 *
411 * @return String representing the class name.
412 */
413 public String getClassName() {
414 return printClassName(thisClass.arg1.strValue).trim();
415 }
416
417 /***
418 * The boring version of display().
419 *
420 * @return String representing the Reader object.
421 */
422 public String toString() {
423 return("Class File (Version " + majorVersion + "." + minorVersion +
424 ") for class " + thisClass.arg1);
425 }
426
427 /***
428 * Print the name of a class in "canonical form".
429 *
430 * @param s Name of the class to print.
431 */
432 private String printClassName(String s) {
433 StringBuffer x;
434
435 if ( s.charAt(0) == '[' ) {
436 return(typeString(s, ""));
437 }
438
439 x = new StringBuffer();
440 for ( int j = 0 ; j < s.length() ; j++ ) {
441 if ( s.charAt(j) == '/' ) {
442 x.append('.');
443 }
444 else {
445 x.append(s.charAt(j));
446 }
447 }
448 return (x.toString());
449 }
450
451 /***
452 * Gets all the dependencies from the method <code>getAllDeoendencies</code>
453 * and filters out the classes so that only the packages appears in the
454 * string.
455 *
456 * @return ArrayList contining all the packages which are "needed" by the
457 * specified class.
458 * @throws InvalidClassFileException If the there is something wrong with
459 * the .class file.
460 */
461 private ArrayList getAllPackDepend() throws InvalidClassFileException {
462
463 ArrayList depend = getAllDependencies();
464 ListIterator iterator = depend.listIterator();
465
466 ArrayList packDepend = new ArrayList(depend.size());
467
468 while( iterator.hasNext() ) {
469 String tmp = ((String)iterator.next()).trim();
470
471 if( ! packDepend.contains(tmp) ) {
472
473 if (tmp.lastIndexOf(".") != -1) {
474 packDepend.add(tmp.substring(0,tmp.lastIndexOf(".")));
475 } else {
476 packDepend.add(tmp);
477 }
478 }
479 }
480 return packDepend;
481 }
482
483
484 /***
485 * Gets all the dependencies the actual class has. That means all the
486 * classes, the actual class needs for compile or for running.<br>
487 *
488 * @return A ArrayList containing all these classes "needed" by the actual
489 * class. The vector contains string which represent the classes in
490 * in the fully qualified form. (see JVM specification)
491 * @throws InvalidClassFileException If the there is something wrong with
492 * the .class file.
493 */
494 private ArrayList getAllDependencies() throws InvalidClassFileException {
495
496 ArrayList depend = new ArrayList();
497
498 int i;
499 String myClassName;
500 String mySuperClassName;
501 String packageName = null;
502
503 if ( ! isValidClass ) {
504 throw new InvalidClassFileException();
505 }
506
507 myClassName = printClassName(thisClass.arg1.strValue);
508 mySuperClassName = printClassName(superClass.arg1.strValue);
509
510 for ( i = 1 ; i < constantPool.length ; i++ ) {
511
512 String cpentry = (constantPool[i] == null ? "null" : constantPool[i].toString());
513 LOG.debug("Constant pool entry: " + cpentry);
514
515 if ( constantPool[i] == null ) {
516 continue;
517 }
518
519 if ( (constantPool[i] == thisClass) ||
520 (constantPool[i] == superClass) ) {
521 continue;
522 }
523
524 if ( constantPool[i].type == ConstantPoolInfo.CLASS ) {
525 String s = constantPool[i].arg1.strValue;
526
527 if ( s.charAt(0) == '[' ) {
528 continue;
529 }
530
531 s = printClassName(constantPool[i].arg1.strValue);
532
533 if ( (packageName != null ) &&
534 ( s.startsWith(packageName) ) ) {
535 continue;
536 }
537 depend.add(printClassName(s).trim());
538 }
539 }
540
541 if( !mySuperClassName.equals("java.lang.Object") ) {
542 depend.add(mySuperClassName.trim());
543 }
544
545 getImplementedInterfaces();
546
547 return depend;
548 }
549
550
551 /***
552 * Get super class. If nothing explicit is extented in this class
553 * 'java.lang.Object' is returned.
554 *
555 * @return Super class in the full qualified class name.
556 */
557 public String getSuperClass(){
558 return printClassName(superClass.arg1.strValue);
559 }
560
561 /***
562 * Get the implemented interfaces by this class.
563 *
564 * @return HashSet with all the implemented interfaces in the full
565 * qualified class name. Null if there is no interface implemented
566 * in this class.
567 */
568 public HashSet getImplementedInterfaces(){
569 HashSet implSet = new HashSet();
570
571 // no interface is implemented --> return empty set
572 if ( interfaces == null ) {
573 return implSet;
574 }
575
576 // get all the implemented interfaces
577 int numOfInterfaces = interfaces.length;
578 for ( int i=0 ; i<numOfInterfaces ; i++ ) {
579 implSet.add(printClassName(interfaces[i].arg1.strValue).trim());
580 }
581 return implSet;
582 }
583
584
585 /***
586 * Helper method for substitute the slashes of the interface string with
587 * points so that all the dependency strings looks the same way.
588 *
589 * @param str String to modify.
590 * @return Modified nice string.
591 */
592 private String makeNiceString(String str){
593 StringBuffer buffer = new StringBuffer(str);
594
595 for ( int i=0 ; i<buffer.length() ; i++ ) {
596 if( buffer.charAt(i) == '/' ) {
597 buffer.setCharAt(i, '.');
598 }
599 }
600 return buffer.toString();
601 }
602 }
This page was automatically generated by Maven