1 16 17 package org.cojen.classfile; 18 19 import java.io.Serializable ; 20 import java.io.Externalizable ; 21 import java.io.ObjectOutput ; 22 import java.io.ObjectInput ; 23 import java.io.IOException ; 24 import java.io.ObjectStreamException ; 25 import java.lang.ref.SoftReference ; 26 import java.lang.reflect.Array ; 27 import java.util.Collections ; 28 import java.util.Map ; 29 import org.cojen.util.SoftValuedHashMap; 30 import org.cojen.util.WeakCanonicalSet; 31 import org.cojen.util.WeakIdentityMap; 32 33 40 public abstract class TypeDesc extends Descriptor implements Serializable { 41 45 public final static int 46 OBJECT_CODE = 0, 47 VOID_CODE = 1, 48 BOOLEAN_CODE = 4, 49 CHAR_CODE = 5, 50 FLOAT_CODE = 6, 51 DOUBLE_CODE = 7, 52 BYTE_CODE = 8, 53 SHORT_CODE = 9, 54 INT_CODE = 10, 55 LONG_CODE = 11; 56 57 58 public final static TypeDesc VOID; 59 60 public final static TypeDesc BOOLEAN; 61 62 public final static TypeDesc CHAR; 63 64 public final static TypeDesc BYTE; 65 66 public final static TypeDesc SHORT; 67 68 public final static TypeDesc INT; 69 70 public final static TypeDesc LONG; 71 72 public final static TypeDesc FLOAT; 73 74 public final static TypeDesc DOUBLE; 75 76 77 public final static TypeDesc OBJECT; 78 79 public final static TypeDesc STRING; 80 81 final static WeakCanonicalSet cInstances; 83 84 private final static Map cClassesToInstances; 86 87 private final static Map cNamesToInstances; 89 90 private final static Map cDescriptorsToInstances; 92 93 static { 94 cInstances = new WeakCanonicalSet(); 95 96 cClassesToInstances = Collections.synchronizedMap(new WeakIdentityMap()); 97 cNamesToInstances = Collections.synchronizedMap(new SoftValuedHashMap()); 98 cDescriptorsToInstances = Collections.synchronizedMap(new SoftValuedHashMap()); 99 100 VOID = intern(new PrimitiveType("V", VOID_CODE)); 101 BOOLEAN = intern(new PrimitiveType("Z", BOOLEAN_CODE)); 102 CHAR = intern(new PrimitiveType("C", CHAR_CODE)); 103 BYTE = intern(new PrimitiveType("B", BYTE_CODE)); 104 SHORT = intern(new PrimitiveType("S", SHORT_CODE)); 105 INT = intern(new PrimitiveType("I", INT_CODE)); 106 LONG = intern(new PrimitiveType("J", LONG_CODE)); 107 FLOAT = intern(new PrimitiveType("F", FLOAT_CODE)); 108 DOUBLE = intern(new PrimitiveType("D", DOUBLE_CODE)); 109 110 OBJECT = forClass("java.lang.Object"); 111 STRING = forClass("java.lang.String"); 112 } 113 114 static TypeDesc intern(TypeDesc type) { 115 return (TypeDesc)cInstances.put(type); 116 } 117 118 121 public static TypeDesc forClass(final Class clazz) { 122 if (clazz == null) { 123 return null; 124 } 125 126 TypeDesc type = (TypeDesc)cClassesToInstances.get(clazz); 127 128 if (type == null || type.toClass() != clazz) { 129 if (clazz.isArray()) { 130 type = forClass(clazz.getComponentType()).toArrayType(); 131 } else if (clazz.isPrimitive()) { 132 if (clazz == int.class) { 133 type = INT; 134 } else if (clazz == boolean.class) { 135 type = BOOLEAN; 136 } else if (clazz == char.class) { 137 type = CHAR; 138 } else if (clazz == byte.class) { 139 type = BYTE; 140 } else if (clazz == long.class) { 141 type = LONG; 142 } else if (clazz == float.class) { 143 type = FLOAT; 144 } else if (clazz == double.class) { 145 type = DOUBLE; 146 } else if (clazz == short.class) { 147 type = SHORT; 148 } else if (clazz == void.class) { 149 type = VOID; 150 } 151 } else { 152 String name = clazz.getName(); 153 type = intern(new ObjectType(generateDescriptor(name), name)); 154 } 155 156 if (type.toClass() != clazz) { 157 type = new ObjectType(type.getDescriptor(), clazz.getName()); 158 ((ObjectType) type).setClass(clazz); 159 } 160 161 synchronized (cClassesToInstances) { 162 if (cClassesToInstances.containsKey(clazz)) { 163 type = (TypeDesc)cClassesToInstances.get(clazz); 164 } else { 165 cClassesToInstances.put(clazz, type); 166 } 167 } 168 } 169 170 return type; 171 } 172 173 177 public static TypeDesc forClass(final String name) throws IllegalArgumentException { 178 if (name.length() < 1) { 179 throw invalidName(name); 180 } 181 182 184 TypeDesc type = (TypeDesc)cNamesToInstances.get(name); 185 if (type != null) { 186 return type; 187 } 188 189 int index1 = name.lastIndexOf('['); 190 int index2 = name.lastIndexOf(']'); 191 if (index2 >= 0) { 192 if (index2 + 1 != name.length() || index1 + 1 != index2) { 193 throw invalidName(name); 194 } 195 try { 196 type = forClass(name.substring(0, index1)).toArrayType(); 197 } catch (IllegalArgumentException e) { 198 throw invalidName(name); 199 } 200 } else if (index1 >= 0) { 201 throw invalidName(name); 202 } else { 203 setType: { 204 switch (name.charAt(0)) { 205 case 'v': 206 if (name.equals("void")) { 207 type = VOID; 208 break setType; 209 } 210 break; 211 case 'b': 212 if (name.equals("boolean")) { 213 type = BOOLEAN; 214 break setType; 215 } else if (name.equals("byte")) { 216 type = BYTE; 217 break setType; 218 } 219 break; 220 case 'c': 221 if (name.equals("char")) { 222 type = CHAR; 223 break setType; 224 } 225 break; 226 case 's': 227 if (name.equals("short")) { 228 type = SHORT; 229 break setType; 230 } 231 break; 232 case 'i': 233 if (name.equals("int")) { 234 type = INT; 235 break setType; 236 } 237 break; 238 case 'l': 239 if (name.equals("long")) { 240 type = LONG; 241 break setType; 242 } 243 break; 244 case 'f': 245 if (name.equals("float")) { 246 type = FLOAT; 247 break setType; 248 } 249 break; 250 case 'd': 251 if (name.equals("double")) { 252 type = DOUBLE; 253 break setType; 254 } 255 break; 256 } 257 258 String desc = generateDescriptor(name); 259 if (name.indexOf('/') < 0) { 260 type = new ObjectType(desc, name); 261 } else { 262 type = new ObjectType(desc, name.replace('/', '.')); 263 } 264 type = intern(type); 265 } 266 } 267 268 cNamesToInstances.put(name, type); 269 return type; 270 } 271 272 private static IllegalArgumentException invalidName(String name) { 273 return new IllegalArgumentException ("Invalid name: " + name); 274 } 275 276 280 public static TypeDesc forDescriptor(final String desc) throws IllegalArgumentException { 281 TypeDesc type = (TypeDesc)cDescriptorsToInstances.get(desc); 282 if (type != null) { 283 return type; 284 } 285 286 288 String rootDesc = desc; 289 int cursor = 0; 290 int dim = 0; 291 try { 292 char c; 293 while ((c = rootDesc.charAt(cursor++)) == '[') { 294 dim++; 295 } 296 297 switch (c) { 298 case 'V': 299 type = VOID; 300 break; 301 case 'Z': 302 type = BOOLEAN; 303 break; 304 case 'C': 305 type = CHAR; 306 break; 307 case 'B': 308 type = BYTE; 309 break; 310 case 'S': 311 type = SHORT; 312 break; 313 case 'I': 314 type = INT; 315 break; 316 case 'J': 317 type = LONG; 318 break; 319 case 'F': 320 type = FLOAT; 321 break; 322 case 'D': 323 type = DOUBLE; 324 break; 325 case 'L': 326 if (dim > 0) { 327 rootDesc = rootDesc.substring(dim); 328 cursor = 1; 329 } 330 StringBuffer name = new StringBuffer (rootDesc.length() - 2); 331 while ((c = rootDesc.charAt(cursor++)) != ';') { 332 if (c == '/') { 333 c = '.'; 334 } 335 name.append(c); 336 } 337 type = intern(new ObjectType(rootDesc, name.toString())); 338 break; 339 default: 340 throw invalidDescriptor(desc); 341 } 342 } catch (NullPointerException e) { 343 throw invalidDescriptor(desc); 344 } catch (IndexOutOfBoundsException e) { 345 throw invalidDescriptor(desc); 346 } 347 348 if (cursor != rootDesc.length()) { 349 throw invalidDescriptor(desc); 350 } 351 352 while (--dim >= 0) { 353 type = type.toArrayType(); 354 } 355 356 cDescriptorsToInstances.put(desc, type); 357 return type; 358 } 359 360 private static IllegalArgumentException invalidDescriptor(String desc) { 361 return new IllegalArgumentException ("Invalid descriptor: " + desc); 362 } 363 364 private static String generateDescriptor(String classname) { 365 int length = classname.length(); 366 char[] buf = new char[length + 2]; 367 buf[0] = 'L'; 368 classname.getChars(0, length, buf, 1); 369 int i; 370 for (i=1; i<=length; i++) { 371 char c = buf[i]; 372 if (c == '.') { 373 buf[i] = '/'; 374 } 375 } 376 buf[i] = ';'; 377 return new String (buf); 378 } 379 380 transient final String mDescriptor; 381 382 TypeDesc(String desc) { 383 mDescriptor = desc; 384 } 385 386 389 public final String getDescriptor() { 390 return mDescriptor; 391 } 392 393 396 398 403 public abstract String getRootName(); 404 405 410 public abstract String getFullName(); 411 412 415 418 422 public abstract int getTypeCode(); 423 424 427 public abstract boolean isPrimitive(); 428 429 432 public abstract boolean isDoubleWord(); 433 434 437 public abstract boolean isArray(); 438 439 443 public abstract int getDimensions(); 444 445 449 public abstract TypeDesc getComponentType(); 450 451 455 public abstract TypeDesc getRootComponentType(); 456 457 461 public abstract TypeDesc toArrayType(); 462 463 468 public abstract TypeDesc toObjectType(); 469 470 476 public abstract TypeDesc toPrimitiveType(); 477 478 482 public abstract Class toClass(); 483 484 489 public abstract Class toClass(ClassLoader loader); 490 491 public String toString() { 492 return mDescriptor; 494 } 495 496 public int hashCode() { 497 return mDescriptor.hashCode(); 498 } 499 500 public boolean equals(Object other) { 501 if (this == other) { 502 return true; 503 } 504 if (other instanceof TypeDesc) { 505 return ((TypeDesc)other).mDescriptor.equals(mDescriptor); 506 } 507 return false; 508 } 509 510 Object writeReplace() throws ObjectStreamException { 511 return new External(mDescriptor); 512 } 513 514 private static class PrimitiveType extends TypeDesc { 515 private transient final int mCode; 516 private transient TypeDesc mArrayType; 517 private transient TypeDesc mObjectType; 518 519 PrimitiveType(String desc, int code) { 520 super(desc); 521 mCode = code; 522 } 523 524 public String getRootName() { 525 switch (mCode) { 526 default: 527 case VOID_CODE: 528 return "void"; 529 case BOOLEAN_CODE: 530 return "boolean"; 531 case CHAR_CODE: 532 return "char"; 533 case BYTE_CODE: 534 return "byte"; 535 case SHORT_CODE: 536 return "short"; 537 case INT_CODE: 538 return "int"; 539 case LONG_CODE: 540 return "long"; 541 case FLOAT_CODE: 542 return "float"; 543 case DOUBLE_CODE: 544 return "double"; 545 } 546 } 547 548 public String getFullName() { 549 return getRootName(); 550 } 551 552 public int getTypeCode() { 553 return mCode; 554 } 555 556 public boolean isPrimitive() { 557 return true; 558 } 559 560 public boolean isDoubleWord() { 561 return mCode == DOUBLE_CODE || mCode == LONG_CODE; 562 } 563 564 public boolean isArray() { 565 return false; 566 } 567 568 public int getDimensions() { 569 return 0; 570 } 571 572 public TypeDesc getComponentType() { 573 return null; 574 } 575 576 public TypeDesc getRootComponentType() { 577 return null; 578 } 579 580 public TypeDesc toArrayType() { 581 if (mArrayType == null) { 582 char[] buf = new char[2]; 583 buf[0] = '['; 584 buf[1] = mDescriptor.charAt(0); 585 mArrayType = intern(new ArrayType(new String (buf), this)); 586 } 587 return mArrayType; 588 } 589 590 public TypeDesc toObjectType() { 591 if (mObjectType == null) { 592 switch (mCode) { 593 default: 594 case VOID_CODE: 595 mObjectType = forClass("java.lang.Void"); 596 break; 597 case BOOLEAN_CODE: 598 mObjectType = forClass("java.lang.Boolean"); 599 break; 600 case CHAR_CODE: 601 mObjectType = forClass("java.lang.Character"); 602 break; 603 case BYTE_CODE: 604 mObjectType = forClass("java.lang.Byte"); 605 break; 606 case SHORT_CODE: 607 mObjectType = forClass("java.lang.Short"); 608 break; 609 case INT_CODE: 610 mObjectType = forClass("java.lang.Integer"); 611 break; 612 case LONG_CODE: 613 mObjectType = forClass("java.lang.Long"); 614 break; 615 case FLOAT_CODE: 616 mObjectType = forClass("java.lang.Float"); 617 break; 618 case DOUBLE_CODE: 619 mObjectType = forClass("java.lang.Double"); 620 break; 621 } 622 } 623 return mObjectType; 624 } 625 626 public TypeDesc toPrimitiveType() { 627 return this; 628 } 629 630 public Class toClass() { 631 switch (mCode) { 632 default: 633 case VOID_CODE: 634 return void.class; 635 case BOOLEAN_CODE: 636 return boolean.class; 637 case CHAR_CODE: 638 return char.class; 639 case BYTE_CODE: 640 return byte.class; 641 case SHORT_CODE: 642 return short.class; 643 case INT_CODE: 644 return int.class; 645 case LONG_CODE: 646 return long.class; 647 case FLOAT_CODE: 648 return float.class; 649 case DOUBLE_CODE: 650 return double.class; 651 } 652 } 653 654 public Class toClass(ClassLoader loader) { 655 return toClass(); 656 } 657 } 658 659 private static class ObjectType extends TypeDesc { 660 private transient final String mName; 661 private transient TypeDesc mArrayType; 662 private transient TypeDesc mPrimitiveType; 663 664 private transient SoftReference mClassRef; 667 668 ObjectType(String desc, String name) { 669 super(desc); 670 mName = name; 671 } 672 673 public String getRootName() { 674 return mName; 675 } 676 677 public String getFullName() { 678 return mName; 679 } 680 681 public int getTypeCode() { 682 return OBJECT_CODE; 683 } 684 685 public boolean isPrimitive() { 686 return false; 687 } 688 689 public boolean isDoubleWord() { 690 return false; 691 } 692 693 public boolean isArray() { 694 return false; 695 } 696 697 public int getDimensions() { 698 return 0; 699 } 700 701 public TypeDesc getComponentType() { 702 return null; 703 } 704 705 public TypeDesc getRootComponentType() { 706 return null; 707 } 708 709 public TypeDesc toArrayType() { 710 if (mArrayType == null) { 711 int length = mDescriptor.length(); 712 char[] buf = new char[length + 1]; 713 buf[0] = '['; 714 mDescriptor.getChars(0, length, buf, 1); 715 mArrayType = intern(new ArrayType(new String (buf), this)); 716 } 717 return mArrayType; 718 } 719 720 public TypeDesc toObjectType() { 721 return this; 722 } 723 724 public TypeDesc toPrimitiveType() { 725 if (mPrimitiveType == null) { 726 String name = mName; 727 if (name.startsWith("java.lang.") && name.length() > 10) { 728 switch (name.charAt(10)) { 729 case 'V': 730 if (name.equals("java.lang.Void")) { 731 mPrimitiveType = VOID; 732 } 733 break; 734 case 'B': 735 if (name.equals("java.lang.Boolean")) { 736 mPrimitiveType = BOOLEAN; 737 } else if (name.equals("java.lang.Byte")) { 738 mPrimitiveType = BYTE; 739 } 740 break; 741 case 'C': 742 if (name.equals("java.lang.Character")) { 743 mPrimitiveType = CHAR; 744 } 745 break; 746 case 'S': 747 if (name.equals("java.lang.Short")) { 748 mPrimitiveType = SHORT; 749 } 750 break; 751 case 'I': 752 if (name.equals("java.lang.Integer")) { 753 mPrimitiveType = INT; 754 } 755 break; 756 case 'L': 757 if (name.equals("java.lang.Long")) { 758 mPrimitiveType = LONG; 759 } 760 break; 761 case 'F': 762 if (name.equals("java.lang.Float")) { 763 mPrimitiveType = FLOAT; 764 } 765 break; 766 case 'D': 767 if (name.equals("java.lang.Double")) { 768 mPrimitiveType = DOUBLE; 769 } 770 break; 771 } 772 } 773 } 774 775 return mPrimitiveType; 776 } 777 778 public final synchronized Class toClass() { 779 Class clazz; 780 if (mClassRef != null) { 781 clazz = (Class )mClassRef.get(); 782 if (clazz != null) { 783 return clazz; 784 } 785 } 786 clazz = toClass(null); 787 mClassRef = new SoftReference (clazz); 788 return clazz; 789 } 790 791 public Class toClass(ClassLoader loader) { 792 TypeDesc type = toPrimitiveType(); 793 if (type != null) { 794 switch (type.getTypeCode()) { 795 default: 796 case VOID_CODE: 797 return Void .class; 798 case BOOLEAN_CODE: 799 return Boolean .class; 800 case CHAR_CODE: 801 return Character .class; 802 case FLOAT_CODE: 803 return Float .class; 804 case DOUBLE_CODE: 805 return Double .class; 806 case BYTE_CODE: 807 return Byte .class; 808 case SHORT_CODE: 809 return Short .class; 810 case INT_CODE: 811 return Integer .class; 812 case LONG_CODE: 813 return Long .class; 814 } 815 } 816 817 try { 818 if (loader == null) { 819 return Class.forName(mName); 820 } else { 821 return loader.loadClass(mName); 822 } 823 } catch (ClassNotFoundException e) { 824 return null; 825 } 826 } 827 828 void setClass(Class clazz) { 829 mClassRef = new SoftReference (clazz); 830 } 831 } 832 833 private static class ArrayType extends ObjectType { 834 private transient final TypeDesc mComponent; 835 private transient final String mFullName; 836 837 ArrayType(String desc, TypeDesc component) { 838 super(desc, component.getRootName()); 839 mComponent = component; 840 mFullName = component.getFullName().concat("[]"); 841 } 842 843 public String getFullName() { 844 return mFullName; 845 } 846 847 public boolean isArray() { 848 return true; 849 } 850 851 public int getDimensions() { 852 return mComponent.getDimensions() + 1; 853 } 854 855 public TypeDesc getComponentType() { 856 return mComponent; 857 } 858 859 public TypeDesc getRootComponentType() { 860 TypeDesc type = mComponent; 861 while (type.isArray()) { 862 type = type.getComponentType(); 863 } 864 return type; 865 } 866 867 public TypeDesc toPrimitiveType() { 868 return null; 869 } 870 871 public Class toClass(ClassLoader loader) { 872 if (loader == null) { 873 return arrayClass(getRootComponentType().toClass()); 874 } else { 875 return arrayClass(getRootComponentType().toClass(loader)); 876 } 877 } 878 879 private Class arrayClass(Class clazz) { 880 if (clazz == null) { 881 return null; 882 } 883 int dim = getDimensions(); 884 try { 885 if (dim == 1) { 886 return Array.newInstance(clazz, 0).getClass(); 887 } else { 888 return Array.newInstance(clazz, new int[dim]).getClass(); 889 } 890 } catch (IllegalArgumentException e) { 891 return null; 892 } 893 } 894 } 895 896 private static class External implements Externalizable { 897 private String mDescriptor; 898 899 public External() { 900 } 901 902 public External(String desc) { 903 mDescriptor = desc; 904 } 905 906 public void writeExternal(ObjectOutput out) throws IOException { 907 out.writeUTF(mDescriptor); 908 } 909 910 public void readExternal(ObjectInput in) throws IOException { 911 mDescriptor = in.readUTF(); 912 } 913 914 public Object readResolve() throws ObjectStreamException { 915 return forDescriptor(mDescriptor); 916 } 917 } 918 } 919 | Popular Tags |