View Javadoc
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