001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.core.ext.typeinfo;
017:
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.IdentityHashMap;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Set;
024:
025: /**
026: * Represents a parameterized type in a declaration.
027: */
028: public class JParameterizedType extends JDelegatingClassType {
029: private static boolean areTypeArgsAssignableFrom(
030: JClassType[] myTypeArgs, JClassType[] otherTypeArgs) {
031: assert (myTypeArgs.length <= otherTypeArgs.length);
032:
033: for (int i = 0; i < myTypeArgs.length; ++i) {
034: JClassType myTypeArg = myTypeArgs[i];
035: JClassType otherTypeArg = otherTypeArgs[i];
036:
037: if (myTypeArg.isTypeParameter() != null) {
038: /*
039: * If my type argument is a type parameter consider it's erased form. This
040: * avoids recursion sickness in the case where my type argument is of the
041: * form:
042: *
043: * T extends Serializable & Comparable<T>
044: *
045: * and otherTypeArg is something like Long, Short, etc.
046: */
047: return myTypeArg.getErasedType().isAssignableFrom(
048: otherTypeArg);
049: }
050:
051: if (myTypeArg.isWildcard() == null) {
052: // myTypeArg needs to be a wildcard or we cannot be assignable.
053: return false;
054: }
055:
056: if (!myTypeArg.isAssignableFrom(otherTypeArg)) {
057: return false;
058: }
059: }
060:
061: return true;
062: }
063:
064: /**
065: * Create a parameterized type along with any necessary enclosing
066: * parameterized types. Enclosing parameterized types are necessary when the
067: * base type is a non-static member and the enclosing type is also generic.
068: */
069: private static JParameterizedType createParameterizedTypeRecursive(
070: JGenericType baseType,
071: Map<JTypeParameter, JClassType> substitutionMap) {
072: JClassType enclosingType = baseType.getEnclosingType();
073: if (baseType.isMemberType() && !baseType.isStatic()) {
074: // This base type is a non-static generic type so we build the necessary
075: // enclosing parameterized type and update the enclosing type to be
076: // a parameterized type.
077: JGenericType isGenericEnclosingType = enclosingType
078: .isGenericType();
079: if (isGenericEnclosingType != null) {
080: enclosingType = createParameterizedTypeRecursive(
081: isGenericEnclosingType, substitutionMap);
082: }
083: }
084:
085: JTypeParameter[] typeParameters = baseType.getTypeParameters();
086: JClassType[] newTypeArgs = new JClassType[typeParameters.length];
087: TypeOracle oracle = baseType.getOracle();
088: for (int i = 0; i < newTypeArgs.length; ++i) {
089: JClassType newTypeArg = substitutionMap
090: .get(typeParameters[i]);
091: if (newTypeArg == null) {
092: JBound typeParamBounds = typeParameters[i].getBounds();
093: JUpperBound newTypeArgBounds = new JUpperBound(
094: typeParamBounds.getFirstBound());
095: newTypeArg = oracle.getWildcardType(newTypeArgBounds);
096: }
097:
098: newTypeArgs[i] = newTypeArg;
099: }
100:
101: JParameterizedType parameterizedType = oracle
102: .getParameterizedType(baseType, enclosingType,
103: newTypeArgs);
104: return parameterizedType;
105: }
106:
107: /**
108: * Returns <code>true</code> if the rhsType can be assigned to the lhsType.
109: */
110: private static boolean isAssignable(JClassType lhsType,
111: JClassType rhsType) {
112: if (lhsType == rhsType) {
113: return true;
114: }
115:
116: Set<JClassType> rhsSupertypes = getFlattenedSuperTypeHierarchy(rhsType);
117: if (rhsSupertypes.contains(lhsType)) {
118: // Done, appears explicitly in the supertype hierarchy.
119: return true;
120: }
121:
122: /*
123: * Get the generic base type for the lhsType if there is one.
124: */
125: JGenericType lhsBaseType = null;
126: if (lhsType.isParameterized() != null) {
127: lhsBaseType = lhsType.isParameterized().getBaseType();
128: } else if (lhsType.isRawType() != null) {
129: lhsBaseType = lhsType.isRawType().getBaseType();
130: }
131:
132: /*
133: * Check the supertype hierarchy to see if we can find a parameterization
134: * or a raw type that would satisfy the assignment.
135: */
136: for (JClassType rhsSupertype : rhsSupertypes) {
137: if (rhsSupertype.isGenericType() != null) {
138: /*
139: * A generic type will be treated as its raw type for assignment
140: * purposes.
141: */
142: rhsSupertype = rhsSupertype.isGenericType()
143: .getRawType();
144: }
145:
146: JParameterizedType rhsParameterized = rhsSupertype
147: .isParameterized();
148: if (rhsParameterized != null) {
149: if (rhsParameterized.getBaseType() == lhsBaseType) {
150: /*
151: * This supertype and the lhsType have the same base type, but they
152: * have different parameterizations so we test them.
153: */
154: assert (rhsParameterized != lhsType);
155:
156: if (lhsType.isRawType() != null) {
157: // The lhsType is raw so we do not need to check the parameterization.
158: return true;
159: } else {
160: assert (lhsType.isParameterized() != null);
161:
162: return areTypeArgsAssignableFrom(lhsType
163: .isParameterized().getTypeArgs(),
164: rhsParameterized.getTypeArgs());
165: }
166: }
167: } else if (rhsSupertype.isRawType() != null) {
168: if (rhsSupertype.isRawType().getBaseType() == lhsBaseType) {
169: /*
170: * The raw supertype has the same base type as the lhsType so the
171: * assignment is okay.
172: */
173: return true;
174: }
175: }
176: }
177:
178: return false;
179: }
180:
181: private final JClassType enclosingType;
182:
183: private List<JClassType> interfaces;
184:
185: private JClassType lazySuperclass;
186:
187: private final AbstractMembers members;
188:
189: private final List<JClassType> typeArgs = new ArrayList<JClassType>();
190:
191: /**
192: * This map records the JClassType that should be used in place of a given
193: * {@link JTypeParameter}.
194: */
195: private Map<JTypeParameter, JClassType> lazySubstitutionMap;
196:
197: public JParameterizedType(JGenericType baseType,
198: JClassType enclosingType, JClassType[] typeArgs) {
199: super .setBaseType(baseType);
200:
201: this .enclosingType = enclosingType;
202:
203: // NOTE: this instance is not considered a nested type of the enclosing type
204:
205: final JParameterizedType parameterizedType = this ;
206: members = new DelegateMembers(this , baseType,
207: new Substitution() {
208: public JType getSubstitution(JType type) {
209: return type
210: .getSubstitutedType(parameterizedType);
211: }
212: });
213:
214: List<JClassType> typeArgsList = Arrays.asList(typeArgs);
215: this .typeArgs.addAll(typeArgsList);
216: assert (typeArgsList.indexOf(null) == -1);
217:
218: // NOTE: Can't perform substitutions until we are done building
219: }
220:
221: @Override
222: public JConstructor findConstructor(JType[] paramTypes) {
223: return members.findConstructor(paramTypes);
224: }
225:
226: @Override
227: public JField findField(String name) {
228: return members.findField(name);
229: }
230:
231: @Override
232: public JMethod findMethod(String name, JType[] paramTypes) {
233: return members.findMethod(name, paramTypes);
234: }
235:
236: @Override
237: public JClassType findNestedType(String typeName) {
238: return members.findNestedType(typeName);
239: }
240:
241: @Override
242: public JGenericType getBaseType() {
243: return (JGenericType) super .getBaseType();
244: }
245:
246: @Override
247: public JConstructor getConstructor(JType[] paramTypes)
248: throws NotFoundException {
249: return members.getConstructor(paramTypes);
250: }
251:
252: @Override
253: public JConstructor[] getConstructors() {
254: return members.getConstructors();
255: }
256:
257: @Override
258: public JClassType getEnclosingType() {
259: return enclosingType;
260: }
261:
262: @Override
263: public JField getField(String name) {
264: return members.getField(name);
265: }
266:
267: @Override
268: public JField[] getFields() {
269: return members.getFields();
270: }
271:
272: @Override
273: public JClassType[] getImplementedInterfaces() {
274: if (interfaces == null) {
275: interfaces = new ArrayList<JClassType>();
276: JClassType[] intfs = getBaseType()
277: .getImplementedInterfaces();
278: for (JClassType intf : intfs) {
279: JClassType newIntf = intf.getSubstitutedType(this );
280: interfaces.add(newIntf);
281: }
282: }
283: return interfaces.toArray(TypeOracle.NO_JCLASSES);
284: }
285:
286: @Override
287: public JMethod getMethod(String name, JType[] paramTypes)
288: throws NotFoundException {
289: return members.getMethod(name, paramTypes);
290: }
291:
292: @Override
293: public JMethod[] getMethods() {
294: return members.getMethods();
295: }
296:
297: @Override
298: public JClassType getNestedType(String typeName)
299: throws NotFoundException {
300: return members.getNestedType(typeName);
301: }
302:
303: @Override
304: public JClassType[] getNestedTypes() {
305: return members.getNestedTypes();
306: }
307:
308: /**
309: * @deprecated see {@link #getQualifiedSourceName()}
310: */
311: @Deprecated
312: public String getNonParameterizedQualifiedSourceName() {
313: return getQualifiedSourceName();
314: }
315:
316: @Override
317: public JMethod[] getOverloads(String name) {
318: return members.getOverloads(name);
319: }
320:
321: @Override
322: public JMethod[] getOverridableMethods() {
323: return members.getOverridableMethods();
324: }
325:
326: @Override
327: public String getParameterizedQualifiedSourceName() {
328: StringBuffer sb = new StringBuffer();
329:
330: if (getEnclosingType() != null) {
331: sb.append(getEnclosingType()
332: .getParameterizedQualifiedSourceName());
333: sb.append(".");
334: sb.append(getSimpleSourceName());
335: } else {
336: sb.append(getQualifiedSourceName());
337: }
338:
339: if (typeArgs.size() > 0) {
340: sb.append('<');
341: boolean needComma = false;
342: for (JType typeArg : typeArgs) {
343: if (needComma) {
344: sb.append(", ");
345: } else {
346: needComma = true;
347: }
348: sb
349: .append(typeArg
350: .getParameterizedQualifiedSourceName());
351: }
352: sb.append('>');
353: } else {
354: /*
355: * Non-static, inner classes of generic types are modeled as generic, even
356: * if they do not declare type parameters or reference the type parameters
357: * of their enclosing generic type.
358: */
359: }
360:
361: return sb.toString();
362: }
363:
364: /**
365: * Everything is fully qualified and includes the < and > in the
366: * signature.
367: */
368: @Override
369: public String getQualifiedSourceName() {
370: return getBaseType().getQualifiedSourceName();
371: }
372:
373: public JClassType getRawType() {
374: return getBaseType().getRawType();
375: }
376:
377: /**
378: * In this case, the raw type name.
379: */
380: @Override
381: public String getSimpleSourceName() {
382: return getBaseType().getSimpleSourceName();
383: }
384:
385: /*
386: * Goal: Return a list of possible subtypes of this parameterized type. In the
387: * event that we have generic subtypes and we cannot resolve the all of the
388: * type arguments, we need to wildcard types in place of the arguments that we
389: * cannot resolve.
390: *
391: * Algorithm: - Ask generic type for its subtypes - Filter subtypes of the
392: * generic which cannot be our subtype.
393: */
394: @Override
395: public JClassType[] getSubtypes() {
396: List<JClassType> subtypeList = new ArrayList<JClassType>();
397:
398: // Parameterized types are not tracked in the subtype hierarchy; ask base
399: // type
400: JClassType[] genericSubtypes = getBaseType().getSubtypes();
401: for (JClassType subtype : genericSubtypes) {
402: // Could be a subtype depending on how it is substituted
403: Map<JTypeParameter, JClassType> substitutions = findSubtypeSubstitution(subtype);
404: if (substitutions != null) {
405: JGenericType genericType = subtype.isGenericType();
406: if (genericType != null) {
407: subtype = createParameterizedTypeRecursive(
408: genericType, substitutions);
409: } else {
410: // If this is not a generic type then there should not be any
411: // substitution.
412: assert (substitutions.isEmpty());
413: }
414:
415: subtypeList.add(subtype);
416: }
417: }
418:
419: return subtypeList.toArray(TypeOracle.NO_JCLASSES);
420: }
421:
422: @Override
423: public JClassType getSuperclass() {
424: if (isInterface() != null) {
425: return null;
426: }
427:
428: if (lazySuperclass == null) {
429: JGenericType baseType = getBaseType();
430: JClassType super class = baseType.getSuperclass();
431: assert (super class != null);
432: lazySuperclass = super class.getSubstitutedType(this );
433: }
434:
435: return lazySuperclass;
436: }
437:
438: public JClassType[] getTypeArgs() {
439: return typeArgs.toArray(TypeOracle.NO_JCLASSES);
440: }
441:
442: @Override
443: public boolean isAssignableFrom(JClassType otherType) {
444: return isAssignable(this , otherType);
445: }
446:
447: @Override
448: public boolean isAssignableTo(JClassType otherType) {
449: return isAssignable(otherType, this );
450: }
451:
452: @Override
453: public JGenericType isGenericType() {
454: return null;
455: }
456:
457: @Override
458: public JParameterizedType isParameterized() {
459: return this ;
460: }
461:
462: @Override
463: public JRawType isRawType() {
464: return null;
465: }
466:
467: @Override
468: public JWildcardType isWildcard() {
469: return null;
470: }
471:
472: /**
473: */
474: public void setTypeArguments(JClassType[] typeArgs) {
475: this .typeArgs.addAll(Arrays.asList(typeArgs));
476: }
477:
478: @Override
479: public String toString() {
480: if (isInterface() != null) {
481: return "interface " + getParameterizedQualifiedSourceName();
482: }
483:
484: return "class " + getParameterizedQualifiedSourceName();
485: }
486:
487: @Override
488: protected JClassType findNestedTypeImpl(String[] typeName, int index) {
489: return members.findNestedTypeImpl(typeName, index);
490: }
491:
492: @Override
493: protected void getOverridableMethodsOnSuperclassesAndThisClass(
494: Map<String, JMethod> methodsBySignature) {
495: members
496: .getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
497: }
498:
499: /**
500: * Gets the methods declared in interfaces that this type extends. If this
501: * type is a class, its own methods are not added. If this type is an
502: * interface, its own methods are added. Used internally by
503: * {@link #getOverridableMethods()}.
504: *
505: * @param methodsBySignature
506: */
507: @Override
508: protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
509: Map<String, JMethod> methodsBySignature) {
510: members
511: .getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
512: }
513:
514: @Override
515: JClassType getSubstitutedType(JParameterizedType parameterizedType) {
516: maybeInitializeTypeParameterSubstitutionMap();
517:
518: if (this == parameterizedType) {
519: return this ;
520: }
521:
522: JClassType[] newTypeArgs = new JClassType[typeArgs.size()];
523: for (int i = 0; i < newTypeArgs.length; ++i) {
524: newTypeArgs[i] = typeArgs.get(i).getSubstitutedType(
525: parameterizedType);
526: }
527:
528: return getOracle().getParameterizedType(getBaseType(),
529: getEnclosingType(), newTypeArgs);
530: }
531:
532: /**
533: * Returns the {@link JClassType} that is a substitute for the given
534: * {@link JTypeParameter}. If there is no substitution, the original
535: * {@link JTypeParameter} is returned.
536: */
537: JClassType getTypeParameterSubstitution(JTypeParameter typeParameter) {
538: maybeInitializeTypeParameterSubstitutionMap();
539:
540: JClassType substitute = lazySubstitutionMap.get(typeParameter);
541: if (substitute != null) {
542: return substitute;
543: }
544:
545: return typeParameter;
546: }
547:
548: boolean hasTypeArgs(JClassType[] otherArgTypes) {
549: if (otherArgTypes.length != typeArgs.size()) {
550: return false;
551: }
552:
553: for (int i = 0; i < otherArgTypes.length; ++i) {
554: // Identity tests are ok since identity is durable within an oracle.
555: //
556: if (otherArgTypes[i] != typeArgs.get(i)) {
557: return false;
558: }
559: }
560:
561: return true;
562: }
563:
564: /**
565: * Initialize a map of substitutions for {@link JTypeParameter}s to
566: * corresponding {@link JClassType}s. This can only be initialized after the
567: * {@link com.google.gwt.dev.jdt.TypeOracleBuilder TypeOracleBuilder} has
568: * fully resolved all of the {@link JClassType}s.
569: */
570: void maybeInitializeTypeParameterSubstitutionMap() {
571: if (lazySubstitutionMap != null) {
572: return;
573: }
574: lazySubstitutionMap = new IdentityHashMap<JTypeParameter, JClassType>();
575:
576: JParameterizedType currentParameterizedType = this ;
577:
578: while (currentParameterizedType != null) {
579: JGenericType genericType = currentParameterizedType
580: .getBaseType();
581: JTypeParameter[] typeParameters = genericType
582: .getTypeParameters();
583: JClassType[] typeArguments = currentParameterizedType
584: .getTypeArgs();
585:
586: for (JTypeParameter typeParameter : typeParameters) {
587: lazySubstitutionMap.put(typeParameter,
588: typeArguments[typeParameter.getOrdinal()]);
589: }
590:
591: if (currentParameterizedType.isStatic()) {
592: break;
593: }
594:
595: JClassType maybeParameterizedType = currentParameterizedType
596: .getEnclosingType();
597: if (maybeParameterizedType == null
598: || maybeParameterizedType.isParameterized() == null) {
599: break;
600: }
601: currentParameterizedType = maybeParameterizedType
602: .isParameterized();
603: }
604: }
605:
606: /**
607: * Returns a map of substitutions that will make the subtype a proper subtype
608: * of this parameterized type. The map maybe empty in the case that it is
609: * already an exact subtype.
610: */
611: private Map<JTypeParameter, JClassType> findSubtypeSubstitution(
612: JClassType subtype) {
613: Map<JTypeParameter, JClassType> substitutions = new IdentityHashMap<JTypeParameter, JClassType>();
614:
615: // Get the supertype hierarchy. If this JParameterizedType exists
616: // exactly in this set we are done.
617: Set<JClassType> super typeHierarchy = getFlattenedSuperTypeHierarchy(subtype);
618: if (super typeHierarchy.contains(this )) {
619: return substitutions;
620: }
621:
622: /*
623: * Try to find a parameterized supertype whose base type is the same as our
624: * own. Because that parameterized supertype might be made into ourself via
625: * substitution.
626: */
627: for (JClassType candidate : super typeHierarchy) {
628: JParameterizedType parameterizedCandidate = candidate
629: .isParameterized();
630: if (parameterizedCandidate == null) {
631: // If not parameterized then there is no substitution possible.
632: continue;
633: }
634:
635: if (parameterizedCandidate.getBaseType() != getBaseType()) {
636: // This candidate be parameterized to us.
637: continue;
638: }
639:
640: /*
641: * We have a parameterization of our base type. Now we need to see if it
642: * is possible to parameterize subtype such that candidate becomes
643: * equivalent to us.
644: */
645: JClassType[] candidateTypeArgs = parameterizedCandidate
646: .getTypeArgs();
647: JClassType[] myTypeArgs = getTypeArgs();
648: for (int i = 0; i < myTypeArgs.length; ++i) {
649: JClassType otherTypeArg = candidateTypeArgs[i];
650: JClassType myTypeArg = myTypeArgs[i];
651:
652: if (myTypeArg == otherTypeArg) {
653: // There are identical so there is no substitution that is needed.
654: continue;
655: }
656:
657: JTypeParameter otherTypeParameter = otherTypeArg
658: .isTypeParameter();
659: if (otherTypeParameter == null) {
660: // Not a type parameter and not equal so no substitution can make it
661: // equal.
662: return null;
663: }
664:
665: if (!otherTypeParameter.isAssignableFrom(myTypeArg)) {
666: // Make sure that my type argument can be substituted for this type
667: // parameter.
668: return null;
669: }
670:
671: substitutions.put(otherTypeParameter, myTypeArg);
672: }
673: }
674:
675: // Legal substitution can be made and is record in substitutions.
676: return substitutions;
677: }
678: }
|