1 package jdepend.framework; 2 3 import java.io.*; 4 import java.util.*; 5 6 14 15 public class ClassFileParser extends AbstractParser { 16 17 public static final int JAVA_MAGIC = 0xCAFEBABE; 18 public static final int CONSTANT_UTF8 = 1; 19 public static final int CONSTANT_UNICODE = 2; 20 public static final int CONSTANT_INTEGER = 3; 21 public static final int CONSTANT_FLOAT = 4; 22 public static final int CONSTANT_LONG = 5; 23 public static final int CONSTANT_DOUBLE = 6; 24 public static final int CONSTANT_CLASS = 7; 25 public static final int CONSTANT_STRING = 8; 26 public static final int CONSTANT_FIELD = 9; 27 public static final int CONSTANT_METHOD = 10; 28 public static final int CONSTANT_INTERFACEMETHOD = 11; 29 public static final int CONSTANT_NAMEANDTYPE = 12; 30 public static final char CLASS_DESCRIPTOR = 'L'; 31 public static final int ACC_INTERFACE = 0x200; 32 public static final int ACC_ABSTRACT = 0x400; 33 34 private String fileName; 35 private String className; 36 private String superClassName; 37 private String interfaceNames[]; 38 private boolean isAbstract; 39 private JavaClass jClass; 40 private Constant[] constantPool; 41 private FieldOrMethodInfo[] fields; 42 private FieldOrMethodInfo[] methods; 43 private AttributeInfo[] attributes; 44 private DataInputStream in; 45 46 47 public ClassFileParser() { 48 this(new PackageFilter()); 49 } 50 51 public ClassFileParser(PackageFilter filter) { 52 super(filter); 53 reset(); 54 } 55 56 private void reset() { 57 className = null; 58 superClassName = null; 59 interfaceNames = new String [0]; 60 isAbstract = false; 61 62 jClass = null; 63 constantPool = new Constant[1]; 64 fields = new FieldOrMethodInfo[0]; 65 methods = new FieldOrMethodInfo[0]; 66 attributes = new AttributeInfo[0]; 67 } 68 69 73 public JavaClass parse(File classFile) throws IOException { 74 75 this.fileName = classFile.getCanonicalPath(); 76 77 debug("\nParsing " + fileName + "..."); 78 79 FileInputStream in = null; 80 81 try { 82 83 in = new FileInputStream(classFile); 84 85 return parse(in); 86 87 } finally { 88 if (in != null) { 89 try { 90 in.close(); 91 } catch (IOException ioe) { 92 ioe.printStackTrace(); 93 } 94 } 95 } 96 } 97 98 public JavaClass parse(InputStream is) throws IOException { 99 100 reset(); 101 102 jClass = new JavaClass("Unknown"); 103 104 in = new DataInputStream(is); 105 106 int magic = parseMagic(); 107 108 int minorVersion = parseMinorVersion(); 109 int majorVersion = parseMajorVersion(); 110 111 constantPool = parseConstantPool(); 112 113 parseAccessFlags(); 114 115 className = parseClassName(); 116 117 superClassName = parseSuperClassName(); 118 119 interfaceNames = parseInterfaces(); 120 121 fields = parseFields(); 122 123 methods = parseMethods(); 124 125 parseAttributes(); 126 127 addClassConstantReferences(); 128 129 onParsedJavaClass(jClass); 130 131 return jClass; 132 } 133 134 private int parseMagic() throws IOException { 135 int magic = in.readInt(); 136 if (magic != JAVA_MAGIC) { 137 throw new IOException("Invalid class file: " + fileName); 138 } 139 140 return magic; 141 } 142 143 private int parseMinorVersion() throws IOException { 144 return in.readUnsignedShort(); 145 } 146 147 private int parseMajorVersion() throws IOException { 148 return in.readUnsignedShort(); 149 } 150 151 private Constant[] parseConstantPool() throws IOException { 152 int constantPoolSize = in.readUnsignedShort(); 153 154 Constant[] pool = new Constant[constantPoolSize]; 155 156 for (int i = 1; i < constantPoolSize; i++) { 157 158 Constant constant = parseNextConstant(); 159 160 pool[i] = constant; 161 162 if (constant.getTag() == CONSTANT_DOUBLE 166 || constant.getTag() == CONSTANT_LONG) { 167 i++; 168 } 169 } 170 171 return pool; 172 } 173 174 private void parseAccessFlags() throws IOException { 175 int accessFlags = in.readUnsignedShort(); 176 177 boolean isAbstract = ((accessFlags & ACC_ABSTRACT) != 0); 178 boolean isInterface = ((accessFlags & ACC_INTERFACE) != 0); 179 180 this.isAbstract = isAbstract || isInterface; 181 jClass.isAbstract(this.isAbstract); 182 183 debug("Parser: abstract = " + this.isAbstract); 184 } 185 186 private String parseClassName() throws IOException { 187 int entryIndex = in.readUnsignedShort(); 188 String className = getClassConstantName(entryIndex); 189 jClass.setName(className); 190 jClass.setPackageName(getPackageName(className)); 191 192 debug("Parser: class name = " + className); 193 debug("Parser: package name = " + getPackageName(className)); 194 195 return className; 196 } 197 198 private String parseSuperClassName() throws IOException { 199 int entryIndex = in.readUnsignedShort(); 200 String superClassName = getClassConstantName(entryIndex); 201 addImport(getPackageName(superClassName)); 202 203 debug("Parser: super class name = " + superClassName); 204 205 return superClassName; 206 } 207 208 private String [] parseInterfaces() throws IOException { 209 int interfacesCount = in.readUnsignedShort(); 210 String [] interfaceNames = new String [interfacesCount]; 211 for (int i = 0; i < interfacesCount; i++) { 212 int entryIndex = in.readUnsignedShort(); 213 interfaceNames[i] = getClassConstantName(entryIndex); 214 addImport(getPackageName(interfaceNames[i])); 215 216 debug("Parser: interface = " + interfaceNames[i]); 217 } 218 219 return interfaceNames; 220 } 221 222 private FieldOrMethodInfo[] parseFields() throws IOException { 223 int fieldsCount = in.readUnsignedShort(); 224 FieldOrMethodInfo[] fields = new FieldOrMethodInfo[fieldsCount]; 225 for (int i = 0; i < fieldsCount; i++) { 226 fields[i] = parseFieldOrMethodInfo(); 227 String descriptor = toUTF8(fields[i].getDescriptorIndex()); 228 debug("Parser: field descriptor = " + descriptor); 229 String [] types = descriptorToTypes(descriptor); 230 for (int t = 0; t < types.length; t++) { 231 addImport(getPackageName(types[t])); 232 debug("Parser: field type = " + types[t]); 233 } 234 } 235 236 return fields; 237 } 238 239 private FieldOrMethodInfo[] parseMethods() throws IOException { 240 int methodsCount = in.readUnsignedShort(); 241 FieldOrMethodInfo[] methods = new FieldOrMethodInfo[methodsCount]; 242 for (int i = 0; i < methodsCount; i++) { 243 methods[i] = parseFieldOrMethodInfo(); 244 String descriptor = toUTF8(methods[i].getDescriptorIndex()); 245 debug("Parser: method descriptor = " + descriptor); 246 String [] types = descriptorToTypes(descriptor); 247 for (int t = 0; t < types.length; t++) { 248 if (types[t].length() > 0) { 249 addImport(getPackageName(types[t])); 250 debug("Parser: method type = " + types[t]); 251 } 252 } 253 } 254 255 return methods; 256 } 257 258 private Constant parseNextConstant() throws IOException { 259 260 Constant result; 261 262 byte tag = in.readByte(); 263 264 switch (tag) { 265 266 case (ClassFileParser.CONSTANT_CLASS): 267 case (ClassFileParser.CONSTANT_STRING): 268 result = new Constant(tag, in.readUnsignedShort()); 269 break; 270 case (ClassFileParser.CONSTANT_FIELD): 271 case (ClassFileParser.CONSTANT_METHOD): 272 case (ClassFileParser.CONSTANT_INTERFACEMETHOD): 273 case (ClassFileParser.CONSTANT_NAMEANDTYPE): 274 result = new Constant(tag, in.readUnsignedShort(), in 275 .readUnsignedShort()); 276 break; 277 case (ClassFileParser.CONSTANT_INTEGER): 278 result = new Constant(tag, new Integer (in.readInt())); 279 break; 280 case (ClassFileParser.CONSTANT_FLOAT): 281 result = new Constant(tag, new Float (in.readFloat())); 282 break; 283 case (ClassFileParser.CONSTANT_LONG): 284 result = new Constant(tag, new Long (in.readLong())); 285 break; 286 case (ClassFileParser.CONSTANT_DOUBLE): 287 result = new Constant(tag, new Double (in.readDouble())); 288 break; 289 case (ClassFileParser.CONSTANT_UTF8): 290 result = new Constant(tag, in.readUTF()); 291 break; 292 default: 293 throw new IOException("Unknown constant: " + tag); 294 } 295 296 return result; 297 } 298 299 private FieldOrMethodInfo parseFieldOrMethodInfo() throws IOException { 300 301 FieldOrMethodInfo result = new FieldOrMethodInfo( 302 in.readUnsignedShort(), in.readUnsignedShort(), in 303 .readUnsignedShort()); 304 305 int attributesCount = in.readUnsignedShort(); 306 for (int a = 0; a < attributesCount; a++) { 307 parseAttribute(); 308 } 309 310 return result; 311 } 312 313 private void parseAttributes() throws IOException { 314 int attributesCount = in.readUnsignedShort(); 315 attributes = new AttributeInfo[attributesCount]; 316 317 for (int i = 0; i < attributesCount; i++) { 318 attributes[i] = parseAttribute(); 319 320 if (attributes[i].getName() != null) { 322 if (attributes[i].getName().equals("SourceFile")) { 323 byte[] b = attributes[i].getValue(); 324 int b0 = b[0] < 0 ? b[0] + 256 : b[0]; 325 int b1 = b[1] < 0 ? b[1] + 256 : b[1]; 326 int pe = b0 * 256 + b1; 327 328 String descriptor = toUTF8(pe); 329 jClass.setSourceFile(descriptor); 330 } 331 } 332 } 333 } 334 335 private AttributeInfo parseAttribute() throws IOException { 336 AttributeInfo result = new AttributeInfo(); 337 338 int nameIndex = in.readUnsignedShort(); 339 if (nameIndex != -1) { 340 result.setName(toUTF8(nameIndex)); 341 } 342 343 int attributeLength = in.readInt(); 344 byte[] value = new byte[attributeLength]; 345 for (int b = 0; b < attributeLength; b++) { 346 value[b] = in.readByte(); 347 } 348 349 result.setValue(value); 350 return result; 351 } 352 353 private Constant getConstantPoolEntry(int entryIndex) throws IOException { 354 355 if (entryIndex < 0 || entryIndex >= constantPool.length) { 356 throw new IOException("Illegal constant pool index : " + entryIndex); 357 } 358 359 return constantPool[entryIndex]; 360 } 361 362 private void addClassConstantReferences() throws IOException { 363 for (int j = 1; j < constantPool.length; j++) { 364 if (constantPool[j].getTag() == CONSTANT_CLASS) { 365 String name = toUTF8(constantPool[j].getNameIndex()); 366 addImport(getPackageName(name)); 367 368 debug("Parser: class type = " + slashesToDots(name)); 369 } 370 371 if (constantPool[j].getTag() == CONSTANT_DOUBLE 372 || constantPool[j].getTag() == CONSTANT_LONG) { 373 j++; 374 } 375 } 376 } 377 378 private String getClassConstantName(int entryIndex) throws IOException { 379 380 Constant entry = getConstantPoolEntry(entryIndex); 381 if (entry == null) { 382 return ""; 383 } 384 return slashesToDots(toUTF8(entry.getNameIndex())); 385 } 386 387 private String toUTF8(int entryIndex) throws IOException { 388 Constant entry = getConstantPoolEntry(entryIndex); 389 if (entry.getTag() == CONSTANT_UTF8) { 390 return (String ) entry.getValue(); 391 } 392 393 throw new IOException("Constant pool entry is not a UTF8 type: " 394 + entryIndex); 395 } 396 397 private void addImport(String importPackage) { 398 if ((importPackage != null) && (getFilter().accept(importPackage))) { 399 jClass.addImportedPackage(new JavaPackage(importPackage)); 400 } 401 } 402 403 private String slashesToDots(String s) { 404 return s.replace('/', '.'); 405 } 406 407 private String getPackageName(String s) { 408 if ((s.length() > 0) && (s.charAt(0) == '[')) { 409 String types[] = descriptorToTypes(s); 410 if (types.length == 0) { 411 return null; } 413 414 s = types[0]; 415 } 416 417 s = slashesToDots(s); 418 int index = s.lastIndexOf("."); 419 if (index > 0) { 420 return s.substring(0, index); 421 } 422 423 return "Default"; 424 } 425 426 private String [] descriptorToTypes(String descriptor) { 427 428 int typesCount = 0; 429 for (int index = 0; index < descriptor.length(); index++) { 430 if (descriptor.charAt(index) == ';') { 431 typesCount++; 432 } 433 } 434 435 String types[] = new String [typesCount]; 436 437 int typeIndex = 0; 438 for (int index = 0; index < descriptor.length(); index++) { 439 440 int startIndex = descriptor.indexOf(CLASS_DESCRIPTOR, index); 441 if (startIndex < 0) { 442 break; 443 } 444 445 index = descriptor.indexOf(';', startIndex + 1); 446 types[typeIndex++] = descriptor.substring(startIndex + 1, index); 447 } 448 449 return types; 450 } 451 452 class Constant { 453 454 private byte _tag; 455 456 private int _nameIndex; 457 458 private int _typeIndex; 459 460 private Object _value; 461 462 Constant(byte tag, int nameIndex) { 463 this(tag, nameIndex, -1); 464 } 465 466 Constant(byte tag, Object value) { 467 this(tag, -1, -1); 468 _value = value; 469 } 470 471 Constant(byte tag, int nameIndex, int typeIndex) { 472 _tag = tag; 473 _nameIndex = nameIndex; 474 _typeIndex = typeIndex; 475 _value = null; 476 } 477 478 byte getTag() { 479 return _tag; 480 } 481 482 int getNameIndex() { 483 return _nameIndex; 484 } 485 486 int getTypeIndex() { 487 return _typeIndex; 488 } 489 490 Object getValue() { 491 return _value; 492 } 493 494 public String toString() { 495 496 StringBuffer s = new StringBuffer (""); 497 498 s.append("tag: " + getTag()); 499 500 if (getNameIndex() > -1) { 501 s.append(" nameIndex: " + getNameIndex()); 502 } 503 504 if (getTypeIndex() > -1) { 505 s.append(" typeIndex: " + getTypeIndex()); 506 } 507 508 if (getValue() != null) { 509 s.append(" value: " + getValue()); 510 } 511 512 return s.toString(); 513 } 514 } 515 516 class FieldOrMethodInfo { 517 518 private int _accessFlags; 519 520 private int _nameIndex; 521 522 private int _descriptorIndex; 523 524 FieldOrMethodInfo(int accessFlags, int nameIndex, int descriptorIndex) { 525 526 _accessFlags = accessFlags; 527 _nameIndex = nameIndex; 528 _descriptorIndex = descriptorIndex; 529 } 530 531 int accessFlags() { 532 return _accessFlags; 533 } 534 535 int getNameIndex() { 536 return _nameIndex; 537 } 538 539 int getDescriptorIndex() { 540 return _descriptorIndex; 541 } 542 543 public String toString() { 544 StringBuffer s = new StringBuffer (""); 545 546 try { 547 548 s.append("\n name (#" + getNameIndex() + ") = " 549 + toUTF8(getNameIndex())); 550 551 s.append("\n signature (#" + getDescriptorIndex() + ") = " 552 + toUTF8(getDescriptorIndex())); 553 554 String [] types = descriptorToTypes(toUTF8(getDescriptorIndex())); 555 for (int t = 0; t < types.length; t++) { 556 s.append("\n type = " + types[t]); 557 } 558 559 } catch (Exception e) { 560 e.printStackTrace(); 561 } 562 563 return s.toString(); 564 } 565 } 566 567 class AttributeInfo { 568 569 private String name; 570 571 private byte[] value; 572 573 public void setName(String name) { 574 this.name = name; 575 } 576 577 public String getName() { 578 return this.name; 579 } 580 581 public void setValue(byte[] value) { 582 this.value = value; 583 } 584 585 public byte[] getValue() { 586 return this.value; 587 } 588 } 589 590 595 public String toString() { 596 597 StringBuffer s = new StringBuffer (); 598 599 try { 600 601 s.append("\n" + className + ":\n"); 602 603 s.append("\nConstants:\n"); 604 for (int i = 1; i < constantPool.length; i++) { 605 Constant entry = getConstantPoolEntry(i); 606 s.append(" " + i + ". " + entry.toString() + "\n"); 607 if (entry.getTag() == CONSTANT_DOUBLE 608 || entry.getTag() == CONSTANT_LONG) { 609 i++; 610 } 611 } 612 613 s.append("\nClass Name: " + className + "\n"); 614 s.append("Super Name: " + superClassName + "\n\n"); 615 616 s.append(interfaceNames.length + " interfaces\n"); 617 for (int i = 0; i < interfaceNames.length; i++) { 618 s.append(" " + interfaceNames[i] + "\n"); 619 } 620 621 s.append("\n" + fields.length + " fields\n"); 622 for (int i = 0; i < fields.length; i++) { 623 s.append(fields[i].toString() + "\n"); 624 } 625 626 s.append("\n" + methods.length + " methods\n"); 627 for (int i = 0; i < methods.length; i++) { 628 s.append(methods[i].toString() + "\n"); 629 } 630 631 s.append("\nDependencies:\n"); 632 Iterator imports = jClass.getImportedPackages().iterator(); 633 while (imports.hasNext()) { 634 JavaPackage jPackage = (JavaPackage) imports.next(); 635 s.append(" " + jPackage.getName() + "\n"); 636 } 637 638 } catch (Exception e) { 639 e.printStackTrace(); 640 } 641 642 return s.toString(); 643 } 644 645 648 public static void main(String args[]) { 649 try { 650 651 ClassFileParser.DEBUG = true; 652 653 if (args.length <= 0) { 654 System.err.println("usage: ClassFileParser <class-file>"); 655 System.exit(0); 656 } 657 658 ClassFileParser parser = new ClassFileParser(); 659 660 parser.parse(new File(args[0])); 661 662 System.err.println(parser.toString()); 663 664 } catch (Exception e) { 665 System.err.println(e.getMessage()); 666 } 667 } 668 } 669 | Popular Tags |