1 16 package com.google.gwt.core.ext.typeinfo; 17 18 import com.google.gwt.core.ext.TreeLogger; 19 import com.google.gwt.core.ext.UnableToCompleteException; 20 21 import java.util.ArrayList ; 22 import java.util.Arrays ; 23 import java.util.Comparator ; 24 import java.util.HashMap ; 25 import java.util.HashSet ; 26 import java.util.IdentityHashMap ; 27 import java.util.Iterator ; 28 import java.util.List ; 29 import java.util.Map ; 30 import java.util.Set ; 31 32 54 public class TypeOracle { 55 56 61 public static final String TAG_TYPEARGS = "gwt.typeArgs"; 62 63 static final int MOD_ABSTRACT = 0x00000001; 64 static final int MOD_FINAL = 0x00000002; 65 static final int MOD_NATIVE = 0x00000004; 66 static final int MOD_PRIVATE = 0x00000008; 67 static final int MOD_PROTECTED = 0x00000010; 68 static final int MOD_PUBLIC = 0x00000020; 69 static final int MOD_STATIC = 0x00000040; 70 static final int MOD_TRANSIENT = 0x00000080; 71 static final int MOD_VOLATILE = 0x00000100; 72 73 static final JClassType[] NO_JCLASSES = new JClassType[0]; 74 static final JConstructor[] NO_JCTORS = new JConstructor[0]; 75 static final JField[] NO_JFIELDS = new JField[0]; 76 static final JMethod[] NO_JMETHODS = new JMethod[0]; 77 static final JPackage[] NO_JPACKAGES = new JPackage[0]; 78 static final JParameter[] NO_JPARAMS = new JParameter[0]; 79 static final JType[] NO_JTYPES = new JType[0]; 80 static final String [][] NO_STRING_ARR_ARR = new String [0][]; 81 static final String [] NO_STRINGS = new String [0]; 82 83 static String combine(String [] strings, int startIndex) { 84 StringBuffer sb = new StringBuffer (); 85 for (int i = startIndex; i < strings.length; i++) { 86 String s = strings[i]; 87 sb.append(s); 88 } 89 return sb.toString(); 90 } 91 92 static String [] modifierBitsToNames(int bits) { 93 List strings = new ArrayList (); 94 95 if (0 != (bits & MOD_PUBLIC)) { 98 strings.add("public"); 99 } 100 101 if (0 != (bits & MOD_PRIVATE)) { 102 strings.add("private"); 103 } 104 105 if (0 != (bits & MOD_PROTECTED)) { 106 strings.add("protected"); 107 } 108 109 if (0 != (bits & MOD_STATIC)) { 110 strings.add("static"); 111 } 112 113 if (0 != (bits & MOD_ABSTRACT)) { 114 strings.add("abstract"); 115 } 116 117 if (0 != (bits & MOD_FINAL)) { 118 strings.add("final"); 119 } 120 121 if (0 != (bits & MOD_NATIVE)) { 122 strings.add("native"); 123 } 124 125 if (0 != (bits & MOD_TRANSIENT)) { 126 strings.add("transient"); 127 } 128 129 if (0 != (bits & MOD_VOLATILE)) { 130 strings.add("volatile"); 131 } 132 133 return (String []) strings.toArray(NO_STRINGS); 134 } 135 136 145 private static boolean isInvalidatedTypeRecursive(JType type, Set invalidTypes) { 146 if (type instanceof JParameterizedType) { 147 JParameterizedType parameterizedType = (JParameterizedType) type; 148 if (isInvalidatedTypeRecursive(parameterizedType.getRawType(), 149 invalidTypes)) { 150 return true; 151 } 152 153 JType[] typeArgs = parameterizedType.getTypeArgs(); 154 for (int i = 0; i < typeArgs.length; ++i) { 155 JType typeArg = typeArgs[i]; 156 157 if (isInvalidatedTypeRecursive(typeArg, invalidTypes)) { 158 return true; 159 } 160 } 161 162 return false; 163 } else { 164 return invalidTypes.contains(type); 165 } 166 } 167 168 private final Map arrayTypes = new IdentityHashMap (); 169 170 private JClassType javaLangObject; 171 172 private final Map packages = new HashMap (); 173 174 private final Map parameterizedTypes = new HashMap (); 175 176 private int reloadCount = 0; 177 178 private final Map typesByCup = new IdentityHashMap (); 179 180 public TypeOracle() { 181 getOrCreatePackage(""); 184 } 185 186 192 public JPackage findPackage(String pkgName) { 193 return (JPackage) packages.get(pkgName); 194 } 195 196 203 public JClassType findType(String name) { 204 int i = name.length() - 1; 207 while (i >= 0) { 208 int dot = name.lastIndexOf('.', i); 209 String pkgName = ""; 210 String typeName = name; 211 if (dot != -1) { 212 pkgName = name.substring(0, dot); 213 typeName = name.substring(dot + 1); 214 i = dot - 1; 215 } else { 216 i = -1; 217 } 218 JClassType result = findType(pkgName, typeName); 219 if (result != null) { 220 return result; 221 } 222 } 223 return null; 224 } 225 226 233 public JClassType findType(String pkgName, String typeName) { 234 JPackage pkg = findPackage(pkgName); 235 if (pkg != null) { 236 JClassType type = pkg.findType(typeName); 237 if (type != null) { 238 return type; 239 } 240 } 241 return null; 242 } 243 244 253 public JArrayType getArrayType(JType componentType) { 254 JArrayType arrayType = (JArrayType) arrayTypes.get(componentType); 255 if (arrayType == null) { 256 arrayType = new JArrayType(componentType); 257 arrayTypes.put(componentType, arrayType); 258 } 259 return arrayType; 260 } 261 262 266 public JClassType getJavaLangObject() { 267 return javaLangObject; 268 } 269 270 274 public JPackage getOrCreatePackage(String name) { 275 int i = name.lastIndexOf('.'); 276 if (i != -1) { 277 getOrCreatePackage(name.substring(0, i)); 280 } 281 282 JPackage pkg = (JPackage) packages.get(name); 283 if (pkg == null) { 284 pkg = new JPackage(name); 285 packages.put(name, pkg); 286 } 287 return pkg; 288 } 289 290 296 public JPackage getPackage(String pkgName) throws NotFoundException { 297 JPackage result = findPackage(pkgName); 298 if (result == null) { 299 throw new NotFoundException(pkgName); 300 } 301 return result; 302 } 303 304 309 public JPackage[] getPackages() { 310 return (JPackage[]) packages.values().toArray(NO_JPACKAGES); 311 } 312 313 326 public JType getParameterizedType(JClassType rawType, JType[] typeArgs) { 327 JParameterizedType parameterized = new JParameterizedType(rawType); 330 for (int i = 0; i < typeArgs.length; i++) { 331 parameterized.addTypeArg(typeArgs[i]); 332 } 333 String sig = parameterized.getParameterizedQualifiedSourceName(); 334 JParameterizedType existing = (JParameterizedType) parameterizedTypes.get(sig); 335 if (existing == null) { 336 parameterizedTypes.put(sig, parameterized); 337 existing = parameterized; 338 } 339 return existing; 340 } 341 342 public long getReloadCount() { 343 return reloadCount; 344 } 345 346 353 public JClassType getType(String name) throws NotFoundException { 354 JClassType type = findType(name); 355 if (type == null) { 356 throw new NotFoundException(name); 357 } 358 return type; 359 } 360 361 368 public JClassType getType(String pkgName, String topLevelTypeSimpleName) 369 throws NotFoundException { 370 JClassType type = findType(pkgName, topLevelTypeSimpleName); 371 if (type == null) { 372 throw new NotFoundException(pkgName + "." + topLevelTypeSimpleName); 373 } 374 return type; 375 } 376 377 382 public JClassType[] getTypes() { 383 Set allTypes = new HashSet (); 384 JPackage[] pkgs = getPackages(); 385 for (int i = 0; i < pkgs.length; i++) { 386 JPackage pkg = pkgs[i]; 387 JClassType[] types = pkg.getTypes(); 388 for (int j = 0; j < types.length; j++) { 389 JClassType type = types[j]; 390 buildAllTypesImpl(allTypes, type); 391 } 392 } 393 return (JClassType[]) allTypes.toArray(NO_JCLASSES); 394 } 395 396 public JClassType[] getTypesInCompilationUnit(CompilationUnitProvider cup) { 397 JClassType[] types = (JClassType[]) typesByCup.get(cup); 398 if (types != null) { 399 return types; 400 } else { 401 return NO_JCLASSES; 402 } 403 } 404 405 426 public JType parse(String type) throws TypeOracleException { 427 type = type.replaceAll("\\\\s", ""); 430 431 return parseImpl(type); 434 } 435 436 441 public void sort(JClassType[] types) { 442 Arrays.sort(types, new Comparator () { 443 public int compare(Object type1, Object type2) { 444 String name1 = ((JClassType) type1).getQualifiedSourceName(); 445 String name2 = ((JClassType) type2).getQualifiedSourceName(); 446 return name1.compareTo(name2); 447 } 448 }); 449 } 450 451 456 public void sort(JConstructor[] ctors) { 457 Arrays.sort(ctors, new Comparator () { 458 public int compare(Object o1, Object o2) { 459 return 0; 461 } 462 }); 463 } 464 465 470 public void sort(JField[] fields) { 471 Arrays.sort(fields, new Comparator () { 472 public int compare(Object o1, Object o2) { 473 final JField f1 = ((JField) o1); 474 final JField f2 = ((JField) o2); 475 String name1 = f1.getName(); 476 String name2 = f2.getName(); 477 return name1.compareTo(name2); 478 } 479 }); 480 } 481 482 487 public void sort(JMethod[] methods) { 488 Arrays.sort(methods, new Comparator () { 489 public int compare(Object o1, Object o2) { 490 final JMethod m1 = ((JMethod) o1); 491 final JMethod m2 = ((JMethod) o2); 492 String name1 = m1.getName(); 493 String name2 = m2.getName(); 494 return name1.compareTo(name2); 495 } 496 }); 497 } 498 499 void incrementReloadCount() { 500 reloadCount++; 501 } 502 503 509 void invalidateTypesInCompilationUnit(CompilationUnitProvider cup) { 510 Set invalidTypes = new HashSet (); 511 JClassType[] types = (JClassType[]) typesByCup.get(cup); 512 if (types == null) { 513 return; 514 } 515 516 for (int i = 0; i < types.length; i++) { 517 JClassType classTypeToInvalidate = types[i]; 518 invalidTypes.add(classTypeToInvalidate); 519 } 520 521 typesByCup.remove(cup); 522 523 removeInvalidatedArrayTypes(invalidTypes); 524 525 removeInvalidatedParameterizedTypes(invalidTypes); 526 527 removeTypes(invalidTypes); 528 } 529 530 void recordTypeInCompilationUnit(CompilationUnitProvider cup, JClassType type) { 531 JClassType[] types = (JClassType[]) typesByCup.get(cup); 532 if (types == null) { 533 types = new JClassType[] {type}; 534 } else { 535 JClassType[] temp = new JClassType[types.length + 1]; 536 System.arraycopy(types, 0, temp, 0, types.length); 537 temp[types.length] = type; 538 types = temp; 539 } 540 typesByCup.put(cup, types); 541 } 542 543 553 void refresh(TreeLogger logger) throws NotFoundException { 554 if (javaLangObject == null) { 555 javaLangObject = findType("java.lang.Object"); 556 if (javaLangObject == null) { 557 throw new NotFoundException("java.lang.Object"); 558 } 559 } 560 computeHierarchyRelationships(); 561 consumeTypeArgMetaData(logger); 562 } 563 564 private void buildAllTypesImpl(Set allTypes, JClassType type) { 565 boolean didAdd = allTypes.add(type); 566 assert (didAdd); 567 JClassType[] nestedTypes = type.getNestedTypes(); 568 for (int i = 0; i < nestedTypes.length; i++) { 569 JClassType nestedType = nestedTypes[i]; 570 buildAllTypesImpl(allTypes, nestedType); 571 } 572 } 573 574 private void computeHierarchyRelationships() { 575 JClassType[] allTypes = getTypes(); 579 for (int i = 0; i < allTypes.length; i++) { 580 JClassType type = allTypes[i]; 581 type.notifySuperTypes(); 582 } 583 } 584 585 private void consumeTypeArgMetaData(TreeLogger logger) { 586 logger = logger.branch(TreeLogger.DEBUG, "Examining " + TAG_TYPEARGS 587 + " tags", null); 588 consumeTypeArgMetaData(logger, getTypes()); 589 } 590 591 private void consumeTypeArgMetaData(TreeLogger logger, JClassType[] types) { 592 for (int i = 0; i < types.length; i++) { 593 JClassType type = types[i]; 594 596 TreeLogger branch = logger.branch(TreeLogger.DEBUG, "Type " 597 + type.getQualifiedSourceName(), null); 598 599 consumeTypeArgMetaData(branch, type.getMethods()); 600 consumeTypeArgMetaData(branch, type.getFields()); 601 } 602 } 603 604 private void consumeTypeArgMetaData(TreeLogger logger, JField[] fields) { 605 TreeLogger branch; 606 for (int i = 0; i < fields.length; i++) { 607 JField field = fields[i]; 608 609 String [][] tokensArray = field.getMetaData(TAG_TYPEARGS); 610 if (tokensArray.length == 0) { 611 continue; 613 } 614 615 try { 616 String msg = "Field " + field.getName(); 617 branch = logger.branch(TreeLogger.TRACE, msg, null); 618 619 if (tokensArray.length > 1) { 620 branch.log(TreeLogger.WARN, "Metadata error on field '" 622 + field.getName() + "' in type '" + field.getEnclosingType() 623 + "': expecting at most one " + TAG_TYPEARGS 624 + " (the last one will be used)", null); 625 } 626 627 JType fieldType = field.getType(); 632 String [] token = tokensArray[tokensArray.length - 1]; 633 JType resultingType = determineActualType(branch, fieldType, token, 0); 634 field.setType(resultingType); 635 } catch (UnableToCompleteException e) { 636 } 639 } 640 } 641 642 private void consumeTypeArgMetaData(TreeLogger logger, JMethod[] methods) { 643 TreeLogger branch; 644 for (int i = 0; i < methods.length; i++) { 645 JMethod method = methods[i]; 646 647 String [][] tokensArray = method.getMetaData(TAG_TYPEARGS); 648 if (tokensArray.length == 0) { 649 continue; 651 } 652 try { 653 String msg = "Method " + method.getReadableDeclaration(); 654 branch = logger.branch(TreeLogger.TRACE, msg, null); 655 656 boolean returnTypeHandled = false; 659 Set paramsAlreadySet = new HashSet (); 660 for (int j = 0; j < tokensArray.length; j++) { 661 String [] tokens = tokensArray[j]; 662 if (tokens.length == 0) { 665 branch.log(TreeLogger.WARN, 668 "Metadata error: expecting tokens after " + TAG_TYPEARGS, null); 669 throw new UnableToCompleteException(); 670 } 671 672 JParameter param = method.findParameter(tokens[0]); 675 if (param != null) { 676 if (!paramsAlreadySet.contains(param)) { 677 JType resultingType = determineActualType(branch, 680 param.getType(), tokens, 1); 681 param.setType(resultingType); 682 paramsAlreadySet.add(param); 683 } else { 684 msg = "Metadata error: duplicate attempt to specify type args for parameter '" 687 + param.getName() + "'"; 688 branch.log(TreeLogger.WARN, msg, null); 689 throw new UnableToCompleteException(); 690 } 691 } else { 692 if (!returnTypeHandled) { 695 JType resultingType = determineActualType(branch, 696 method.getReturnType(), tokens, 0); 697 method.setReturnType(resultingType); 698 returnTypeHandled = true; 699 } else { 700 msg = "Metadata error: duplicate attempt to specify type args for the return type"; 703 branch.log(TreeLogger.WARN, msg, null); 704 } 705 } 706 } 707 } catch (UnableToCompleteException e) { 708 } 711 } 712 } 713 714 718 private JType determineActualType(TreeLogger logger, JType declType, 719 String [] tokens, int startIndex) throws UnableToCompleteException { 720 JType leafType = declType.getLeafType(); 723 String typeName = leafType.getQualifiedSourceName(); 724 JType resultingType = parseTypeArgTokens(logger, typeName, tokens, 725 startIndex); 726 JArrayType arrayType = declType.isArray(); 727 if (arrayType != null) { 728 arrayType.setLeafType(resultingType); 729 730 return declType; 731 } 732 733 return resultingType; 734 } 735 736 private JType parseImpl(String type) throws NotFoundException, 737 ParseException, BadTypeArgsException { 738 if (type.endsWith("[]")) { 739 String remainder = type.substring(0, type.length() - 2); 740 JType componentType = parseImpl(remainder); 741 return getArrayType(componentType); 742 } 743 744 if (type.endsWith(">")) { 745 int bracket = type.indexOf('<'); 746 if (bracket == -1) { 747 throw new ParseException( 748 "Mismatched brackets; expected '<' to match subsequent '>'"); 749 } 750 751 String rawTypeName = type.substring(0, bracket); 754 JType rawType = parseImpl(rawTypeName); 755 if (rawType.isParameterized() != null) { 756 throw new BadTypeArgsException( 759 "Only non-parameterized classes and interface can be parameterized"); 760 } else if (rawType.isClassOrInterface() == null) { 761 throw new BadTypeArgsException( 765 "Only classes and interface can be parameterized, so " 766 + rawType.getQualifiedSourceName() 767 + " cannot be used in this context"); 768 } 769 770 String typeArgContents = type.substring(bracket + 1, type.length() - 1); 773 JType[] typeArgs = parseTypeArgContents(typeArgContents); 774 775 return getParameterizedType(rawType.isClassOrInterface(), typeArgs); 778 } 779 780 JType result = JPrimitiveType.valueOf(type); 781 if (result != null) { 782 return result; 783 } 784 785 result = findType(type); 786 if (result != null) { 787 return result; 788 } 789 790 throw new NotFoundException(type); 791 } 792 793 private void parseTypeArgComponent(List typeArgList, String typeArgComponent) 794 throws NotFoundException, ParseException, BadTypeArgsException { 795 JType typeArg = parseImpl(typeArgComponent); 796 if (typeArg.isPrimitive() != null) { 797 throw new BadTypeArgsException("Type arguments cannot be primitive, so " 800 + typeArg.getQualifiedSourceName() 801 + " cannot be used in this context"); 802 } 803 804 typeArgList.add(typeArg); 805 } 806 807 811 private JType[] parseTypeArgContents(String typeArgContents) 812 throws ParseException, NotFoundException, BadTypeArgsException { 813 List typeArgList = new ArrayList (); 814 815 int start = 0; 816 for (int offset = 0, length = typeArgContents.length(); offset < length; ++offset) { 817 char ch = typeArgContents.charAt(offset); 818 switch (ch) { 819 case '<': 820 for (int depth = 1; depth > 0; ) { 822 if (++offset == length) { 823 throw new ParseException( 824 "Mismatched brackets; expected '<' to match subsequent '>'"); 825 } 826 827 char ich = typeArgContents.charAt(offset); 828 if (ich == '<') { 829 ++depth; 830 } else if (ich == '>') { 831 --depth; 832 } 833 } 834 break; 835 case '>': 836 throw new ParseException("No matching '<' for '>'"); 837 case ',': 838 String typeArgComponent = typeArgContents.substring(start, offset); 839 parseTypeArgComponent(typeArgList, typeArgComponent); 840 start = offset + 1; 841 break; 842 default: 843 break; 844 } 845 } 846 847 String typeArgComponent = typeArgContents.substring(start); 848 parseTypeArgComponent(typeArgList, typeArgComponent); 849 850 JType[] typeArgs = (JType[]) typeArgList.toArray(new JType[typeArgList.size()]); 851 return typeArgs; 852 } 853 854 private JType parseTypeArgTokens(TreeLogger logger, String maybeRawType, 855 String [] tokens, int startIndex) throws UnableToCompleteException { 856 String munged = combine(tokens, startIndex).trim(); 857 String toParse = maybeRawType + munged; 858 JType parameterizedType; 859 try { 860 parameterizedType = parse(toParse); 861 } catch (TypeOracleException e) { 862 String msg = "Unable to recognize '" + toParse 863 + "' as a type name (is it fully qualified?)"; 864 logger.log(TreeLogger.WARN, msg, null); 865 throw new UnableToCompleteException(); 866 } 867 return parameterizedType; 868 } 869 870 875 private void removeInvalidatedArrayTypes(Set invalidTypes) { 876 arrayTypes.keySet().removeAll(invalidTypes); 877 } 878 879 885 private void removeInvalidatedParameterizedTypes(Set invalidTypes) { 886 Iterator iter = parameterizedTypes.values().iterator(); 887 888 while (iter.hasNext()) { 889 JType type = (JType) iter.next(); 890 891 if (isInvalidatedTypeRecursive(type, invalidTypes)) { 892 iter.remove(); 893 } 894 } 895 } 896 897 902 private void removeTypes(Set invalidTypes) { 903 Iterator iter = invalidTypes.iterator(); 904 905 while (iter.hasNext()) { 906 JClassType classType = (JClassType) iter.next(); 907 JPackage pkg = classType.getPackage(); 908 if (pkg != null) { 909 pkg.remove(classType); 910 } 911 912 classType.removeFromSupertypes(); 913 } 914 } 915 } 916 | Popular Tags |