blob: af921586923e65ae60ea44a53755c1c6c38f5152 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}