| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.core.ext.typeinfo; |
| |
| import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType; |
| import com.google.gwt.dev.util.collect.IdentityHashMap; |
| import com.google.gwt.dev.util.collect.Lists; |
| import com.google.gwt.dev.util.collect.Maps; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Represents a parameterized type in a declaration. |
| */ |
| public class JParameterizedType extends JMaybeParameterizedType { |
| /** |
| * Create a parameterized type along with any necessary enclosing |
| * parameterized types. Enclosing parameterized types are necessary when the |
| * base type is a non-static member and the enclosing type is also generic. |
| */ |
| private static JParameterizedType createParameterizedTypeRecursive( |
| JGenericType baseType, Map<JTypeParameter, JClassType> substitutionMap) { |
| JClassType enclosingType = baseType.getEnclosingType(); |
| if (baseType.isMemberType() && !baseType.isStatic()) { |
| // This base type is a non-static generic type so we build the necessary |
| // enclosing parameterized type and update the enclosing type to be |
| // a parameterized type. |
| JGenericType isGenericEnclosingType = enclosingType.isGenericType(); |
| if (isGenericEnclosingType != null) { |
| enclosingType = createParameterizedTypeRecursive( |
| isGenericEnclosingType, substitutionMap); |
| } |
| } |
| |
| JTypeParameter[] typeParameters = baseType.getTypeParameters(); |
| JClassType[] newTypeArgs = new JClassType[typeParameters.length]; |
| TypeOracle oracle = baseType.getOracle(); |
| for (int i = 0; i < newTypeArgs.length; ++i) { |
| JClassType newTypeArg = substitutionMap.get(typeParameters[i]); |
| if (newTypeArg == null) { |
| newTypeArg = oracle.getWildcardType(BoundType.EXTENDS, |
| typeParameters[i].getFirstBound()); |
| } |
| |
| newTypeArgs[i] = newTypeArg; |
| } |
| |
| JParameterizedType parameterizedType = oracle.getParameterizedType( |
| baseType, enclosingType, newTypeArgs); |
| return parameterizedType; |
| } |
| |
| private final JClassType enclosingType; |
| |
| private List<JClassType> interfaces; |
| |
| /** |
| * This map records the JClassType that should be used in place of a given |
| * {@link JTypeParameter}. |
| */ |
| private Map<JTypeParameter, JClassType> lazySubstitutionMap; |
| |
| private JClassType lazySuperclass; |
| |
| private final AbstractMembers members; |
| |
| private final List<JClassType> typeArgs; |
| |
| JParameterizedType(JGenericType baseType, JClassType enclosingType, |
| JClassType[] typeArgs) { |
| super.setBaseType(baseType); |
| |
| this.enclosingType = enclosingType; |
| |
| // NOTE: this instance is not considered a nested type of the enclosing type |
| |
| final JParameterizedType parameterizedType = this; |
| members = new DelegateMembers(this, baseType, new Substitution() { |
| public JType getSubstitution(JType type) { |
| return type.getSubstitutedType(parameterizedType); |
| } |
| }); |
| |
| this.typeArgs = Lists.create(typeArgs); |
| assert (this.typeArgs.indexOf(null) == -1) |
| : "Unresolved typeArg creating JParameterizedType from " + baseType; |
| |
| // NOTE: Can't perform substitutions until we are done building |
| } |
| |
| @Override |
| public JConstructor findConstructor(JType[] paramTypes) { |
| return members.findConstructor(paramTypes); |
| } |
| |
| @Override |
| public JField findField(String name) { |
| return members.findField(name); |
| } |
| |
| @Override |
| public JMethod findMethod(String name, JType[] paramTypes) { |
| return members.findMethod(name, paramTypes); |
| } |
| |
| @Override |
| public JClassType findNestedType(String typeName) { |
| return members.findNestedType(typeName); |
| } |
| |
| @Override |
| public JConstructor getConstructor(JType[] paramTypes) |
| throws NotFoundException { |
| return members.getConstructor(paramTypes); |
| } |
| |
| @Override |
| public JConstructor[] getConstructors() { |
| return members.getConstructors(); |
| } |
| |
| @Override |
| public JClassType getEnclosingType() { |
| return enclosingType; |
| } |
| |
| @Override |
| public JField getField(String name) { |
| return members.getField(name); |
| } |
| |
| @Override |
| public JField[] getFields() { |
| return members.getFields(); |
| } |
| |
| @Override |
| public JClassType[] getImplementedInterfaces() { |
| if (interfaces == null) { |
| interfaces = new ArrayList<JClassType>(); |
| JClassType[] intfs = getBaseType().getImplementedInterfaces(); |
| for (JClassType intf : intfs) { |
| JClassType newIntf = intf.getSubstitutedType(this); |
| interfaces.add(newIntf); |
| } |
| interfaces = Lists.normalize(interfaces); |
| } |
| return interfaces.toArray(TypeOracle.NO_JCLASSES); |
| } |
| |
| @Override |
| public JMethod[] getInheritableMethods() { |
| return members.getInheritableMethods(); |
| } |
| |
| @Override |
| public JMethod getMethod(String name, JType[] paramTypes) |
| throws NotFoundException { |
| return members.getMethod(name, paramTypes); |
| } |
| |
| @Override |
| public JMethod[] getMethods() { |
| return members.getMethods(); |
| } |
| |
| @Override |
| public JClassType getNestedType(String typeName) throws NotFoundException { |
| return members.getNestedType(typeName); |
| } |
| |
| @Override |
| public JClassType[] getNestedTypes() { |
| return members.getNestedTypes(); |
| } |
| |
| /** |
| * @deprecated See {@link #getQualifiedSourceName()} |
| */ |
| @Deprecated |
| public String getNonParameterizedQualifiedSourceName() { |
| return getQualifiedSourceName(); |
| } |
| |
| @Override |
| public JMethod[] getOverloads(String name) { |
| return members.getOverloads(name); |
| } |
| |
| @Override |
| public JMethod[] getOverridableMethods() { |
| return members.getOverridableMethods(); |
| } |
| |
| @Override |
| public String getParameterizedQualifiedSourceName() { |
| StringBuffer sb = new StringBuffer(); |
| |
| if (getEnclosingType() != null) { |
| sb.append(getEnclosingType().getParameterizedQualifiedSourceName()); |
| sb.append("."); |
| sb.append(getSimpleSourceName()); |
| } else { |
| sb.append(getQualifiedSourceName()); |
| } |
| |
| if (typeArgs.size() > 0) { |
| sb.append('<'); |
| boolean needComma = false; |
| for (JType typeArg : typeArgs) { |
| if (needComma) { |
| sb.append(", "); |
| } else { |
| needComma = true; |
| } |
| sb.append(typeArg.getParameterizedQualifiedSourceName()); |
| } |
| sb.append('>'); |
| } else { |
| /* |
| * Non-static, inner classes of generic types are modeled as generic, even |
| * if they do not declare type parameters or reference the type parameters |
| * of their enclosing generic type. |
| */ |
| } |
| |
| return sb.toString(); |
| } |
| |
| @Override |
| public String getQualifiedBinaryName() { |
| return getBaseType().getQualifiedBinaryName(); |
| } |
| |
| /** |
| * Everything is fully qualified and includes the < and > in the |
| * signature. |
| */ |
| @Override |
| public String getQualifiedSourceName() { |
| return getBaseType().getQualifiedSourceName(); |
| } |
| |
| public JClassType getRawType() { |
| return getBaseType().getRawType(); |
| } |
| |
| /** |
| * In this case, the raw type name. |
| */ |
| @Override |
| public String getSimpleSourceName() { |
| return getBaseType().getSimpleSourceName(); |
| } |
| |
| /* |
| * Goal: Return a list of possible subtypes of this parameterized type. In the |
| * event that we have generic subtypes and we cannot resolve the all of the |
| * type arguments, we need to wildcard types in place of the arguments that we |
| * cannot resolve. |
| * |
| * Algorithm: - Ask generic type for its subtypes - Filter subtypes of the |
| * generic which cannot be our subtype. |
| */ |
| @Override |
| public JClassType[] getSubtypes() { |
| List<JClassType> subtypeList = new ArrayList<JClassType>(); |
| |
| // Parameterized types are not tracked in the subtype hierarchy; ask base |
| // type |
| JClassType[] genericSubtypes = getBaseType().getSubtypes(); |
| for (JClassType subtype : genericSubtypes) { |
| // Could be a subtype depending on how it is substituted |
| Map<JTypeParameter, JClassType> substitutions = findSubtypeSubstitution(subtype); |
| if (substitutions != null) { |
| JGenericType genericType = subtype.isGenericType(); |
| if (genericType != null) { |
| subtype = createParameterizedTypeRecursive(genericType, substitutions); |
| } else { |
| // If this is not a generic type then there should not be any |
| // substitution. |
| assert (substitutions.isEmpty()); |
| } |
| |
| subtypeList.add(subtype); |
| } |
| } |
| |
| return subtypeList.toArray(TypeOracle.NO_JCLASSES); |
| } |
| |
| @Override |
| public JClassType getSuperclass() { |
| if (isInterface() != null) { |
| return null; |
| } |
| |
| if (lazySuperclass == null) { |
| JGenericType baseType = getBaseType(); |
| JClassType superclass = baseType.getSuperclass(); |
| assert (superclass != null); |
| lazySuperclass = superclass.getSubstitutedType(this); |
| } |
| |
| return lazySuperclass; |
| } |
| |
| public JClassType[] getTypeArgs() { |
| return typeArgs.toArray(TypeOracle.NO_JCLASSES); |
| } |
| |
| @Override |
| public JGenericType isGenericType() { |
| return null; |
| } |
| |
| @Override |
| public JParameterizedType isParameterized() { |
| return this; |
| } |
| |
| @Override |
| public JRawType isRawType() { |
| return null; |
| } |
| |
| @Override |
| public JWildcardType isWildcard() { |
| return null; |
| } |
| |
| @Override |
| public String toString() { |
| if (isInterface() != null) { |
| return "interface " + getParameterizedQualifiedSourceName(); |
| } |
| |
| return "class " + getParameterizedQualifiedSourceName(); |
| } |
| |
| @Override |
| protected JClassType findNestedTypeImpl(String[] typeName, int index) { |
| return members.findNestedTypeImpl(typeName, index); |
| } |
| |
| @Override |
| protected void getInheritableMethodsOnSuperclassesAndThisClass( |
| Map<String, JMethod> methodsBySignature) { |
| members.getInheritableMethodsOnSuperclassesAndThisClass(methodsBySignature); |
| } |
| |
| /** |
| * Gets the methods declared in interfaces that this type extends. If this |
| * type is a class, its own methods are not added. If this type is an |
| * interface, its own methods are added. Used internally by |
| * {@link #getOverridableMethods()}. |
| * |
| * @param methodsBySignature |
| */ |
| @Override |
| protected void getInheritableMethodsOnSuperinterfacesAndMaybeThisInterface( |
| Map<String, JMethod> methodsBySignature) { |
| members.getInheritableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature); |
| } |
| |
| @Override |
| JClassType getSubstitutedType(JParameterizedType parameterizedType) { |
| maybeInitializeTypeParameterSubstitutionMap(); |
| |
| if (this == parameterizedType) { |
| return this; |
| } |
| |
| JClassType[] newTypeArgs = new JClassType[typeArgs.size()]; |
| for (int i = 0; i < newTypeArgs.length; ++i) { |
| newTypeArgs[i] = typeArgs.get(i).getSubstitutedType(parameterizedType); |
| } |
| |
| return getOracle().getParameterizedType(getBaseType(), getEnclosingType(), |
| newTypeArgs); |
| } |
| |
| /** |
| * Returns the {@link JClassType} that is a substitute for the given |
| * {@link JTypeParameter}. If there is no substitution, the original |
| * {@link JTypeParameter} is returned. |
| */ |
| JClassType getTypeParameterSubstitution(JTypeParameter typeParameter) { |
| maybeInitializeTypeParameterSubstitutionMap(); |
| |
| JClassType substitute = lazySubstitutionMap.get(typeParameter); |
| if (substitute != null) { |
| return substitute; |
| } |
| |
| return typeParameter; |
| } |
| |
| boolean hasTypeArgs(JClassType[] otherArgTypes) { |
| if (otherArgTypes.length != typeArgs.size()) { |
| return false; |
| } |
| |
| for (int i = 0; i < otherArgTypes.length; ++i) { |
| // Identity tests are ok since identity is durable within an oracle. |
| // |
| if (otherArgTypes[i] != typeArgs.get(i)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Initialize a map of substitutions for {@link JTypeParameter}s to |
| * corresponding {@link JClassType}s. This can only be initialized after the |
| * {@link com.google.gwt.dev.javac.TypeOracleMediator TypeOracleMediator} has |
| * fully resolved all of the {@link JClassType}s. |
| */ |
| void maybeInitializeTypeParameterSubstitutionMap() { |
| if (lazySubstitutionMap != null) { |
| return; |
| } |
| lazySubstitutionMap = new IdentityHashMap<JTypeParameter, JClassType>(); |
| |
| JParameterizedType currentParameterizedType = this; |
| |
| while (currentParameterizedType != null) { |
| JGenericType genericType = currentParameterizedType.getBaseType(); |
| JTypeParameter[] typeParameters = genericType.getTypeParameters(); |
| JClassType[] typeArguments = currentParameterizedType.getTypeArgs(); |
| |
| for (JTypeParameter typeParameter : typeParameters) { |
| lazySubstitutionMap.put(typeParameter, |
| typeArguments[typeParameter.getOrdinal()]); |
| } |
| |
| if (currentParameterizedType.isStatic()) { |
| break; |
| } |
| |
| JClassType maybeParameterizedType = currentParameterizedType.getEnclosingType(); |
| if (maybeParameterizedType == null |
| || maybeParameterizedType.isParameterized() == null) { |
| break; |
| } |
| currentParameterizedType = maybeParameterizedType.isParameterized(); |
| } |
| lazySubstitutionMap = Maps.normalize(lazySubstitutionMap); |
| } |
| |
| void setTypeArguments(JClassType[] typeArgs) { |
| this.typeArgs.addAll(Arrays.asList(typeArgs)); |
| } |
| |
| /** |
| * Returns a map of substitutions that will make the subtype a proper subtype |
| * of this parameterized type. The map maybe empty in the case that it is |
| * already an exact subtype. |
| */ |
| private Map<JTypeParameter, JClassType> findSubtypeSubstitution( |
| JClassType subtype) { |
| Map<JTypeParameter, JClassType> substitutions = new IdentityHashMap<JTypeParameter, JClassType>(); |
| |
| // Get the supertype hierarchy. If this JParameterizedType exists |
| // exactly in this set we are done. |
| Set<JClassType> supertypeHierarchy = getFlattenedSuperTypeHierarchy(subtype); |
| if (supertypeHierarchy.contains(this)) { |
| return substitutions; |
| } |
| |
| /* |
| * Try to find a parameterized supertype whose base type is the same as our |
| * own. Because that parameterized supertype might be made into ourself via |
| * substitution. |
| */ |
| for (JClassType candidate : supertypeHierarchy) { |
| JParameterizedType parameterizedCandidate = candidate.isParameterized(); |
| if (parameterizedCandidate == null) { |
| // If not parameterized then there is no substitution possible. |
| continue; |
| } |
| |
| if (parameterizedCandidate.getBaseType() != getBaseType()) { |
| // This candidate be parameterized to us. |
| continue; |
| } |
| |
| /* |
| * We have a parameterization of our base type. Now we need to see if it |
| * is possible to parameterize subtype such that candidate becomes |
| * equivalent to us. |
| */ |
| JClassType[] candidateTypeArgs = parameterizedCandidate.getTypeArgs(); |
| JClassType[] myTypeArgs = getTypeArgs(); |
| for (int i = 0; i < myTypeArgs.length; ++i) { |
| JClassType otherTypeArg = candidateTypeArgs[i]; |
| JClassType myTypeArg = myTypeArgs[i]; |
| |
| if (myTypeArg == otherTypeArg) { |
| // There are identical so there is no substitution that is needed. |
| continue; |
| } |
| |
| JTypeParameter otherTypeParameter = otherTypeArg.isTypeParameter(); |
| if (otherTypeParameter == null) { |
| // Not a type parameter and not equal so no substitution can make it |
| // equal. |
| return null; |
| } |
| |
| if (!otherTypeParameter.isAssignableFrom(myTypeArg)) { |
| // Make sure that my type argument can be substituted for this type |
| // parameter. |
| return null; |
| } |
| |
| substitutions.put(otherTypeParameter, myTypeArg); |
| } |
| } |
| |
| // Legal substitution can be made and is record in substitutions. |
| return substitutions; |
| } |
| } |