--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.structure.constraints;
+
+import java.util.AbstractSet;
+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.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.core.dom.CastExpression;
+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.Name;
+import org.eclipse.jdt.core.dom.Type;
+
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.CompilationUnitRange;
+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.typeconstraints2.CastVariable2;
+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.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;
+
+
+/**
+ * Type constraints model to hold all type constraints to replace type occurrences by a given supertype.
+ *
+ * @since 3.1
+ */
+public final class SuperTypeConstraintsModel {
+
+ /** Customized implementation of a hash set
+ * @param <E> the element type */
+ private static class HashedSet<E> extends AbstractSet<E> {
+
+ /** The backing hash map */
+ private final Map<E, E> fImplementation= new HashMap<E, E>();
+
+ /*
+ * @see java.util.AbstractCollection#add(java.lang.Object)
+ */
+ @Override
+ public final boolean add(final E object) {
+ return fImplementation.put(object, object) == null;
+ }
+
+ /**
+ * Attempts to add the specified object to this set.
+ *
+ * @param object the object to add
+ * @return An already existing object considered equal to the specified one, or the newly added object
+ */
+ public final E addExisting(final E object) {
+ final E result= fImplementation.get(object);
+ if (result != null)
+ return result;
+ fImplementation.put(object, object);
+ return object;
+ }
+
+ /*
+ * @see java.util.AbstractCollection#clear()
+ */
+ @Override
+ public final void clear() {
+ fImplementation.clear();
+ }
+
+ /*
+ * @see java.util.AbstractCollection#contains(java.lang.Object)
+ */
+ @Override
+ public final boolean contains(final Object object) {
+ return fImplementation.containsKey(object);
+ }
+
+ /*
+ * @see java.util.AbstractCollection#isEmpty()
+ */
+ @Override
+ public final boolean isEmpty() {
+ return fImplementation.isEmpty();
+ }
+
+ /*
+ * @see java.util.AbstractCollection#iterator()
+ */
+ @Override
+ public final Iterator<E> iterator() {
+ return fImplementation.keySet().iterator();
+ }
+
+ /*
+ * @see java.util.AbstractCollection#remove(java.lang.Object)
+ */
+ @Override
+ public final boolean remove(final Object object) {
+ return fImplementation.remove(object) == object;
+ }
+
+ /*
+ * @see java.util.AbstractCollection#size()
+ */
+ @Override
+ public final int size() {
+ return fImplementation.size();
+ }
+ }
+
+ /** The usage data */
+ private static final String DATA_USAGE= "us"; //$NON-NLS-1$
+
+ /** Maximal number of TTypes */
+ private static final int MAX_CACHE= 64;
+
+ /**
+ * Returns the usage of the specified constraint variable.
+ *
+ * @param variable the constraint variable
+ * @return the usage of the constraint variable (element type: <code>ITypeConstraint2</code>)
+ */
+ public static Collection<ITypeConstraint2> getVariableUsage(final ConstraintVariable2 variable) {
+ final Object data= variable.getData(DATA_USAGE);
+ if (data == null) {
+ return Collections.emptyList();
+ } else if (data instanceof Collection) {
+ @SuppressWarnings("unchecked")
+ Collection<ITypeConstraint2> collection= (Collection<ITypeConstraint2>) data;
+ return Collections.unmodifiableCollection(collection);
+ } else {
+ return Collections.singletonList((ITypeConstraint2) data);
+ }
+ }
+
+ /**
+ * Is the type represented by the specified binding a constrained type?
+ *
+ * @param binding the binding to check, or <code>null</code>
+ * @return <code>true</code> if it is constrained, <code>false</code> otherwise
+ */
+ public static boolean isConstrainedType(final ITypeBinding binding) {
+ return binding != null && !binding.isSynthetic() && !binding.isPrimitive();
+ }
+
+ /**
+ * Sets the usage of the specified constraint variable.
+ *
+ * @param variable the constraint variable
+ * @param constraint the type constraint
+ */
+ public static void setVariableUsage(final ConstraintVariable2 variable, ITypeConstraint2 constraint) {
+ final Object data= variable.getData(DATA_USAGE);
+ if (data == null) {
+ variable.setData(DATA_USAGE, constraint);
+ } else if (data instanceof Collection) {
+ @SuppressWarnings("unchecked")
+ Collection<ITypeConstraint2> collection= (Collection<ITypeConstraint2>) data;
+ collection.add(constraint);
+ } else {
+ final Collection<Object> usage= new ArrayList<Object>(2);
+ usage.add(data);
+ usage.add(constraint);
+ variable.setData(DATA_USAGE, usage);
+ }
+ }
+
+ /** The cast variables (element type: <code>CastVariable2</code>) */
+ private final Collection<CastVariable2> fCastVariables= new ArrayList<CastVariable2>();
+
+ /** The compliance level */
+ private int fCompliance= 3;
+
+ /** The set of constraint variables (element type: <code>ConstraintVariable2</code>) */
+ private final HashedSet<ConstraintVariable2> fConstraintVariables= new HashedSet<ConstraintVariable2>();
+
+ /** The covariant type constraints (element type: <code>CovariantTypeConstraint</code>) */
+ private final Collection<ITypeConstraint2> fCovariantTypeConstraints= new ArrayList<ITypeConstraint2>();
+
+ /** The type environment to use */
+ private TypeEnvironment fEnvironment;
+
+ /** The subtype to replace */
+ private final TType fSubType;
+
+ /** The supertype as replacement */
+ private final TType fSuperType;
+
+ /** The TType cache */
+ private Map<String, TType> fTTypeCache= new LinkedHashMap<String, TType>(MAX_CACHE, 0.75f, true) {
+
+ private static final long serialVersionUID= 1L;
+
+ @Override
+ protected final boolean removeEldestEntry(Map.Entry<String, TType> entry) {
+ return size() > MAX_CACHE;
+ }
+ };
+
+ /** The set of type constraints (element type: <code>ITypeConstraint2</code>) */
+ private final Set<ITypeConstraint2> fTypeConstraints= new HashSet<ITypeConstraint2>();
+
+ /**
+ * Creates a new super type constraints model.
+ *
+ * @param environment the type environment
+ * @param subType the subtype to replace
+ * @param superType the supertype replacement
+ */
+ public SuperTypeConstraintsModel(final TypeEnvironment environment, TType subType, TType superType) {
+ fEnvironment= environment;
+ fSubType= subType;
+ fSuperType= superType;
+ }
+
+ /**
+ * Gets called when the creation of the model begins.
+ */
+ public final void beginCreation() {
+ // Do nothing right now
+ }
+
+ /**
+ * Creates a cast variable.
+ *
+ * @param expression the cast expression
+ * @param variable the associated constraint variable
+ * @return the created cast variable
+ */
+ public final ConstraintVariable2 createCastVariable(final CastExpression expression, final ConstraintVariable2 variable) {
+ ITypeBinding binding= expression.resolveTypeBinding();
+ if (binding.isArray())
+ binding= binding.getElementType();
+ if (isConstrainedType(binding)) {
+ final CastVariable2 result= new CastVariable2(createTType(binding), new CompilationUnitRange(RefactoringASTParser.getCompilationUnit(expression), expression), variable);
+ fCastVariables.add(result);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * Creates a conditional type constraint.
+ *
+ * @param expressionVariable the expression type constraint variable
+ * @param thenVariable the then type constraint variable
+ * @param elseVariable the else type constraint variable
+ */
+ public final void createConditionalTypeConstraint(final ConstraintVariable2 expressionVariable, final ConstraintVariable2 thenVariable, final ConstraintVariable2 elseVariable) {
+ final ITypeConstraint2 constraint= new ConditionalTypeConstraint(expressionVariable, thenVariable, elseVariable);
+ if (!fTypeConstraints.contains(constraint)) {
+ fTypeConstraints.add(constraint);
+ setVariableUsage(expressionVariable, constraint);
+ setVariableUsage(thenVariable, constraint);
+ setVariableUsage(elseVariable, constraint);
+ }
+ }
+
+ /**
+ * Creates a subtype constraint.
+ *
+ * @param descendant the descendant type constraint variable
+ * @param ancestor the ancestor type constraint variable
+ */
+ public final void createCovariantTypeConstraint(final ConstraintVariable2 descendant, final ConstraintVariable2 ancestor) {
+ final ITypeConstraint2 constraint= new CovariantTypeConstraint(descendant, ancestor);
+ if (!fTypeConstraints.contains(constraint)) {
+ fTypeConstraints.add(constraint);
+ fCovariantTypeConstraints.add(constraint);
+ setVariableUsage(descendant, constraint);
+ setVariableUsage(ancestor, constraint);
+ }
+ }
+
+ /**
+ * Creates a declaring type variable.
+ * <p>
+ * A declaring type variable stands for a type where something has been declared.
+ * </p>
+ *
+ * @param type the type binding
+ * @return the created declaring type variable
+ */
+ public final ConstraintVariable2 createDeclaringTypeVariable(ITypeBinding type) {
+ if (type.isArray())
+ type= type.getElementType();
+ type= type.getTypeDeclaration();
+ return fConstraintVariables.addExisting(new ImmutableTypeVariable2(createTType(type)));
+ }
+
+ /**
+ * Creates an equality constraint.
+ *
+ * @param left the left typeconstraint variable
+ * @param right the right typeconstraint variable
+ */
+ public final void createEqualityConstraint(final ConstraintVariable2 left, final ConstraintVariable2 right) {
+ if (left != null && right != null) {
+ final TypeEquivalenceSet first= left.getTypeEquivalenceSet();
+ final TypeEquivalenceSet second= right.getTypeEquivalenceSet();
+ if (first == null) {
+ if (second == null) {
+ final TypeEquivalenceSet set= new TypeEquivalenceSet(left, right);
+ left.setTypeEquivalenceSet(set);
+ right.setTypeEquivalenceSet(set);
+ } else {
+ second.add(left);
+ left.setTypeEquivalenceSet(second);
+ }
+ } else {
+ if (second == null) {
+ first.add(right);
+ right.setTypeEquivalenceSet(first);
+ } else if (first == second)
+ return;
+ else {
+ final ConstraintVariable2[] variables= second.getContributingVariables();
+ first.addAll(variables);
+ for (int index= 0; index < variables.length; index++)
+ variables[index].setTypeEquivalenceSet(first);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates an exception variable.
+ *
+ * @param name the name of the thrown exception
+ * @return the created exception variable
+ */
+ public final ConstraintVariable2 createExceptionVariable(final Name name) {
+ final ITypeBinding binding= name.resolveTypeBinding();
+ if (isConstrainedType(binding))
+ return fConstraintVariables.addExisting(new TypeVariable2(createTType(binding), new CompilationUnitRange(RefactoringASTParser.getCompilationUnit(name), name)));
+ return null;
+ }
+
+ /**
+ * Creates an immutable type variable.
+ *
+ * @param type the type binding
+ * @return the created plain type variable
+ */
+ public final ConstraintVariable2 createImmutableTypeVariable(ITypeBinding type) {
+ if (type.isArray())
+ type= type.getElementType();
+ if (isConstrainedType(type))
+ return fConstraintVariables.addExisting(new ImmutableTypeVariable2(createTType(type)));
+ return null;
+ }
+
+ /**
+ * Creates an independent type variable.
+ * <p>
+ * An independant type variable stands for an arbitrary type.
+ * </p>
+ *
+ * @param type the type binding
+ * @return the created independant type variable
+ */
+ public final ConstraintVariable2 createIndependentTypeVariable(ITypeBinding type) {
+ if (type.isArray())
+ type= type.getElementType();
+ if (isConstrainedType(type))
+ return fConstraintVariables.addExisting(new IndependentTypeVariable2(createTType(type)));
+ return null;
+ }
+
+ /**
+ * Creates a new method parameter variable.
+ *
+ * @param method the method binding
+ * @param index the index of the parameter
+ * @return the created method parameter variable
+ */
+ public final ConstraintVariable2 createMethodParameterVariable(final IMethodBinding method, final int index) {
+ final ITypeBinding[] parameters= method.getParameterTypes();
+ if (parameters.length < 1)
+ return null;
+ ITypeBinding binding= parameters[Math.min(index, parameters.length - 1)];
+ if (binding.isArray())
+ binding= binding.getElementType();
+ if (isConstrainedType(binding)) {
+ ConstraintVariable2 variable= null;
+ final TType type= createTType(binding);
+ if (method.getDeclaringClass().isFromSource())
+ variable= new ParameterTypeVariable2(type, index, method.getMethodDeclaration());
+ else
+ variable= new ImmutableTypeVariable2(type);
+ return fConstraintVariables.addExisting(variable);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new return type variable.
+ *
+ * @param method the method binding
+ * @return the created return type variable
+ */
+ public final ConstraintVariable2 createReturnTypeVariable(final IMethodBinding method) {
+ if (!method.isConstructor()) {
+ ITypeBinding binding= method.getReturnType();
+ if (binding != null && binding.isArray())
+ binding= binding.getElementType();
+ if (binding != null && isConstrainedType(binding)) {
+ ConstraintVariable2 variable= null;
+ final TType type= createTType(binding);
+ if (method.getDeclaringClass().isFromSource())
+ variable= new ReturnTypeVariable2(type, method);
+ else
+ variable= new ImmutableTypeVariable2(type);
+ return fConstraintVariables.addExisting(variable);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates a subtype constraint.
+ *
+ * @param descendant the descendant type constraint variable
+ * @param ancestor the ancestor type constraint variable
+ */
+ public final void createSubtypeConstraint(final ConstraintVariable2 descendant, final ConstraintVariable2 ancestor) {
+ final ITypeConstraint2 constraint= new SubTypeConstraint2(descendant, ancestor);
+ if (!fTypeConstraints.contains(constraint)) {
+ fTypeConstraints.add(constraint);
+ setVariableUsage(descendant, constraint);
+ setVariableUsage(ancestor, constraint);
+ }
+ }
+
+ /**
+ * Creates a new TType for the corresponding binding.
+ *
+ * @param binding The type binding
+ * @return The corresponding TType
+ */
+ public final TType createTType(final ITypeBinding binding) {
+ final String key= binding.getKey();
+ final TType cached= fTTypeCache.get(key);
+ if (cached != null)
+ return cached;
+ final TType type= fEnvironment.create(binding);
+ fTTypeCache.put(key, type);
+ return type;
+ }
+
+ /**
+ * Creates a type variable.
+ *
+ * @param type the type binding
+ * @param range the compilation unit range
+ * @return the created type variable
+ */
+ public final ConstraintVariable2 createTypeVariable(ITypeBinding type, final CompilationUnitRange range) {
+ if (type.isArray())
+ type= type.getElementType();
+ if (isConstrainedType(type))
+ return fConstraintVariables.addExisting(new TypeVariable2(createTType(type), range));
+ return null;
+ }
+
+ /**
+ * Creates a type variable.
+ *
+ * @param type the type
+ * @return the created type variable
+ */
+ public final ConstraintVariable2 createTypeVariable(final Type type) {
+ ITypeBinding binding= type.resolveBinding();
+ if (binding != null) {
+ if (binding.isArray())
+ binding= binding.getElementType();
+ if (isConstrainedType(binding))
+ return fConstraintVariables.addExisting(new TypeVariable2(createTType(binding), new CompilationUnitRange(RefactoringASTParser.getCompilationUnit(type), type)));
+ }
+ return null;
+ }
+
+ /**
+ * Creates a variable type variable.
+ *
+ * @param binding the variable binding
+ * @return the created variable variable
+ */
+ public final ConstraintVariable2 createVariableVariable(final IVariableBinding binding) {
+ ITypeBinding type= binding.getType();
+ if (type.isArray())
+ type= type.getElementType();
+ if (isConstrainedType(type)) {
+ ConstraintVariable2 variable= null;
+ final IVariableBinding declaration= binding.getVariableDeclaration();
+ if (declaration.isField()) {
+ final ITypeBinding declaring= declaration.getDeclaringClass();
+ if (!declaring.isFromSource())
+ variable= new ImmutableTypeVariable2(createTType(type));
+ } else {
+ final IMethodBinding declaring= declaration.getDeclaringMethod();
+ if (declaring != null && !declaring.getDeclaringClass().isFromSource())
+ variable= new ImmutableTypeVariable2(createTType(type));
+ }
+ if (variable == null)
+ variable= new VariableVariable2(createTType(type), declaration);
+ return fConstraintVariables.addExisting(variable);
+ }
+ return null;
+ }
+
+ /**
+ * Gets called when the creation of the model ends.
+ */
+ public final void endCreation() {
+ fEnvironment= null;
+ fTTypeCache= null;
+ }
+
+ /**
+ * Returns the cast variables of this model.
+ *
+ * @return the cast variables (element type: <code>CastVariable2</code>)
+ */
+ public final Collection<CastVariable2> getCastVariables() {
+ return Collections.unmodifiableCollection(fCastVariables);
+ }
+
+ /**
+ * Returns the compliance level to use.
+ *
+ * @return the compliance level
+ */
+ public final int getCompliance() {
+ return fCompliance;
+ }
+
+ /**
+ * Returns the constraint variables of this model.
+ *
+ * @return the constraint variables (element type: <code>ConstraintVariable2</code>)
+ */
+ public final Collection<ConstraintVariable2> getConstraintVariables() {
+ return Collections.unmodifiableCollection(fConstraintVariables);
+ }
+
+ /**
+ * Returns the subtype to be replaced.
+ *
+ * @return the subtype to be replaced
+ */
+ public final TType getSubType() {
+ return fSubType;
+ }
+
+ /**
+ * Returns the supertype as replacement.
+ *
+ * @return the supertype as replacement
+ */
+ public final TType getSuperType() {
+ return fSuperType;
+ }
+
+ /**
+ * Returns the type constraints of this model.
+ *
+ * @return the type constraints (element type: <code>ITypeConstraint2</code>)
+ */
+ public final Collection<ITypeConstraint2> getTypeConstraints() {
+ return Collections.unmodifiableCollection(fTypeConstraints);
+ }
+
+ /**
+ * Sets the compliance level to use.
+ *
+ * @param level the compliance level to use. The argument must be one of the <code>AST.JLSx</code> constants.
+ */
+ public final void setCompliance(final int level) {
+ fCompliance= level;
+ }
+}
\ No newline at end of file