| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.internal.corext.refactoring.generics; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.Platform; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.dom.CastExpression; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Type; |
| |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.CompilationUnitRange; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.ArrayType; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.GenericType; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.ParameterizedType; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeEnvironment; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeVariable; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ArrayElementVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ArrayTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CastVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CollectionElementVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ConstraintVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraint2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ImmutableTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.IndependentTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ParameterTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ParameterizedTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ReturnTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.SubTypeConstraint2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeEquivalenceSet; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.VariableVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; |
| |
| |
| public class InferTypeArgumentsTCModel { |
| |
| protected static final boolean DEBUG= Boolean.valueOf(Platform.getDebugOption("org.eclipse.jdt.ui/debug/TypeConstraints")).booleanValue(); //$NON-NLS-1$ |
| |
| private static final String INDEXED_COLLECTION_ELEMENTS= "IndexedCollectionElements"; //$NON-NLS-1$ |
| private static final String ARRAY_ELEMENT= "ArrayElement"; //$NON-NLS-1$ |
| private static final String USED_IN= "UsedIn"; //$NON-NLS-1$ |
| private static final String METHOD_RECEIVER= "MethodReceiver"; //$NON-NLS-1$ |
| private static final Map EMPTY_COLLECTION_ELEMENT_VARIABLES_MAP= Collections.EMPTY_MAP; |
| |
| protected static boolean fStoreToString= DEBUG; |
| |
| /** |
| * Map from a {@link ConstraintVariable2} to itself. |
| */ |
| private HashMap/*<ConstraintVariable2, ConstraintVariable2>*/ fConstraintVariables; |
| /** |
| * Map from a {@link ITypeConstraint2} to itself. |
| */ |
| private HashMap/*<ITypeConstraint2, ITypeConstraint2>*/ fTypeConstraints; |
| private Collection/*CastVariable2*/ fCastVariables; |
| |
| private HashSet fCuScopedConstraintVariables; |
| |
| private TypeEnvironment fTypeEnvironment; |
| |
| private static final int MAX_TTYPE_CACHE= 1024; |
| private Map/*<String, TType>*/ fTTypeCache= new LinkedHashMap(MAX_TTYPE_CACHE, 0.75f, true) { |
| private static final long serialVersionUID= 1L; |
| protected boolean removeEldestEntry(Map.Entry eldest) { |
| return size() > MAX_TTYPE_CACHE; |
| } |
| }; |
| |
| |
| public InferTypeArgumentsTCModel() { |
| fTypeConstraints= new HashMap(); |
| fConstraintVariables= new HashMap(); |
| fCastVariables= new ArrayList(); |
| |
| fCuScopedConstraintVariables= new HashSet(); |
| |
| fTypeEnvironment= new TypeEnvironment(true); |
| } |
| |
| /** |
| * Allows for avoiding the creation of SimpleTypeConstraints based on properties of |
| * their constituent ConstraintVariables and ConstraintOperators. Can be used to e.g. |
| * avoid creation of constraints for assignments between built-in types. |
| * |
| * @param cv1 |
| * @param cv2 |
| * @return <code>true</code> iff the type constraint should really be created |
| */ |
| protected boolean keep(ConstraintVariable2 cv1, ConstraintVariable2 cv2) { |
| if ((cv1 == null || cv2 == null)) |
| return false; |
| |
| if (cv1.equals(cv2)) { |
| if (cv1 == cv2) |
| return false; |
| else |
| Assert.isTrue(false); |
| } |
| |
| if (cv1 instanceof CollectionElementVariable2 || cv2 instanceof CollectionElementVariable2) |
| return true; |
| |
| if (cv1 instanceof IndependentTypeVariable2 || cv2 instanceof IndependentTypeVariable2) |
| return true; |
| |
| if (isAGenericType(cv1.getType())) |
| return true; |
| |
| if (isAGenericType(cv2.getType())) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * @param cv |
| * @return a List of ITypeConstraint2s where cv is used |
| */ |
| public List/*<ITypeConstraint2>*/ getUsedIn(ConstraintVariable2 cv) { |
| Object usedIn= cv.getData(USED_IN); |
| if (usedIn == null) |
| return Collections.EMPTY_LIST; |
| else if (usedIn instanceof ArrayList) |
| return Collections.unmodifiableList((ArrayList) usedIn); |
| else |
| return Collections.singletonList(usedIn); |
| } |
| |
| public void newCu() { |
| pruneUnusedCuScopedCvs(); |
| fCuScopedConstraintVariables.clear(); |
| fTTypeCache.clear(); |
| } |
| |
| private void pruneUnusedCuScopedCvs() { |
| for (Iterator iter= fCuScopedConstraintVariables.iterator(); iter.hasNext();) { |
| ConstraintVariable2 cv= (ConstraintVariable2) iter.next(); |
| pruneCvIfUnused(cv); |
| } |
| } |
| |
| private boolean pruneCvIfUnused(ConstraintVariable2 cv) { |
| if (getUsedIn(cv).size() != 0) |
| return false; |
| |
| if (cv.getTypeEquivalenceSet() != null) { |
| if (cv.getTypeEquivalenceSet().getContributingVariables().length > 0) |
| return false; |
| } |
| |
| ArrayElementVariable2 arrayElementVariable= getArrayElementVariable(cv); |
| if (arrayElementVariable != null && ! pruneCvIfUnused(arrayElementVariable)) |
| return false; |
| |
| Map elementVariables= getElementVariables(cv); |
| for (Iterator iter= elementVariables.values().iterator(); iter.hasNext();) { |
| CollectionElementVariable2 elementVariable= (CollectionElementVariable2) iter.next(); |
| if (! pruneCvIfUnused(elementVariable)) |
| return false; |
| } |
| |
| fConstraintVariables.remove(cv); |
| return true; |
| } |
| |
| public ConstraintVariable2[] getAllConstraintVariables() { |
| ConstraintVariable2[] result= new ConstraintVariable2[fConstraintVariables.size()]; |
| int i= 0; |
| for (Iterator iter= fConstraintVariables.keySet().iterator(); iter.hasNext(); i++) |
| result[i]= (ConstraintVariable2) iter.next(); |
| return result; |
| } |
| |
| public ITypeConstraint2[] getAllTypeConstraints() { |
| Set typeConstraints= fTypeConstraints.keySet(); |
| return (ITypeConstraint2[]) typeConstraints.toArray(new ITypeConstraint2[typeConstraints.size()]); |
| } |
| |
| public CastVariable2[] getCastVariables() { |
| return (CastVariable2[]) fCastVariables.toArray(new CastVariable2[fCastVariables.size()]); |
| } |
| |
| /** |
| * Controls calculation and storage of information for more readable toString() messages. |
| * <p><em>Warning: This method is for testing purposes only and should not be called except from unit tests.</em></p> |
| * |
| * @param store <code>true</code> iff information for toString() should be stored |
| */ |
| public static void setStoreToString(boolean store) { |
| fStoreToString= store; |
| } |
| |
| public void createSubtypeConstraint(ConstraintVariable2 cv1, ConstraintVariable2 cv2) { |
| if (! keep(cv1, cv2)) |
| return; |
| |
| ConstraintVariable2 storedCv1= storedCv(cv1); |
| ConstraintVariable2 storedCv2= storedCv(cv2); |
| ITypeConstraint2 typeConstraint= new SubTypeConstraint2(storedCv1, storedCv2); |
| |
| Object storedTc= fTypeConstraints.get(typeConstraint); |
| if (storedTc == null) { |
| fTypeConstraints.put(typeConstraint, typeConstraint); |
| } else { |
| typeConstraint= (ITypeConstraint2) storedTc; |
| } |
| |
| registerCvWithTc(storedCv1, typeConstraint); |
| registerCvWithTc(storedCv2, typeConstraint); |
| } |
| |
| private ConstraintVariable2 storedCv(ConstraintVariable2 cv) { |
| Object stored= fConstraintVariables.get(cv); |
| if (stored == null) { |
| fConstraintVariables.put(cv, cv); |
| return cv; |
| } else { |
| return (ConstraintVariable2) stored; |
| } |
| } |
| |
| private void registerCvWithTc(ConstraintVariable2 storedCv, ITypeConstraint2 typeConstraint) { |
| Object usedIn= storedCv.getData(USED_IN); |
| if (usedIn == null) { |
| storedCv.setData(USED_IN, typeConstraint); |
| } else if (usedIn instanceof ArrayList) { |
| ArrayList usedInList= (ArrayList) usedIn; |
| usedInList.add(typeConstraint); |
| } else { |
| ArrayList usedInList= new ArrayList(2); |
| usedInList.add(usedIn); |
| usedInList.add(typeConstraint); |
| storedCv.setData(USED_IN, usedInList); |
| } |
| } |
| |
| public void createEqualsConstraint(ConstraintVariable2 leftElement, ConstraintVariable2 rightElement) { |
| if (leftElement == null || rightElement == null) |
| return; |
| |
| TypeEquivalenceSet leftSet= leftElement.getTypeEquivalenceSet(); |
| TypeEquivalenceSet rightSet= rightElement.getTypeEquivalenceSet(); |
| if (leftSet == null) { |
| if (rightSet == null) { |
| TypeEquivalenceSet set= new TypeEquivalenceSet(leftElement, rightElement); |
| leftElement.setTypeEquivalenceSet(set); |
| rightElement.setTypeEquivalenceSet(set); |
| } else { |
| rightSet.add(leftElement); |
| leftElement.setTypeEquivalenceSet(rightSet); |
| } |
| } else { |
| if (rightSet == null) { |
| leftSet.add(rightElement); |
| rightElement.setTypeEquivalenceSet(leftSet); |
| } else if (leftSet == rightSet) { |
| return; |
| } else { |
| ConstraintVariable2[] cvs= rightSet.getContributingVariables(); |
| leftSet.addAll(cvs); |
| for (int i= 0; i < cvs.length; i++) |
| cvs[i].setTypeEquivalenceSet(leftSet); |
| } |
| } |
| } |
| |
| public TType createTType(ITypeBinding typeBinding) { |
| String key= typeBinding.getKey(); |
| TType cached= (TType) fTTypeCache.get(key); |
| if (cached != null) |
| return cached; |
| TType type= fTypeEnvironment.create(typeBinding); |
| fTTypeCache.put(key, type); |
| return type; |
| } |
| |
| private TType getBoxedType(ITypeBinding typeBinding, Expression expression) { |
| if (typeBinding == null) |
| return null; |
| |
| if (! typeBinding.isPrimitive()) |
| return createTType(typeBinding); |
| |
| if (expression == null || ! expression.resolveBoxing()) |
| return null; |
| |
| String primitiveName= typeBinding.getName(); |
| ITypeBinding boxed= expression.getAST().resolveWellKnownType(getBoxedTypeName(primitiveName)); |
| return createTType(boxed); |
| } |
| |
| private String getBoxedTypeName(String primitiveName) { |
| if ("long".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Long"; //$NON-NLS-1$ |
| |
| else if ("int".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Integer"; //$NON-NLS-1$ |
| |
| else if ("short".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Short"; //$NON-NLS-1$ |
| |
| else if ("char".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Character"; //$NON-NLS-1$ |
| |
| else if ("byte".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Byte"; //$NON-NLS-1$ |
| |
| else if ("boolean".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Boolean"; //$NON-NLS-1$ |
| |
| else if ("float".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Float"; //$NON-NLS-1$ |
| |
| else if ("double".equals(primitiveName)) //$NON-NLS-1$ |
| return "java.lang.Double"; //$NON-NLS-1$ |
| |
| else |
| return null; |
| } |
| |
| public VariableVariable2 makeVariableVariable(IVariableBinding variableBinding) { |
| if (variableBinding == null) |
| return null; |
| TType type= getBoxedType(variableBinding.getType(), /*no boxing*/null); |
| if (type == null) |
| return null; |
| VariableVariable2 cv= new VariableVariable2(type, variableBinding); |
| VariableVariable2 storedCv= (VariableVariable2) storedCv(cv); |
| if (storedCv == cv) { |
| if (! variableBinding.isField() || Modifier.isPrivate(variableBinding.getModifiers())) |
| fCuScopedConstraintVariables.add(storedCv); |
| makeElementVariables(storedCv, type); |
| makeArrayElementVariable(storedCv); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, '[' + variableBinding.getName() + ']'); |
| } |
| return storedCv; |
| } |
| |
| public VariableVariable2 makeDeclaredVariableVariable(IVariableBinding variableBinding, ICompilationUnit cu) { |
| VariableVariable2 cv= makeVariableVariable(variableBinding); |
| if (cv == null) |
| return null; |
| cv.setCompilationUnit(cu); |
| return cv; |
| } |
| |
| public TypeVariable2 makeTypeVariable(Type type) { |
| ICompilationUnit cu= RefactoringASTParser.getCompilationUnit(type); |
| TType ttype= getBoxedType(type.resolveBinding(), /*no boxing*/null); |
| if (ttype == null) |
| return null; |
| |
| CompilationUnitRange range= new CompilationUnitRange(cu, type); |
| TypeVariable2 typeVariable= new TypeVariable2(ttype, range); |
| TypeVariable2 storedCv= (TypeVariable2) storedCv(typeVariable); |
| if (storedCv == typeVariable) { |
| fCuScopedConstraintVariables.add(storedCv); |
| if (isAGenericType(ttype)) |
| makeElementVariables(storedCv, ttype); |
| makeArrayElementVariable(storedCv); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, type.toString()); |
| } |
| return storedCv; |
| } |
| |
| public IndependentTypeVariable2 makeIndependentTypeVariable(TypeVariable type) { |
| IndependentTypeVariable2 cv= new IndependentTypeVariable2(type); |
| IndependentTypeVariable2 storedCv= (IndependentTypeVariable2) storedCv(cv); |
| if (cv == storedCv) { |
| fCuScopedConstraintVariables.add(storedCv); |
| // if (isAGenericType(typeBinding)) // would lead to infinite recursion! |
| // makeElementVariables(storedCv, typeBinding); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, "IndependentType(" + type.getPrettySignature() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return storedCv; |
| } |
| |
| public ParameterizedTypeVariable2 makeParameterizedTypeVariable(ITypeBinding typeBinding) { |
| if (typeBinding == null) |
| return null; |
| TType type= createTType(typeBinding); |
| return makeParameterizedTypeVariable(type); |
| } |
| |
| private ParameterizedTypeVariable2 makeParameterizedTypeVariable(TType type) { |
| Assert.isTrue(isAGenericType(type)); |
| |
| ParameterizedTypeVariable2 cv= new ParameterizedTypeVariable2(type); |
| ParameterizedTypeVariable2 storedCv= (ParameterizedTypeVariable2) storedCv(cv); |
| if (cv == storedCv) { |
| fCuScopedConstraintVariables.add(storedCv); |
| makeElementVariables(storedCv, type); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, "ParameterizedType(" + type.getPrettySignature() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return storedCv; |
| } |
| |
| public ArrayTypeVariable2 makeArrayTypeVariable(ITypeBinding typeBinding) { |
| if (typeBinding == null) |
| return null; |
| TType type= createTType(typeBinding); |
| return makeArrayTypeVariable((ArrayType) type); |
| } |
| |
| private ArrayTypeVariable2 makeArrayTypeVariable(ArrayType type) { |
| ArrayTypeVariable2 cv= new ArrayTypeVariable2(type); |
| ArrayTypeVariable2 storedCv= (ArrayTypeVariable2) storedCv(cv); |
| if (cv == storedCv) { |
| fCuScopedConstraintVariables.add(storedCv); |
| makeArrayElementVariable(storedCv); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, "ArrayType(" + type.getPrettySignature() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return storedCv; |
| } |
| |
| public ParameterTypeVariable2 makeParameterTypeVariable(IMethodBinding methodBinding, int parameterIndex) { |
| if (methodBinding == null) |
| return null; |
| TType type= getBoxedType(methodBinding.getParameterTypes() [parameterIndex], /*no boxing*/null); |
| if (type == null) |
| return null; |
| |
| ParameterTypeVariable2 cv= new ParameterTypeVariable2(type, parameterIndex, methodBinding); |
| ParameterTypeVariable2 storedCv= (ParameterTypeVariable2) storedCv(cv); |
| if (storedCv == cv) { |
| if (methodBinding.getDeclaringClass().isLocal() || Modifier.isPrivate(methodBinding.getModifiers())) |
| fCuScopedConstraintVariables.add(cv); |
| makeElementVariables(storedCv, type); |
| makeArrayElementVariable(storedCv); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, "[Parameter(" + parameterIndex + "," + Bindings.asString(methodBinding) + ")]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| return storedCv; |
| } |
| |
| /** |
| * Make a ParameterTypeVariable2 from a method declaration. |
| * The constraint variable is always stored if it passes the type filter. |
| * @param methodBinding |
| * @param parameterIndex |
| * @param cu |
| * @return the ParameterTypeVariable2, or <code>null</code> |
| */ |
| public ParameterTypeVariable2 makeDeclaredParameterTypeVariable(IMethodBinding methodBinding, int parameterIndex, ICompilationUnit cu) { |
| if (methodBinding == null) |
| return null; |
| ParameterTypeVariable2 cv= makeParameterTypeVariable(methodBinding, parameterIndex); |
| if (cv == null) |
| return null; |
| cv.setCompilationUnit(cu); |
| return cv; |
| } |
| |
| public ReturnTypeVariable2 makeReturnTypeVariable(IMethodBinding methodBinding) { |
| if (methodBinding == null) |
| return null; |
| TType returnType= getBoxedType(methodBinding.getReturnType(), /*no boxing*/null); |
| if (returnType == null) |
| return null; |
| |
| ReturnTypeVariable2 cv= new ReturnTypeVariable2(returnType, methodBinding); |
| ReturnTypeVariable2 storedCv= (ReturnTypeVariable2) storedCv(cv); |
| if (cv == storedCv) { |
| makeElementVariables(storedCv, returnType); |
| makeArrayElementVariable(storedCv); |
| if (fStoreToString) |
| storedCv.setData(ConstraintVariable2.TO_STRING, "[ReturnType(" + Bindings.asString(methodBinding) + ")]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return storedCv; |
| } |
| |
| public ReturnTypeVariable2 makeDeclaredReturnTypeVariable(IMethodBinding methodBinding, ICompilationUnit cu) { |
| if (methodBinding == null) |
| return null; |
| ReturnTypeVariable2 cv= makeReturnTypeVariable(methodBinding); |
| if (cv == null) |
| return null; |
| |
| cv.setCompilationUnit(cu); |
| if (methodBinding.getDeclaringClass().isLocal()) |
| fCuScopedConstraintVariables.add(cv); |
| return cv; |
| } |
| |
| public ImmutableTypeVariable2 makeImmutableTypeVariable(ITypeBinding typeBinding, Expression expression) { |
| // Assert.isTrue(! typeBinding.isGenericType()); // see JDT/Core bug 80472 |
| TType type= getBoxedType(typeBinding, expression); |
| if (type == null) |
| return null; |
| return makeImmutableTypeVariable(type); |
| } |
| |
| public ImmutableTypeVariable2 makeImmutableTypeVariable(TType type) { |
| ImmutableTypeVariable2 cv= new ImmutableTypeVariable2(type); |
| ImmutableTypeVariable2 storedCv= (ImmutableTypeVariable2) storedCv(cv); |
| if (cv == storedCv) { |
| makeFixedElementVariables(storedCv, type); |
| makeArrayElementVariable(storedCv); |
| } |
| return storedCv; |
| } |
| |
| public static boolean isAGenericType(TType type) { |
| return type.isGenericType() |
| || type.isParameterizedType() |
| || type.isRawType(); |
| } |
| |
| public static boolean isAGenericType(ITypeBinding type) { |
| return type.isGenericType() |
| || type.isParameterizedType() |
| || type.isRawType(); |
| } |
| |
| public CastVariable2 makeCastVariable(CastExpression castExpression, ConstraintVariable2 expressionCv) { |
| ITypeBinding typeBinding= castExpression.resolveTypeBinding(); |
| ICompilationUnit cu= RefactoringASTParser.getCompilationUnit(castExpression); |
| CompilationUnitRange range= new CompilationUnitRange(cu, castExpression); |
| CastVariable2 castCv= new CastVariable2(createTType(typeBinding), range, expressionCv); |
| fCastVariables.add(castCv); |
| return castCv; |
| } |
| |
| public TypeEnvironment getTypeEnvironment() { |
| return fTypeEnvironment; |
| } |
| |
| public CollectionElementVariable2 getElementVariable(ConstraintVariable2 constraintVariable, ITypeBinding typeVariable) { |
| Assert.isTrue(typeVariable.isTypeVariable()); // includes null check |
| HashMap typeVarToElementVars= (HashMap) constraintVariable.getData(INDEXED_COLLECTION_ELEMENTS); |
| if (typeVarToElementVars == null) |
| return null; |
| return (CollectionElementVariable2) typeVarToElementVars.get(typeVariable.getKey()); |
| } |
| |
| public Map/*<String typeVariableKey, CollectionElementVariable2>*/ getElementVariables(ConstraintVariable2 constraintVariable) { |
| Map elementVariables= (Map) constraintVariable.getData(INDEXED_COLLECTION_ELEMENTS); |
| if (elementVariables == null) |
| return EMPTY_COLLECTION_ELEMENT_VARIABLES_MAP; |
| else |
| return elementVariables; |
| } |
| |
| public ArrayElementVariable2 getArrayElementVariable(ConstraintVariable2 constraintVariable) { |
| return (ArrayElementVariable2) constraintVariable.getData(ARRAY_ELEMENT); |
| } |
| |
| private void setArrayElementVariable(ConstraintVariable2 constraintVariable, ArrayElementVariable2 arrayElementVariable) { |
| constraintVariable.setData(ARRAY_ELEMENT, arrayElementVariable); |
| } |
| |
| public void makeArrayElementVariable(ConstraintVariable2 constraintVariable2) { |
| if (constraintVariable2.getType() == null || ! constraintVariable2.getType().isArrayType()) |
| return; |
| |
| ArrayElementVariable2 storedArrayElementVariable= getArrayElementVariable(constraintVariable2); |
| if (storedArrayElementVariable != null) |
| return; |
| |
| ArrayElementVariable2 arrayElementCv= new ArrayElementVariable2(constraintVariable2); |
| arrayElementCv= (ArrayElementVariable2) storedCv(arrayElementCv); |
| setArrayElementVariable(constraintVariable2, arrayElementCv); |
| |
| makeArrayElementVariable(arrayElementCv); //recursive |
| } |
| |
| public void makeElementVariables(ConstraintVariable2 expressionCv, TType type) { |
| if (isAGenericType(type)) { |
| GenericType genericType= (GenericType) type.getTypeDeclaration(); |
| TType[] typeParameters= genericType.getTypeParameters(); |
| for (int i= 0; i < typeParameters.length; i++) { |
| TypeVariable typeVariable= (TypeVariable) typeParameters[i]; |
| makeElementVariable(expressionCv, typeVariable, i); |
| if (typeVariable.getBounds().length != 0) { |
| //TODO: create subtype constraints for bounds |
| } |
| } |
| } |
| makeElementVariablesFromSupertypes(expressionCv, type.getTypeDeclaration()); |
| } |
| |
| private void makeElementVariablesFromSupertypes(ConstraintVariable2 expressionCv, TType type) { |
| TType superclass= type.getSuperclass(); |
| if (superclass != null) { |
| makeSupertypeElementVariables(expressionCv, superclass); |
| } |
| |
| TType[] interfaces= type.getInterfaces(); |
| for (int i= 0; i < interfaces.length; i++) { |
| makeSupertypeElementVariables(expressionCv, interfaces[i]); |
| } |
| |
| } |
| |
| private void makeSupertypeElementVariables(ConstraintVariable2 expressionCv, TType supertype) { |
| if (supertype.isParameterizedType() || supertype.isRawType()) { |
| TType[] typeArguments= null; |
| if (supertype.isParameterizedType()) { |
| typeArguments= ((ParameterizedType) supertype).getTypeArguments(); |
| } |
| TypeVariable[] typeParameters= ((GenericType) supertype.getTypeDeclaration()).getTypeParameters(); |
| for (int i= 0; i < typeParameters.length; i++) { |
| TypeVariable typeParameter= typeParameters[i]; |
| TType referenceTypeArgument; |
| if (typeArguments == null) { // raw type |
| referenceTypeArgument= typeParameter.getErasure(); |
| } else { |
| referenceTypeArgument= typeArguments[i]; |
| } |
| if (referenceTypeArgument.isTypeVariable()) { |
| CollectionElementVariable2 referenceTypeArgumentCv= getElementVariable(expressionCv, (TypeVariable) referenceTypeArgument); |
| setElementVariable(expressionCv, referenceTypeArgumentCv, typeParameter); |
| } else { |
| makeElementVariable(expressionCv, typeParameter, CollectionElementVariable2.NOT_DECLARED_TYPE_VARIABLE_INDEX); |
| } |
| } |
| } |
| makeElementVariablesFromSupertypes(expressionCv, supertype); |
| } |
| |
| public void makeFixedElementVariables(ConstraintVariable2 expressionCv, TType type) { |
| if (isAGenericType(type)) { |
| GenericType genericType= (GenericType) type.getTypeDeclaration(); |
| TType[] typeParameters= genericType.getTypeParameters(); |
| TType[] typeArguments= null; |
| if (type.isParameterizedType()) |
| typeArguments= ((ParameterizedType) type).getTypeArguments(); |
| |
| for (int i= 0; i < typeParameters.length; i++) { |
| TypeVariable typeVariable= (TypeVariable) typeParameters[i]; |
| CollectionElementVariable2 elementCv= makeElementVariable(expressionCv, typeVariable, i); |
| TType referenceTypeArgument; |
| if (typeArguments == null) { // raw type |
| continue; // do not consider |
| } else { |
| referenceTypeArgument= typeArguments[i]; |
| } |
| createEqualsConstraint(elementCv, makeImmutableTypeVariable(referenceTypeArgument)); |
| // if (typeVariable.getBounds().length != 0) { |
| // //TODO: create subtype constraints for bounds |
| // } |
| } |
| } |
| makeFixedElementVariablesFromSupertypes(expressionCv, type.getTypeDeclaration()); |
| } |
| |
| private void makeFixedElementVariablesFromSupertypes(ConstraintVariable2 expressionCv, TType type) { |
| TType superclass= type.getSuperclass(); |
| if (superclass != null) |
| makeFixedSupertypeElementVariables(expressionCv, superclass); |
| |
| TType[] interfaces= type.getInterfaces(); |
| for (int i= 0; i < interfaces.length; i++) |
| makeFixedSupertypeElementVariables(expressionCv, interfaces[i]); |
| } |
| |
| private void makeFixedSupertypeElementVariables(ConstraintVariable2 expressionCv, TType supertype) { |
| if (supertype.isParameterizedType() || supertype.isRawType()) { |
| TType[] typeArguments= null; |
| if (supertype.isParameterizedType()) |
| typeArguments= ((ParameterizedType) supertype).getTypeArguments(); |
| |
| TypeVariable[] typeParameters= ((GenericType) supertype.getTypeDeclaration()).getTypeParameters(); |
| for (int i= 0; i < typeParameters.length; i++) { |
| TypeVariable typeParameter= typeParameters[i]; |
| TType referenceTypeArgument; |
| if (typeArguments == null) { // raw type |
| continue; // do not consider |
| } else { |
| referenceTypeArgument= typeArguments[i]; |
| } |
| if (referenceTypeArgument.isTypeVariable()) { |
| CollectionElementVariable2 referenceTypeArgumentCv= getElementVariable(expressionCv, (TypeVariable) referenceTypeArgument); |
| setElementVariable(expressionCv, referenceTypeArgumentCv, typeParameter); |
| } else { |
| CollectionElementVariable2 elementCv= makeElementVariable(expressionCv, typeParameter, CollectionElementVariable2.NOT_DECLARED_TYPE_VARIABLE_INDEX); |
| createEqualsConstraint(elementCv, makeImmutableTypeVariable(referenceTypeArgument)); |
| } |
| } |
| } |
| makeFixedElementVariablesFromSupertypes(expressionCv, supertype); |
| } |
| |
| /** |
| * Create equality constraints between generic type variables of expressionCv and referenceCv. |
| * For example, the generic interface <code>java.lang.Iterable<E></code> defines a method |
| * <code>Iterator<E> iterator()</code>. Given |
| * <ul> |
| * <li>an expressionCv of a subtype of <code>Iterable</code>,</li> |
| * <li>a referenceCv of a subtype of <code>Iterator</code>, and</li> |
| * <li>a reference binding of the Iterable#iterator()'s return type (the parameterized type <code>Iterator<E></code>),</li> |
| * </ul> |
| * this method creates an equality constraint between the type variable E in expressionCV and |
| * the type variable E in referenceCV. |
| * |
| * @param expressionCv the type constraint variable of an expression |
| * @param methodTypeVariables |
| * @param referenceCv the type constraint variable of a type reference |
| * @param reference the declared type reference |
| */ |
| public void createTypeVariablesEqualityConstraints(ConstraintVariable2 expressionCv, Map/*<String, IndependentTypeVariable2>*/ methodTypeVariables, ConstraintVariable2 referenceCv, TType reference) { |
| if (reference.isParameterizedType() || reference.isRawType()) { |
| TType[] referenceTypeArguments= null; |
| if (reference.isParameterizedType()) { |
| referenceTypeArguments= ((ParameterizedType) reference).getTypeArguments(); |
| } |
| TType[] referenceTypeParameters= ((GenericType) reference.getTypeDeclaration()).getTypeParameters(); |
| for (int i= 0; i < referenceTypeParameters.length; i++) { |
| TypeVariable referenceTypeParameter= (TypeVariable) referenceTypeParameters[i]; |
| TType referenceTypeArgument; |
| if (referenceTypeArguments == null) |
| referenceTypeArgument= referenceTypeParameter.getErasure(); |
| else |
| referenceTypeArgument= referenceTypeArguments[i]; |
| if (referenceTypeArgument.isTypeVariable()) { |
| ConstraintVariable2 referenceTypeArgumentCv= getElementTypeCv(referenceTypeArgument, expressionCv, methodTypeVariables); |
| CollectionElementVariable2 referenceTypeParametersCv= getElementVariable(referenceCv, referenceTypeParameter); |
| createEqualsConstraint(referenceTypeArgumentCv, referenceTypeParametersCv); |
| } else if (referenceTypeArgument.isWildcardType()) { |
| ConstraintVariable2 referenceTypeArgumentCv= makeImmutableTypeVariable(fTypeEnvironment.VOID); //block it for now (bug 106174) |
| CollectionElementVariable2 referenceTypeParametersCv= getElementVariable(referenceCv, referenceTypeParameter); |
| createEqualsConstraint(referenceTypeArgumentCv, referenceTypeParametersCv); |
| |
| // WildcardType wildcardType= (WildcardType) referenceTypeArgument; |
| // if (wildcardType.isUnboundWildcardType()) { |
| // ConstraintVariable2 referenceTypeArgumentCv= makeImmutableTypeVariable(wildcardType); |
| // CollectionElementVariable2 referenceTypeParametersCv= getElementVariable(referenceCv, referenceTypeParameter); |
| // createEqualsConstraint(referenceTypeArgumentCv, referenceTypeParametersCv); |
| // } else if (wildcardType.isSuperWildcardType() && wildcardType.getBound().isTypeVariable()) { |
| // ConstraintVariable2 referenceTypeArgumentBoundCv= getElementTypeCv(wildcardType.getBound(), expressionCv, methodTypeVariables); |
| // CollectionElementVariable2 referenceTypeParametersCv= getElementVariable(referenceCv, referenceTypeParameter); |
| // //TODO: need *strict* subtype constraint? |
| // createSubtypeConstraint(referenceTypeParametersCv, referenceTypeArgumentBoundCv); |
| // } |
| // else: TODO |
| |
| // } else if (referenceTypeArgument.isParameterizedType()) { |
| // //TODO: nested containers |
| // ParameterizedType parameterizedType= (ParameterizedType) referenceTypeArgument; |
| // ParameterizedTypeVariable2 parameterizedTypeCv= makeParameterizedTypeVariable(parameterizedType.getTypeDeclaration()); |
| // CollectionElementVariable2 referenceTypeParametersCv= getElementVariable(referenceCv, referenceTypeParameter); |
| // createEqualsConstraint(parameterizedTypeCv, referenceTypeParametersCv); |
| // createElementEqualsConstraints(parameterizedTypeCv, referenceTypeParametersCv); |
| } else { |
| //TODO |
| } |
| } |
| |
| } else if (reference.isArrayType()) { |
| TType elementType= ((ArrayType) reference).getElementType(); |
| if (elementType.isRawType()) |
| elementType= elementType.getErasure(); |
| ConstraintVariable2 elementTypeCv= getElementTypeCv(elementType, expressionCv, methodTypeVariables); |
| ArrayElementVariable2 arrayElementTypeCv= getArrayElementVariable(referenceCv); |
| createEqualsConstraint(elementTypeCv, arrayElementTypeCv); |
| } |
| } |
| |
| private ConstraintVariable2 getElementTypeCv(TType elementType, ConstraintVariable2 expressionCv, Map methodTypeVariables) { |
| if (elementType.isTypeVariable()) { |
| ConstraintVariable2 elementTypeCv= (ConstraintVariable2) methodTypeVariables.get(elementType.getBindingKey()); |
| if (elementTypeCv != null) |
| return elementTypeCv; |
| if (expressionCv != null) |
| return getElementVariable(expressionCv, (TypeVariable) elementType); |
| } |
| return null; |
| } |
| |
| private CollectionElementVariable2 makeElementVariable(ConstraintVariable2 expressionCv, TypeVariable typeVariable, int declarationTypeVariableIndex) { |
| if (expressionCv == null) |
| return null; |
| |
| CollectionElementVariable2 storedElementVariable= getElementVariable(expressionCv, typeVariable); |
| if (storedElementVariable != null) |
| return storedElementVariable; |
| |
| CollectionElementVariable2 cv= new CollectionElementVariable2(expressionCv, typeVariable, declarationTypeVariableIndex); |
| cv= (CollectionElementVariable2) storedCv(cv); |
| setElementVariable(expressionCv, cv, typeVariable); |
| return cv; |
| } |
| |
| private void setElementVariable(ConstraintVariable2 typeConstraintVariable, CollectionElementVariable2 elementVariable, TypeVariable typeVariable) { |
| HashMap keyToElementVar= (HashMap) typeConstraintVariable.getData(INDEXED_COLLECTION_ELEMENTS); |
| String key= typeVariable.getBindingKey(); |
| if (keyToElementVar == null) { |
| keyToElementVar= new HashMap(); |
| typeConstraintVariable.setData(INDEXED_COLLECTION_ELEMENTS, keyToElementVar); |
| } else { |
| Object existingElementVar= keyToElementVar.get(key); |
| if (existingElementVar != null) { |
| Assert.isTrue(existingElementVar == elementVariable); |
| } |
| } |
| keyToElementVar.put(key, elementVariable); |
| } |
| |
| public CollectionElementVariable2 getElementVariable(ConstraintVariable2 constraintVariable, TypeVariable typeVariable) { |
| Assert.isTrue(typeVariable.isTypeVariable()); // includes null check |
| HashMap typeVarToElementVars= (HashMap) constraintVariable.getData(INDEXED_COLLECTION_ELEMENTS); |
| if (typeVarToElementVars == null) |
| return null; |
| return (CollectionElementVariable2) typeVarToElementVars.get(typeVariable.getBindingKey()); |
| } |
| |
| public void createElementEqualsConstraints(ConstraintVariable2 cv, ConstraintVariable2 initializerCv) { |
| internalCreateElementEqualsConstraints(cv, initializerCv, false); |
| } |
| |
| public void createAssignmentElementConstraints(ConstraintVariable2 cv, ConstraintVariable2 initializerCv) { |
| internalCreateElementEqualsConstraints(cv, initializerCv, true); |
| } |
| |
| private void internalCreateElementEqualsConstraints(ConstraintVariable2 cv, ConstraintVariable2 initializerCv, boolean isAssignment) { |
| if (cv == null || initializerCv == null) |
| return; |
| |
| Map leftElements= getElementVariables(cv); |
| Map rightElements= getElementVariables(initializerCv); |
| for (Iterator leftIter= leftElements.entrySet().iterator(); leftIter.hasNext();) { |
| Map.Entry leftEntry= (Map.Entry) leftIter.next(); |
| String leftTypeVariableKey= (String) leftEntry.getKey(); |
| CollectionElementVariable2 rightElementVariable= (CollectionElementVariable2) rightElements.get(leftTypeVariableKey); |
| if (rightElementVariable != null) { |
| CollectionElementVariable2 leftElementVariable= (CollectionElementVariable2) leftEntry.getValue(); |
| createEqualsConstraint(leftElementVariable, rightElementVariable); |
| internalCreateElementEqualsConstraints(leftElementVariable, rightElementVariable, false); // recursive |
| } |
| } |
| |
| ArrayElementVariable2 leftArrayElement= getArrayElementVariable(cv); |
| ArrayElementVariable2 rightArrayElement= getArrayElementVariable(initializerCv); |
| if (leftArrayElement != null && rightArrayElement != null) { |
| if (isAssignment) |
| createSubtypeConstraint(rightArrayElement, leftArrayElement); |
| else |
| createEqualsConstraint(leftArrayElement, rightArrayElement); |
| internalCreateElementEqualsConstraints(leftArrayElement, rightArrayElement, false); // recursive |
| } |
| } |
| |
| /** |
| * @return the receiver of the method invocation this expressionVariable |
| * depends on, or null iff no receiver is available. If the receiver |
| * stayed raw, then the method return type cannot be substituted. |
| */ |
| public ConstraintVariable2 getMethodReceiverCv(ConstraintVariable2 expressionVariable) { |
| return (ConstraintVariable2) expressionVariable.getData(METHOD_RECEIVER); |
| } |
| |
| public void setMethodReceiverCV(ConstraintVariable2 expressionVariable, ConstraintVariable2 methodReceiverCV) { |
| expressionVariable.setData(METHOD_RECEIVER, methodReceiverCV); |
| } |
| |
| } |