0001: /*
0002: * Copyright 2007 Google Inc.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy of
0006: * the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations under
0014: * the License.
0015: */
0016: package com.google.gwt.core.ext.typeinfo;
0017:
0018: import com.google.gwt.core.ext.TreeLogger;
0019: import com.google.gwt.core.ext.UnableToCompleteException;
0020:
0021: import java.lang.annotation.Annotation;
0022: import java.util.ArrayList;
0023: import java.util.Arrays;
0024: import java.util.Comparator;
0025: import java.util.HashMap;
0026: import java.util.HashSet;
0027: import java.util.IdentityHashMap;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.util.Set;
0032:
0033: /**
0034: * Provides type-related information about a set of source files, including doc
0035: * comment metadata.
0036: * <p>
0037: * All type objects exposed, such as
0038: * {@link com.google.gwt.core.ext.typeinfo.JClassType} and others, have a stable
0039: * identity relative to this type oracle instance. Consequently, you can
0040: * reliably compare object identity of any objects this type oracle produces.
0041: * For example, the following code relies on this stable identity guarantee:
0042: *
0043: * <pre>
0044: * JClassType o = typeOracle.getJavaLangObject();
0045: * JClassType s1 = typeOracle.getType("java.lang.String");
0046: * JClassType s2 = typeOracle.getType("java.lang.String");
0047: * assert(s1 == s2);
0048: * assert(o == s1.getSuperclass());
0049: * JParameterizedType ls = typeOracle.parse("java.util.List<java.lang.String>");
0050: * assert(ls.getTypeArgs()[0] == s1);
0051: * </pre>
0052: *
0053: * </p>
0054: */
0055: public class TypeOracle {
0056: /**
0057: * A reserved metadata tag to indicates that a field type, method return type
0058: * or method parameter type is intended to be parameterized. Note that
0059: * constructor type parameters are not supported at present.
0060: */
0061: public static final String TAG_TYPEARGS = "gwt.typeArgs";
0062: static final int MOD_ABSTRACT = 0x00000001;
0063: static final int MOD_FINAL = 0x00000002;
0064: static final int MOD_NATIVE = 0x00000004;
0065: static final int MOD_PRIVATE = 0x00000008;
0066: static final int MOD_PROTECTED = 0x00000010;
0067: static final int MOD_PUBLIC = 0x00000020;
0068: static final int MOD_STATIC = 0x00000040;
0069: static final int MOD_TRANSIENT = 0x00000080;
0070:
0071: static final int MOD_VOLATILE = 0x00000100;
0072: static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
0073: static final JClassType[] NO_JCLASSES = new JClassType[0];
0074: static final JConstructor[] NO_JCTORS = new JConstructor[0];
0075: static final JField[] NO_JFIELDS = new JField[0];
0076: static final JMethod[] NO_JMETHODS = new JMethod[0];
0077: static final JPackage[] NO_JPACKAGES = new JPackage[0];
0078: static final JParameter[] NO_JPARAMS = new JParameter[0];
0079: static final JType[] NO_JTYPES = new JType[0];
0080: static final String[][] NO_STRING_ARR_ARR = new String[0][];
0081: static final String[] NO_STRINGS = new String[0];
0082:
0083: static String combine(String[] strings, int startIndex) {
0084: StringBuffer sb = new StringBuffer();
0085: for (int i = startIndex; i < strings.length; i++) {
0086: String s = strings[i];
0087: sb.append(s);
0088: }
0089: return sb.toString();
0090: }
0091:
0092: static String[] modifierBitsToNames(int bits) {
0093: List<String> strings = new ArrayList<String>();
0094:
0095: // The order is based on the order in which we want them to appear.
0096: //
0097: if (0 != (bits & MOD_PUBLIC)) {
0098: strings.add("public");
0099: }
0100:
0101: if (0 != (bits & MOD_PRIVATE)) {
0102: strings.add("private");
0103: }
0104:
0105: if (0 != (bits & MOD_PROTECTED)) {
0106: strings.add("protected");
0107: }
0108:
0109: if (0 != (bits & MOD_STATIC)) {
0110: strings.add("static");
0111: }
0112:
0113: if (0 != (bits & MOD_ABSTRACT)) {
0114: strings.add("abstract");
0115: }
0116:
0117: if (0 != (bits & MOD_FINAL)) {
0118: strings.add("final");
0119: }
0120:
0121: if (0 != (bits & MOD_NATIVE)) {
0122: strings.add("native");
0123: }
0124:
0125: if (0 != (bits & MOD_TRANSIENT)) {
0126: strings.add("transient");
0127: }
0128:
0129: if (0 != (bits & MOD_VOLATILE)) {
0130: strings.add("volatile");
0131: }
0132:
0133: return strings.toArray(NO_STRINGS);
0134: }
0135:
0136: /**
0137: * Returns true if the type has been invalidated because it is in the set of
0138: * invalid types or if it is a parameterized type and either it raw type or
0139: * any one of its type args has been invalidated.
0140: *
0141: * @param type type to check
0142: * @param invalidTypes set of type known to be invalid
0143: * @return true if the type has been invalidated
0144: */
0145: private static boolean isInvalidatedTypeRecursive(JType type,
0146: Set<JClassType> invalidTypes) {
0147: if (type instanceof JParameterizedType) {
0148: JParameterizedType parameterizedType = (JParameterizedType) type;
0149: if (isInvalidatedTypeRecursive(parameterizedType
0150: .getBaseType(), invalidTypes)) {
0151: return true;
0152: }
0153:
0154: JType[] typeArgs = parameterizedType.getTypeArgs();
0155: for (int i = 0; i < typeArgs.length; ++i) {
0156: JType typeArg = typeArgs[i];
0157:
0158: if (isInvalidatedTypeRecursive(typeArg, invalidTypes)) {
0159: return true;
0160: }
0161: }
0162:
0163: return false;
0164: } else {
0165: return invalidTypes.contains(type);
0166: }
0167: }
0168:
0169: private final Map<JType, JArrayType> arrayTypes = new IdentityHashMap<JType, JArrayType>();
0170:
0171: private JClassType javaLangObject;
0172:
0173: private final Map<String, JPackage> packages = new HashMap<String, JPackage>();
0174:
0175: private final Map<String, List<JParameterizedType>> parameterizedTypes = new HashMap<String, List<JParameterizedType>>();
0176:
0177: private int reloadCount = 0;
0178:
0179: private final Map<CompilationUnitProvider, JClassType[]> typesByCup = new IdentityHashMap<CompilationUnitProvider, JClassType[]>();
0180:
0181: private final Map<String, List<JWildcardType>> wildcardTypes = new HashMap<String, List<JWildcardType>>();
0182:
0183: public TypeOracle() {
0184: // Always create the default package.
0185: //
0186: getOrCreatePackage("");
0187: }
0188:
0189: /**
0190: * Attempts to find a package by name. All requests for the same package
0191: * return the same package object.
0192: *
0193: * @return <code>null</code> if the package could not be found
0194: */
0195: public JPackage findPackage(String pkgName) {
0196: return packages.get(pkgName);
0197: }
0198:
0199: /**
0200: * Finds a class or interface given its fully-qualified name. For nested
0201: * classes, use its source name rather than its binary name (that is, use a
0202: * "." rather than a "$").
0203: *
0204: * @return <code>null</code> if the type is not found
0205: */
0206: public JClassType findType(String name) {
0207: // Try the dotted pieces, right to left.
0208: //
0209: int i = name.length() - 1;
0210: while (i >= 0) {
0211: int dot = name.lastIndexOf('.', i);
0212: String pkgName = "";
0213: String typeName = name;
0214: if (dot != -1) {
0215: pkgName = name.substring(0, dot);
0216: typeName = name.substring(dot + 1);
0217: i = dot - 1;
0218: } else {
0219: i = -1;
0220: }
0221: JClassType result = findType(pkgName, typeName);
0222: if (result != null) {
0223: return result;
0224: }
0225: }
0226: return null;
0227: }
0228:
0229: /**
0230: * Finds a type given its package-relative name. For nested classes, use its
0231: * source name rather than its binary name (that is, use a "." rather than a
0232: * "$").
0233: *
0234: * @return <code>null</code> if the type is not found
0235: */
0236: public JClassType findType(String pkgName, String typeName) {
0237: JPackage pkg = findPackage(pkgName);
0238: if (pkg != null) {
0239: JClassType type = pkg.findType(typeName);
0240: if (type != null) {
0241: return type;
0242: }
0243: }
0244: return null;
0245: }
0246:
0247: /**
0248: * Gets the type object that represents an array of the specified type. The
0249: * returned type always has a stable identity so as to guarantee that all
0250: * calls to this method with the same argument return the same object.
0251: *
0252: * @param componentType the component type of the array, which can itself be
0253: * an array type
0254: * @return a type object representing an array of the component type
0255: */
0256: public JArrayType getArrayType(JType componentType) {
0257: JArrayType arrayType = arrayTypes.get(componentType);
0258: if (arrayType == null) {
0259: arrayType = new JArrayType(componentType, this );
0260: arrayTypes.put(componentType, arrayType);
0261: }
0262: return arrayType;
0263: }
0264:
0265: /**
0266: * Gets a reference to the type object representing
0267: * <code>java.lang.Object</code>.
0268: */
0269: public JClassType getJavaLangObject() {
0270: return javaLangObject;
0271: }
0272:
0273: /**
0274: * Ensure that a package with the specified name exists as well as its parent
0275: * packages.
0276: */
0277: public JPackage getOrCreatePackage(String name) {
0278: int i = name.lastIndexOf('.');
0279: if (i != -1) {
0280: // Ensure the parent package is also created.
0281: //
0282: getOrCreatePackage(name.substring(0, i));
0283: }
0284:
0285: JPackage pkg = packages.get(name);
0286: if (pkg == null) {
0287: pkg = new JPackage(name);
0288: packages.put(name, pkg);
0289: }
0290: return pkg;
0291: }
0292:
0293: /**
0294: * Gets a package by name. All requests for the same package return the same
0295: * package object.
0296: *
0297: * @return the package object associated with the specified name
0298: */
0299: public JPackage getPackage(String pkgName) throws NotFoundException {
0300: JPackage result = findPackage(pkgName);
0301: if (result == null) {
0302: throw new NotFoundException(pkgName);
0303: }
0304: return result;
0305: }
0306:
0307: /**
0308: * Gets an array of all packages known to this type oracle.
0309: *
0310: * @return an array of packages, possibly of zero-length
0311: */
0312: public JPackage[] getPackages() {
0313: return packages.values().toArray(NO_JPACKAGES);
0314: }
0315:
0316: /**
0317: * Gets the parameterized type object that represents the combination of a
0318: * specified raw type and a set of type arguments. The returned type always
0319: * has a stable identity so as to guarantee that all calls to this method with
0320: * the same arguments return the same object.
0321: *
0322: * @param genericType a generic base class
0323: * @param enclosingType
0324: * @param typeArgs the type arguments bound to the specified generic type
0325: * @return a type object representing this particular binding of type
0326: * arguments to the specified generic
0327: * @throws IllegalArgumentException if the parameterization of a non-static
0328: * member type does not specify an enclosing type or if not enough
0329: * arguments were specified to parameterize the generic type
0330: * @throws NullPointerException if genericType is <code>null</code>
0331: */
0332: public JParameterizedType getParameterizedType(
0333: JGenericType genericType, JClassType enclosingType,
0334: JClassType[] typeArgs) {
0335: if (genericType == null) {
0336: throw new NullPointerException("genericType");
0337: }
0338:
0339: if (genericType.isMemberType() && !genericType.isStatic()) {
0340: if (genericType.getEnclosingType().isGenericType() != null
0341: && enclosingType.isParameterized() == null
0342: && enclosingType.isRawType() == null) {
0343: throw new IllegalArgumentException(
0344: "enclosingType needs to be a parameterized type or a raw type");
0345: }
0346: }
0347:
0348: JTypeParameter[] typeParameters = genericType
0349: .getTypeParameters();
0350: if (typeArgs.length < typeParameters.length) {
0351: throw new IllegalArgumentException(
0352: "Not enough type arguments were specified to parameterize '"
0353: + genericType
0354: .getParameterizedQualifiedSourceName()
0355: + "'");
0356: } else {
0357: /*
0358: * TODO: Should WARN if we specify too many type arguments but we have no
0359: * logger.
0360: */
0361: }
0362:
0363: // TODO: validate that the type arguments satisfy the generic type parameter
0364: // bounds if any were specified
0365:
0366: // Uses the generated string signature to intern parameterized types.
0367: //
0368: JParameterizedType parameterized = new JParameterizedType(
0369: genericType, enclosingType, typeArgs);
0370:
0371: // TODO: parameterized qualified source name does not account for the type
0372: // args of the enclosing type
0373: String sig = parameterized
0374: .getParameterizedQualifiedSourceName();
0375: List<JParameterizedType> candidates = parameterizedTypes
0376: .get(sig);
0377: if (candidates == null) {
0378: candidates = new ArrayList<JParameterizedType>();
0379: parameterizedTypes.put(sig, candidates);
0380: } else {
0381: for (JParameterizedType candidate : candidates) {
0382: if (candidate.hasTypeArgs(typeArgs)) {
0383: return candidate;
0384: }
0385: }
0386: }
0387:
0388: candidates.add(parameterized);
0389:
0390: return parameterized;
0391: }
0392:
0393: /**
0394: * Gets the parameterized type object that represents the combination of a
0395: * specified raw type and a set of type arguments. The returned type always
0396: * has a stable identity so as to guarantee that all calls to this method with
0397: * the same arguments return the same object.
0398: *
0399: * @param genericType a generic base class
0400: * @param typeArgs the type arguments bound to the specified generic type
0401: * @return a type object representing this particular binding of type
0402: * arguments to the specified generic
0403: * @throws IllegalArgumentException if the generic type is a non-static member
0404: * type or if not enough arguments were specified to parameterize
0405: * the generic type
0406: * @throws NullPointerException if genericType is <code>null</code>
0407: */
0408: public JParameterizedType getParameterizedType(
0409: JGenericType genericType, JClassType[] typeArgs) {
0410: return getParameterizedType(genericType, null, typeArgs);
0411: }
0412:
0413: public long getReloadCount() {
0414: return reloadCount;
0415: }
0416:
0417: /**
0418: * Finds a type given its fully qualified name. For nested classes, use its
0419: * source name rather than its binary name (that is, use a "." rather than a
0420: * "$").
0421: *
0422: * @return the specified type
0423: */
0424: public JClassType getType(String name) throws NotFoundException {
0425: JClassType type = findType(name);
0426: if (type == null) {
0427: throw new NotFoundException(name);
0428: }
0429: return type;
0430: }
0431:
0432: /**
0433: * Finds a type given its package-relative name. For nested classes, use its
0434: * source name rather than its binary name (that is, use a "." rather than a
0435: * "$").
0436: *
0437: * @return the specified type
0438: */
0439: public JClassType getType(String pkgName,
0440: String topLevelTypeSimpleName) throws NotFoundException {
0441: JClassType type = findType(pkgName, topLevelTypeSimpleName);
0442: if (type == null) {
0443: throw new NotFoundException(pkgName + "."
0444: + topLevelTypeSimpleName);
0445: }
0446: return type;
0447: }
0448:
0449: /**
0450: * Gets all types, both top-level and nested.
0451: *
0452: * @return an array of types, possibly of zero length
0453: */
0454: public JClassType[] getTypes() {
0455: Set<JClassType> allTypes = new HashSet<JClassType>();
0456: JPackage[] pkgs = getPackages();
0457: for (int i = 0; i < pkgs.length; i++) {
0458: JPackage pkg = pkgs[i];
0459: JClassType[] types = pkg.getTypes();
0460: for (int j = 0; j < types.length; j++) {
0461: JClassType type = types[j];
0462: buildAllTypesImpl(allTypes, type);
0463: }
0464: }
0465: return allTypes.toArray(NO_JCLASSES);
0466: }
0467:
0468: public JClassType[] getTypesInCompilationUnit(
0469: CompilationUnitProvider cup) {
0470: JClassType[] types = typesByCup.get(cup);
0471: if (types != null) {
0472: return types;
0473: } else {
0474: return NO_JCLASSES;
0475: }
0476: }
0477:
0478: public JWildcardType getWildcardType(JBound bounds) {
0479: JWildcardType wildcardType = new JWildcardType(bounds);
0480: String sig = wildcardType.getQualifiedSourceName();
0481: List<JWildcardType> candidates = wildcardTypes.get(sig);
0482: if (candidates == null) {
0483: candidates = new ArrayList<JWildcardType>();
0484: wildcardTypes.put(sig, candidates);
0485: } else {
0486: for (JWildcardType candidate : candidates) {
0487: if (candidate.hasBounds(bounds)) {
0488: return candidate;
0489: }
0490: }
0491: }
0492:
0493: candidates.add(wildcardType);
0494:
0495: return wildcardType;
0496: }
0497:
0498: /**
0499: * Parses the string form of a type to produce the corresponding type object.
0500: * The types that can be parsed include primitives, class and interface names,
0501: * simple parameterized types (those without wildcards or bounds), and arrays
0502: * of the preceding.
0503: * <p>
0504: * Examples of types that can be parsed by this method.
0505: * <ul>
0506: * <li><code>int</code></li>
0507: * <li><code>java.lang.Object</code></li>
0508: * <li><code>java.lang.String[]</code></li>
0509: * <li><code>char[][]</code></li>
0510: * <li><code>void</code></li>
0511: * <li><code>List<Shape></code></li>
0512: * <li><code>List<List<Shape>></code></li>
0513: * </ul>
0514: * </p>
0515: *
0516: * @param type a type signature to be parsed
0517: * @return the type object corresponding to the parse type
0518: */
0519: public JType parse(String type) throws TypeOracleException {
0520: // Remove all internal and external whitespace.
0521: //
0522: type = type.replaceAll("\\\\s", "");
0523:
0524: // Recursively parse.
0525: //
0526: return parseImpl(type);
0527: }
0528:
0529: /**
0530: * Convenience method to sort class types in a consistent way. Note that the
0531: * order is subject to change and is intended to generate an "aesthetically
0532: * pleasing" order rather than a computationally reliable order.
0533: */
0534: public void sort(JClassType[] types) {
0535: Arrays.sort(types, new Comparator<JClassType>() {
0536: public int compare(JClassType type1, JClassType type2) {
0537: String name1 = type1.getQualifiedSourceName();
0538: String name2 = type2.getQualifiedSourceName();
0539: return name1.compareTo(name2);
0540: }
0541: });
0542: }
0543:
0544: /**
0545: * Convenience method to sort constructors in a consistent way. Note that the
0546: * order is subject to change and is intended to generate an "aesthetically
0547: * pleasing" order rather than a computationally reliable order.
0548: */
0549: public void sort(JConstructor[] ctors) {
0550: Arrays.sort(ctors, new Comparator<JConstructor>() {
0551: public int compare(JConstructor o1, JConstructor o2) {
0552: // Nothing for now; could enhance to sort based on parameter list
0553: return 0;
0554: }
0555: });
0556: }
0557:
0558: /**
0559: * Convenience method to sort fields in a consistent way. Note that the order
0560: * is subject to change and is intended to generate an "aesthetically
0561: * pleasing" order rather than a computationally reliable order.
0562: */
0563: public void sort(JField[] fields) {
0564: Arrays.sort(fields, new Comparator<JField>() {
0565: public int compare(JField f1, JField f2) {
0566: String name1 = f1.getName();
0567: String name2 = f2.getName();
0568: return name1.compareTo(name2);
0569: }
0570: });
0571: }
0572:
0573: /**
0574: * Convenience method to sort methods in a consistent way. Note that the order
0575: * is subject to change and is intended to generate an "aesthetically
0576: * pleasing" order rather than a computationally reliable order.
0577: */
0578: public void sort(JMethod[] methods) {
0579: Arrays.sort(methods, new Comparator<JMethod>() {
0580: public int compare(JMethod m1, JMethod m2) {
0581: String name1 = m1.getName();
0582: String name2 = m2.getName();
0583: return name1.compareTo(name2);
0584: }
0585: });
0586: }
0587:
0588: void incrementReloadCount() {
0589: reloadCount++;
0590: }
0591:
0592: /**
0593: * Note, this method is called reflectively from the
0594: * {@link CacheManager#invalidateOnRefresh(TypeOracle)}.
0595: *
0596: * @param cup compilation unit whose types will be invalidated
0597: */
0598: void invalidateTypesInCompilationUnit(CompilationUnitProvider cup) {
0599: Set<JClassType> invalidTypes = new HashSet<JClassType>();
0600: JClassType[] types = typesByCup.get(cup);
0601: if (types == null) {
0602: return;
0603: }
0604:
0605: for (int i = 0; i < types.length; i++) {
0606: JClassType classTypeToInvalidate = types[i];
0607: invalidTypes.add(classTypeToInvalidate);
0608: }
0609:
0610: typesByCup.remove(cup);
0611:
0612: removeInvalidatedArrayTypes(invalidTypes);
0613:
0614: removeInvalidatedParameterizedTypes(invalidTypes);
0615:
0616: removeTypes(invalidTypes);
0617: }
0618:
0619: void recordTypeInCompilationUnit(CompilationUnitProvider cup,
0620: JClassType type) {
0621: JClassType[] types = typesByCup.get(cup);
0622: if (types == null) {
0623: types = new JClassType[] { type };
0624: } else {
0625: JClassType[] temp = new JClassType[types.length + 1];
0626: System.arraycopy(types, 0, temp, 0, types.length);
0627: temp[types.length] = type;
0628: types = temp;
0629: }
0630: typesByCup.put(cup, types);
0631: }
0632:
0633: /**
0634: * Updates relationships within this type oracle. Should be called after any
0635: * changes are made.
0636: *
0637: * <p>
0638: * Throws <code>TypeOracleException</code> thrown if fundamental baseline
0639: * correctness criteria are violated, most notably the absence of
0640: * "java.lang.Object"
0641: * </p>
0642: */
0643: void refresh(TreeLogger logger) throws NotFoundException {
0644: if (javaLangObject == null) {
0645: javaLangObject = findType("java.lang.Object");
0646: if (javaLangObject == null) {
0647: throw new NotFoundException("java.lang.Object");
0648: }
0649: }
0650: computeHierarchyRelationships();
0651: consumeTypeArgMetaData(logger);
0652: }
0653:
0654: private void buildAllTypesImpl(Set<JClassType> allTypes,
0655: JClassType type) {
0656: boolean didAdd = allTypes.add(type);
0657: assert (didAdd);
0658: JClassType[] nestedTypes = type.getNestedTypes();
0659: for (int i = 0; i < nestedTypes.length; i++) {
0660: JClassType nestedType = nestedTypes[i];
0661: buildAllTypesImpl(allTypes, nestedType);
0662: }
0663: }
0664:
0665: private void computeHierarchyRelationships() {
0666: // For each type, walk up its hierarchy chain and tell each supertype
0667: // about its subtype.
0668: //
0669: JClassType[] allTypes = getTypes();
0670: for (int i = 0; i < allTypes.length; i++) {
0671: JClassType type = allTypes[i];
0672: type.notifySuperTypes();
0673: }
0674: }
0675:
0676: private void consumeTypeArgMetaData(TreeLogger logger) {
0677: logger = logger.branch(TreeLogger.DEBUG, "Examining "
0678: + TAG_TYPEARGS + " tags", null);
0679: consumeTypeArgMetaData(logger, getTypes());
0680: }
0681:
0682: private void consumeTypeArgMetaData(TreeLogger logger,
0683: JClassType[] types) {
0684: for (int i = 0; i < types.length; i++) {
0685: JClassType type = types[i];
0686: // CTORS not supported yet
0687:
0688: TreeLogger branch = logger.branch(TreeLogger.DEBUG, "Type "
0689: + type.getQualifiedSourceName(), null);
0690:
0691: consumeTypeArgMetaData(branch, type.getMethods());
0692: consumeTypeArgMetaData(branch, type.getFields());
0693: }
0694: }
0695:
0696: private void consumeTypeArgMetaData(TreeLogger logger,
0697: JField[] fields) {
0698: TreeLogger branch;
0699: for (int i = 0; i < fields.length; i++) {
0700: JField field = fields[i];
0701:
0702: String[][] tokensArray = field.getMetaData(TAG_TYPEARGS);
0703: if (tokensArray.length == 0) {
0704: // No tag.
0705: continue;
0706: }
0707:
0708: try {
0709: String msg = "Field " + field.getName();
0710: branch = logger.branch(TreeLogger.TRACE, msg, null);
0711:
0712: if (tokensArray.length > 1) {
0713: // Too many.
0714: branch.log(TreeLogger.WARN,
0715: "Metadata error on field '"
0716: + field.getName() + "' in type '"
0717: + field.getEnclosingType()
0718: + "': expecting at most one "
0719: + TAG_TYPEARGS
0720: + " (the last one will be used)",
0721: null);
0722: }
0723:
0724: // (1) Parse it.
0725: // (2) Update the field's type.
0726: // If it wasn't a valid parameterized type, parse() would've thrown.
0727: //
0728: JType fieldType = field.getType();
0729: String[] token = tokensArray[tokensArray.length - 1];
0730: JType resultingType = determineActualType(branch,
0731: fieldType, token, 0);
0732: field.setType(resultingType);
0733: } catch (UnableToCompleteException e) {
0734: // Continue; the problem will have been logged.
0735: //
0736: }
0737: }
0738: }
0739:
0740: private void consumeTypeArgMetaData(TreeLogger logger,
0741: JMethod[] methods) {
0742: TreeLogger branch;
0743: for (int i = 0; i < methods.length; i++) {
0744: JMethod method = methods[i];
0745:
0746: String[][] tokensArray = method.getMetaData(TAG_TYPEARGS);
0747: if (tokensArray.length == 0) {
0748: // No tag.
0749: continue;
0750: }
0751: try {
0752: String msg = "Method "
0753: + method.getReadableDeclaration();
0754: branch = logger.branch(TreeLogger.TRACE, msg, null);
0755:
0756: // Okay, parse each one and correlate it to a part of the decl.
0757: //
0758: boolean returnTypeHandled = false;
0759: Set<JParameter> paramsAlreadySet = new HashSet<JParameter>();
0760: for (int j = 0; j < tokensArray.length; j++) {
0761: String[] tokens = tokensArray[j];
0762: // It is either referring to the return type or a parameter type.
0763: //
0764: if (tokens.length == 0) {
0765: // Expecting at least something.
0766: //
0767: branch.log(TreeLogger.WARN,
0768: "Metadata error: expecting tokens after "
0769: + TAG_TYPEARGS, null);
0770: throw new UnableToCompleteException();
0771: }
0772:
0773: // See if the first token is a parameter name.
0774: //
0775: JParameter param = method.findParameter(tokens[0]);
0776: if (param != null) {
0777: if (!paramsAlreadySet.contains(param)) {
0778: // These are type args for a param.
0779: //
0780: JType resultingType = determineActualType(
0781: branch, param.getType(), tokens, 1);
0782: param.setType(resultingType);
0783: paramsAlreadySet.add(param);
0784: } else {
0785: // This parameter type has already been set.
0786: //
0787: msg = "Metadata error: duplicate attempt to specify type args for parameter '"
0788: + param.getName() + "'";
0789: branch.log(TreeLogger.WARN, msg, null);
0790: throw new UnableToCompleteException();
0791: }
0792: } else {
0793: // It's either referring to the return type or a bad param name.
0794: //
0795: if (!returnTypeHandled) {
0796: JType resultingType = determineActualType(
0797: branch, method.getReturnType(),
0798: tokens, 0);
0799: method.setReturnType(resultingType);
0800: returnTypeHandled = true;
0801: } else {
0802: // The return type has already been set.
0803: //
0804: msg = "Metadata error: duplicate attempt to specify type args for the return type";
0805: branch.log(TreeLogger.WARN, msg, null);
0806: }
0807: }
0808: }
0809: } catch (UnableToCompleteException e) {
0810: // Continue; will already have been logged.
0811: //
0812: }
0813: }
0814: }
0815:
0816: /*
0817: * Given a declared type and some number of type arguments determine what the
0818: * actual type should be.
0819: */
0820: private JType determineActualType(TreeLogger logger,
0821: JType declType, String[] tokens, int startIndex)
0822: throws UnableToCompleteException {
0823: // These are type args for a param.
0824: //
0825: JType leafType = declType.getLeafType();
0826: String typeName = leafType.getQualifiedSourceName();
0827: JType resultingType = parseTypeArgTokens(logger, typeName,
0828: tokens, startIndex);
0829: JArrayType arrayType = declType.isArray();
0830: if (arrayType != null) {
0831: arrayType.setLeafType(resultingType);
0832:
0833: return declType;
0834: }
0835:
0836: return resultingType;
0837: }
0838:
0839: private JType parseImpl(String type) throws NotFoundException,
0840: ParseException, BadTypeArgsException {
0841: if (type.endsWith("[]")) {
0842: String remainder = type.substring(0, type.length() - 2);
0843: JType componentType = parseImpl(remainder);
0844: return getArrayType(componentType);
0845: }
0846:
0847: if (type.endsWith(">")) {
0848: int bracket = type.indexOf('<');
0849: if (bracket == -1) {
0850: throw new ParseException(
0851: "Mismatched brackets; expected '<' to match subsequent '>'");
0852: }
0853:
0854: // Resolve the raw type.
0855: //
0856: String rawTypeName = type.substring(0, bracket);
0857: JType rawType = parseImpl(rawTypeName);
0858: if (rawType.isParameterized() != null) {
0859: // The raw type cannot itself be parameterized.
0860: //
0861: throw new BadTypeArgsException(
0862: "Only non-parameterized classes and interface can be parameterized");
0863: } else if (rawType.isClassOrInterface() == null) {
0864: // The raw type must be a class or interface
0865: // (not an array or primitive).
0866: //
0867: throw new BadTypeArgsException(
0868: "Only classes and interface can be parameterized, so "
0869: + rawType.getQualifiedSourceName()
0870: + " cannot be used in this context");
0871: } else if (rawType.isGenericType() == null) {
0872: throw new BadTypeArgsException(
0873: "'"
0874: + rawType.getQualifiedSourceName()
0875: + "' is not a generic type; only generic types can be parameterized");
0876: }
0877:
0878: // Resolve each type argument.
0879: //
0880: String typeArgContents = type.substring(bracket + 1, type
0881: .length() - 1);
0882: JClassType[] typeArgs = parseTypeArgContents(typeArgContents);
0883:
0884: // Intern this type.
0885: //
0886: return getParameterizedType(rawType.isGenericType(),
0887: typeArgs);
0888: }
0889:
0890: JType result = JPrimitiveType.valueOf(type);
0891: if (result != null) {
0892: return result;
0893: }
0894:
0895: result = findType(type);
0896: if (result != null) {
0897: return result;
0898: }
0899:
0900: throw new NotFoundException("Unable to recognize '" + type
0901: + "' as a type name (is it fully qualified?)");
0902: }
0903:
0904: private void parseTypeArgComponent(List<JClassType> typeArgList,
0905: String typeArgComponent) throws NotFoundException,
0906: ParseException, BadTypeArgsException {
0907: JType typeArg = parseImpl(typeArgComponent);
0908: if (typeArg.isPrimitive() != null) {
0909: // Cannot be primitive.
0910: //
0911: throw new BadTypeArgsException(
0912: "Type arguments cannot be primitives, so '"
0913: + typeArg.getQualifiedSourceName()
0914: + "' cannot be used in this context");
0915: }
0916:
0917: typeArgList.add((JClassType) typeArg);
0918: }
0919:
0920: /**
0921: * Returns an array of types specified inside of a gwt.typeArgs javadoc
0922: * annotation.
0923: */
0924: private JClassType[] parseTypeArgContents(String typeArgContents)
0925: throws ParseException, NotFoundException,
0926: BadTypeArgsException {
0927: List<JClassType> typeArgList = new ArrayList<JClassType>();
0928:
0929: int start = 0;
0930: for (int offset = 0, length = typeArgContents.length(); offset < length; ++offset) {
0931: char ch = typeArgContents.charAt(offset);
0932: switch (ch) {
0933: case '<':
0934: // scan for closing '>' while ignoring commas
0935: for (int depth = 1; depth > 0;) {
0936: if (++offset == length) {
0937: throw new ParseException(
0938: "Mismatched brackets; expected '<' to match subsequent '>'");
0939: }
0940:
0941: char ich = typeArgContents.charAt(offset);
0942: if (ich == '<') {
0943: ++depth;
0944: } else if (ich == '>') {
0945: --depth;
0946: }
0947: }
0948: break;
0949: case '>':
0950: throw new ParseException("No matching '<' for '>'");
0951: case ',':
0952: String typeArgComponent = typeArgContents.substring(
0953: start, offset);
0954: parseTypeArgComponent(typeArgList, typeArgComponent);
0955: start = offset + 1;
0956: break;
0957: default:
0958: break;
0959: }
0960: }
0961:
0962: String typeArgComponent = typeArgContents.substring(start);
0963: parseTypeArgComponent(typeArgList, typeArgComponent);
0964:
0965: JClassType[] typeArgs = typeArgList
0966: .toArray(new JClassType[typeArgList.size()]);
0967: return typeArgs;
0968: }
0969:
0970: private JType parseTypeArgTokens(TreeLogger logger,
0971: String maybeRawType, String[] tokens, int startIndex)
0972: throws UnableToCompleteException {
0973: String munged = combine(tokens, startIndex).trim();
0974: String toParse = maybeRawType + munged;
0975: JType parameterizedType;
0976: try {
0977: parameterizedType = parse(toParse);
0978: } catch (IllegalArgumentException e) {
0979: logger.log(TreeLogger.WARN, e.getMessage(), e);
0980: throw new UnableToCompleteException();
0981: } catch (TypeOracleException e) {
0982: logger.log(TreeLogger.WARN, e.getMessage(), e);
0983: throw new UnableToCompleteException();
0984: }
0985: return parameterizedType;
0986: }
0987:
0988: /**
0989: * Remove any array type whose leaf type has been invalidated.
0990: *
0991: * @param invalidTypes set of types that have been invalidated.
0992: */
0993: private void removeInvalidatedArrayTypes(
0994: Set<JClassType> invalidTypes) {
0995: arrayTypes.keySet().removeAll(invalidTypes);
0996: }
0997:
0998: /**
0999: * Remove any parameterized type that was invalidated because either its raw
1000: * type or any one of its type arguments was invalidated.
1001: *
1002: * @param invalidTypes set of types known to have been invalidated
1003: */
1004: private void removeInvalidatedParameterizedTypes(
1005: Set<JClassType> invalidTypes) {
1006: Iterator<List<JParameterizedType>> listIterator = parameterizedTypes
1007: .values().iterator();
1008:
1009: while (listIterator.hasNext()) {
1010: List<JParameterizedType> list = listIterator.next();
1011: Iterator<JParameterizedType> typeIterator = list.iterator();
1012: while (typeIterator.hasNext()) {
1013: JType type = typeIterator.next();
1014: if (isInvalidatedTypeRecursive(type, invalidTypes)) {
1015: typeIterator.remove();
1016: }
1017: }
1018: }
1019: }
1020:
1021: /**
1022: * Removes the specified types from the type oracle.
1023: *
1024: * @param invalidTypes set of types to remove
1025: */
1026: private void removeTypes(Set<JClassType> invalidTypes) {
1027: Iterator<JClassType> iter = invalidTypes.iterator();
1028:
1029: while (iter.hasNext()) {
1030: JClassType classType = iter.next();
1031: JPackage pkg = classType.getPackage();
1032: if (pkg != null) {
1033: pkg.remove(classType);
1034: }
1035:
1036: classType.removeFromSupertypes();
1037: }
1038: }
1039: }
|