--- /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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+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.MethodDeclaration;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.UnionType;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.core.refactoring.descriptors.GeneralizeTypeDescriptor;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchPattern;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
+import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
+import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
+import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
+import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ASTCreator;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.CompositeOrTypeConstraint;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintCollector;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintOperator;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintVariable;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintVariableFactory;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ExpressionVariable;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.FullConstraintCreator;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ITypeConstraint;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ReturnTypeVariable;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.SimpleTypeConstraint;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.TypeConstraintFactory;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.util.Messages;
+import org.eclipse.jdt.internal.corext.util.SearchUtils;
+
+import org.eclipse.jdt.ui.JavaElementLabels;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
+import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
+
+/**
+ * @author tip
+ */
+public class ChangeTypeRefactoring extends Refactoring {
+
+ public static final String ATTRIBUTE_TYPE= "type"; //$NON-NLS-1$
+
+ public final Map<ICompilationUnit, List<ITypeConstraint>> fConstraintCache;
+ /**
+ * Offset of the selected text area.
+ */
+ public int fSelectionStart;
+
+ /**
+ * Length of the selected text area.
+ */
+ public int fSelectionLength;
+
+ /**
+ * Offset of the effective selection
+ */
+ private int fEffectiveSelectionStart;
+
+ /**
+ * Length of the effective selection
+ */
+ private int fEffectiveSelectionLength;
+
+ /**
+ * ICompilationUnit containing the selection.
+ */
+ public ICompilationUnit fCu;
+
+ /**
+ * If the selection corresponds to a method parameter/return type, this field stores
+ * a reference to its IMethodBinding, otherwise this field remains null. Used during
+ * search for references in other CUs, and for determining the ConstraintVariable
+ * that corresponds to the selection
+ */
+ public IMethodBinding fMethodBinding;
+
+ /**
+ * If the selection corresponds to a method parameter, this field stores the parameter
+ * index (0 = first parameter for static methods, 0 = this for nonstatic methods). The
+ * value -1 is stored in the field if the selection corresponds to a method return type.
+ */
+ public int fParamIndex;
+
+ /**
+ * The name of the selected parameter, or <code>null</code>.
+ */
+ private String fParamName;
+
+ /**
+ * If the selection corresponds to a field, this field stores a reference to its IVariableBinding,
+ * otherwise this field remains null. Used during search for references in other CUs.
+ */
+ public IVariableBinding fFieldBinding;
+
+ /**
+ * The compilation units that contain constraint variables related to the selection
+ */
+ private ICompilationUnit[] fAffectedUnits;
+
+ /**
+ * The constraint variables that are of interest to this refactoring. This includes
+ * the constraint var. corresponding to the text selection, and possibly additional
+ * elements due to method overriding, method calls, etc.
+ */
+ public Collection<ConstraintVariable> fRelevantVars;
+
+ /**
+ * The set of types (other than the original type) that can be given to
+ * the selected ASTNode.
+ */
+ private final Collection<ITypeBinding> fValidTypes;
+
+ /**
+ * The type constraints that are related to the selected ASTNode.
+ */
+ private Collection<ITypeConstraint> fRelevantConstraints;
+
+ /**
+ * All type constraints in affected compilation units.
+ */
+ public Collection<ITypeConstraint> fAllConstraints;
+
+ /**
+ * The name of the new type of the selected declaration.
+ */
+ public String fSelectedTypeName;
+
+ /**
+ * The new type of the selected declaration.
+ */
+ public ITypeBinding fSelectedType;
+
+ /**
+ * Organizes SearchResults by CompilationUnit
+ */
+ public Map<ICompilationUnit, SearchResultGroup> fCuToSearchResultGroup= new HashMap<ICompilationUnit, SearchResultGroup>();
+
+
+ /**
+ * ITypeBinding for java.lang.Object
+ */
+ private ITypeBinding fObject;
+
+ public ITypeBinding getObject(){
+ return fObject;
+ }
+
+ /**
+ * Control debugging output.
+ */
+ public static final boolean DEBUG= false;
+
+ public ConstraintVariable fCv;
+ public IBinding fSelectionBinding;
+ private ITypeBinding fSelectionTypeBinding;
+ public ConstraintCollector fCollector;
+
+ public ChangeTypeRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength) {
+ this(cu, selectionStart, selectionLength, null);
+ }
+
+ /**
+ * Constructor for ChangeTypeRefactoring (invoked from tests only)
+ * @param cu the compilation unit
+ * @param selectionStart selection offset
+ * @param selectionLength selection length
+ * @param selectedType selected type
+ */
+ public ChangeTypeRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength, String selectedType) {
+ Assert.isTrue(selectionStart >= 0);
+ Assert.isTrue(selectionLength >= 0);
+
+ fSelectionStart= selectionStart;
+ fSelectionLength= selectionLength;
+
+ fEffectiveSelectionStart= selectionStart;
+ fEffectiveSelectionLength= selectionLength;
+
+ fCu= cu;
+
+ if (selectedType != null)
+ fSelectedTypeName= selectedType;
+
+ fConstraintCache= new HashMap<ICompilationUnit, List<ITypeConstraint>>();
+ fValidTypes= new HashSet<ITypeBinding>();
+ }
+
+ public ChangeTypeRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
+ this(null, 0, 0, null);
+ RefactoringStatus initializeStatus= initialize(arguments);
+ status.merge(initializeStatus);
+ }
+
+ // ------------------------------------------------------------------------------------------------- //
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+ if (fCu == null || !fCu.isStructureKnown())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_invalidSelection);
+ return checkSelection(new SubProgressMonitor(pm, 1));
+ }
+
+ private void setSelectionRanges(Expression exp){
+ fEffectiveSelectionStart= exp.getStartPosition();
+ fEffectiveSelectionLength= exp.getLength();
+ fSelectionBinding= ExpressionVariable.resolveBinding(exp);
+ setOriginalType(exp.resolveTypeBinding());
+ }
+
+ /**
+ * Check if the right type of AST Node is selected. Create the TypeHierarchy needed to
+ * bring up the wizard.
+ * @param pm progress monitor
+ * @return returns the resulting status
+ */
+ private RefactoringStatus checkSelection(IProgressMonitor pm) {
+ try {
+ pm.beginTask("", 5); //$NON-NLS-1$
+
+ ASTNode node= getTargetNode(fCu, fSelectionStart, fSelectionLength);
+ if (DEBUG) {
+ System.out.println(
+ "selection: [" //$NON-NLS-1$
+ + fSelectionStart
+ + "," //$NON-NLS-1$
+ + (fSelectionStart + fSelectionLength)
+ + "] in " //$NON-NLS-1$
+ + fCu.getElementName());
+ System.out.println("node= " + node + ", type= " + node.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ TypeConstraintFactory typeConstraintFactory = new TypeConstraintFactory(){
+ @Override
+ public boolean filter(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator o){
+ if (o.isStrictSubtypeOperator()) //TODO: explain why these can be excluded
+ return true;
+ //Don't create constraint if fSelectionTypeBinding is not involved:
+ if (v1.getBinding() != null && v2.getBinding() != null
+ && ! Bindings.equals(v1.getBinding(), fSelectionTypeBinding)
+ && ! Bindings.equals(v2.getBinding(), fSelectionTypeBinding)) {
+ if (PRINT_STATS) fNrFiltered++;
+ return true;
+ }
+ return super.filter(v1, v2, o);
+ }
+ };
+ fCollector= new ConstraintCollector(new FullConstraintCreator(new ConstraintVariableFactory(), typeConstraintFactory));
+ String selectionValid= determineSelection(node);
+ if (selectionValid != null){
+ if (DEBUG){
+ System.out.println("invalid selection: " + selectionValid); //$NON-NLS-1$
+ }
+ return RefactoringStatus.createFatalErrorStatus(selectionValid);
+ }
+
+ if (fMethodBinding != null) {
+ IMethod selectedMethod= (IMethod) fMethodBinding.getJavaElement();
+ if (selectedMethod == null){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_insideLocalTypesNotSupported);
+ }
+ }
+
+ pm.worked(1);
+
+ RefactoringStatus result= new RefactoringStatus();
+
+ if (DEBUG){
+ System.out.println("fSelectionTypeBinding: " + fSelectionTypeBinding.getName()); //$NON-NLS-1$
+ }
+
+ // produce error message if array or primitive type is selected
+ if (fSelectionTypeBinding.isArray()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported);
+ }
+ if (fSelectionTypeBinding.isPrimitive()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_primitivesNotSupported);
+ }
+ if (checkOverriddenBinaryMethods())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnBinary);
+
+ if (fSelectionTypeBinding.isLocal()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_localTypesNotSupported);
+ }
+
+ if (fFieldBinding != null && fFieldBinding.getDeclaringClass().isLocal()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_insideLocalTypesNotSupported);
+ }
+
+ if (fSelectionTypeBinding.isTypeVariable()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_typeParametersNotSupported);
+ }
+
+ if (fSelectionTypeBinding.isEnum()){
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_enumsNotSupported);
+ }
+
+ pm.worked(1);
+
+ if (fSelectedType != null){ // if invoked from unit test, compute valid types here
+ computeValidTypes(new NullProgressMonitor());
+ }
+ return result;
+ } finally {
+ pm.done();
+ }
+ }
+
+ private boolean checkOverriddenBinaryMethods() {
+ if (fMethodBinding != null){
+ Set<ITypeBinding> declaringSupertypes= getDeclaringSuperTypes(fMethodBinding);
+ for (Iterator<ITypeBinding> iter= declaringSupertypes.iterator(); iter.hasNext();) {
+ ITypeBinding superType= iter.next();
+ IMethodBinding overriddenMethod= findMethod(fMethodBinding, superType);
+ Assert.isNotNull(overriddenMethod);//because we asked for declaring types
+ IMethod iMethod= (IMethod) overriddenMethod.getJavaElement();
+ if (iMethod.isBinary()){
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // copied from FullConstraintCreator
+ private static IMethodBinding findMethod(IMethodBinding methodBinding, ITypeBinding type) {
+ if (methodBinding.getDeclaringClass().equals(type))
+ return methodBinding;
+ return Bindings.findOverriddenMethodInType(type, methodBinding);
+ }
+
+ // copied from FullConstraintCreator
+ private static Set<ITypeBinding> getDeclaringSuperTypes(IMethodBinding methodBinding) {
+ ITypeBinding[] allSuperTypes= Bindings.getAllSuperTypes(methodBinding.getDeclaringClass());
+ Set<ITypeBinding> result= new HashSet<ITypeBinding>();
+ for (int i= 0; i < allSuperTypes.length; i++) {
+ ITypeBinding type= allSuperTypes[i];
+ if (findMethod(methodBinding, type) != null)
+ result.add(type);
+ }
+ return result;
+ }
+
+ /**
+ * Do the actual work of computing allowable types. Invoked by the wizard when
+ * "compute" button is pressed
+ * @param pm the progress monitor
+ * @return the valid types
+ */
+ public Collection<ITypeBinding> computeValidTypes(IProgressMonitor pm) {
+
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_checking_preconditions, 100);
+
+ try {
+ fCv= findConstraintVariableForSelectedNode(new SubProgressMonitor(pm, 3));
+ fCv.generated_4691190627321795876(this, pm);
+
+ if (DEBUG)
+ printCollection("relevant vars:", fRelevantVars); //$NON-NLS-1$
+
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ fRelevantConstraints= findRelevantConstraints(fRelevantVars, new SubProgressMonitor(pm, 30));
+
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ fValidTypes.addAll(computeValidTypes(fSelectionTypeBinding, fRelevantVars,
+ fRelevantConstraints, new SubProgressMonitor(pm, 20)));
+
+ if (DEBUG)
+ printCollection("valid types:", getValidTypeNames()); //$NON-NLS-1$
+ } catch (CoreException e) {
+ JavaPlugin.logErrorMessage("Error occurred during computation of valid types: " + e.toString()); //$NON-NLS-1$
+ fValidTypes.clear(); // error occurred during computation of valid types
+ }
+
+ pm.done();
+
+ return fValidTypes;
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_checking_preconditions, 1);
+
+ RefactoringStatus result= Checks.validateModifiesFiles(
+ ResourceUtil.getFiles(fAffectedUnits), getValidationContext());
+
+ pm.done();
+ return result;
+ }
+// TODO: do sanity check somewhere if the refactoring changes any files.
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeMessages_CreateChangesForChangeType, 1);
+ try {
+ Map<ICompilationUnit, Set<ConstraintVariable>> relevantVarsByUnit= new HashMap<ICompilationUnit, Set<ConstraintVariable>>();
+ groupChangesByCompilationUnit(relevantVarsByUnit);
+ final Map<String, String> arguments= new HashMap<String, String>();
+ String project= null;
+ IJavaProject javaProject= fCu.getJavaProject();
+ if (javaProject != null)
+ project= javaProject.getElementName();
+ final String description= RefactoringCoreMessages.ChangeTypeRefactoring_descriptor_description_short;
+ final String header= Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_descriptor_description, new String[] { BindingLabelProvider.getBindingLabel(fSelectionBinding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(fSelectedType, JavaElementLabels.ALL_FULLY_QUALIFIED)});
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ final GeneralizeTypeDescriptor descriptor= comment.generated_1165472357133297433(arguments, project, description, this);
+ final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.ChangeTypeRefactoring_allChanges);
+ for (Iterator<ICompilationUnit>it= relevantVarsByUnit.keySet().iterator(); it.hasNext();) {
+ ICompilationUnit icu= it.next();
+ Set<ConstraintVariable> cVars= relevantVarsByUnit.get(icu);
+ CompilationUnitChange cuChange= new CompilationUnitChange(getName(), icu);
+ addAllChangesFor(icu, cVars, cuChange);
+ result.add(cuChange);
+ pm.worked(1);
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ }
+ return result;
+ } finally {
+ pm.done();
+ }
+ }
+
+ /**
+ * Apply all changes related to a single ICompilationUnit
+ * @param icu the compilation unit
+ * @param vars
+ * @param unitChange
+ * @throws CoreException
+ */
+ private void addAllChangesFor(ICompilationUnit icu, Set<ConstraintVariable> vars, CompilationUnitChange unitChange) throws CoreException {
+ CompilationUnit unit= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(icu, false);
+ ASTRewrite unitRewriter= ASTRewrite.create(unit.getAST());
+ MultiTextEdit root= new MultiTextEdit();
+ unitChange.setEdit(root); // Adam sez don't need this, but then unitChange.addGroupDescription() fails an assertion!
+
+ String typeName= updateImports(unit, root);
+ updateCu(unit, vars, unitChange, unitRewriter, typeName);
+ root.addChild(unitRewriter.rewriteAST());
+ }
+
+ private class SourceRangeComputer extends TargetSourceRangeComputer {
+ @Override
+ public SourceRange computeSourceRange(ASTNode node) {
+ return new SourceRange(node.getStartPosition(),node.getLength());
+ }
+ }
+
+ private void updateCu(CompilationUnit unit, Set<ConstraintVariable> vars, CompilationUnitChange unitChange,
+ ASTRewrite unitRewriter, String typeName) throws JavaModelException {
+
+ // use custom SourceRangeComputer to avoid losing comments
+ unitRewriter.setTargetSourceRangeComputer(new SourceRangeComputer());
+
+ for (Iterator<ConstraintVariable> it=vars.iterator(); it.hasNext(); ){
+ ConstraintVariable cv = it.next();
+ cv.generated_4433017347881954111(unit, unitChange, unitRewriter, typeName, this);
+ }
+ }
+
+ public void updateType(CompilationUnit cu, Type oldType, CompilationUnitChange unitChange,
+ ASTRewrite unitRewriter, String typeName) {
+
+ String oldName= fSelectionTypeBinding.getName();
+ String[] keys= { BasicElementLabels.getJavaElementName(oldName), BasicElementLabels.getJavaElementName(typeName)};
+ String description= Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_typeChange, keys);
+ TextEditGroup gd= new TextEditGroup(description);
+ AST ast= cu.getAST();
+
+ ASTNode nodeToReplace= oldType;
+ if (fSelectionTypeBinding.isParameterizedType() && !fSelectionTypeBinding.isRawType()){
+ if (oldType.isSimpleType()){
+ nodeToReplace= oldType.getParent();
+ }
+ }
+
+ //TODO handle types other than simple & parameterized (e.g., arrays)
+ Assert.isTrue(fSelectedType.isClass() || fSelectedType.isInterface());
+
+ Type newType= null;
+ if (!fSelectedType.isParameterizedType()){
+ newType= ast.newSimpleType(ASTNodeFactory.newName(ast, typeName));
+ } else {
+ newType= createParameterizedType(ast, fSelectedType);
+ }
+
+ unitRewriter.replace(nodeToReplace, newType, gd);
+ unitChange.addTextEditGroup(gd);
+ }
+
+ /**
+ * Creates the appropriate ParameterizedType node. Recursion is needed to
+ * handle the nested case (e.g., Vector<Vector<String>>).
+ * @param ast
+ * @param typeBinding
+ * @return the created type
+ */
+ private Type createParameterizedType(AST ast, ITypeBinding typeBinding){
+ if (typeBinding.isParameterizedType() && !typeBinding.isRawType()){
+ Type baseType= ast.newSimpleType(ASTNodeFactory.newName(ast, typeBinding.getErasure().getName()));
+ ParameterizedType newType= ast.newParameterizedType(baseType);
+ for (int i=0; i < typeBinding.getTypeArguments().length; i++){
+ ITypeBinding typeArg= typeBinding.getTypeArguments()[i];
+ Type argType= createParameterizedType(ast, typeArg); // recursive call
+ newType.typeArguments().add(argType);
+ }
+ return newType;
+ } else {
+ if (!typeBinding.isTypeVariable()){
+ return ast.newSimpleType(ASTNodeFactory.newName(ast, typeBinding.getErasure().getName()));
+ } else {
+ return ast.newSimpleType(ast.newSimpleName(typeBinding.getName()));
+ }
+ }
+ }
+
+
+
+ private void groupChangesByCompilationUnit(Map<ICompilationUnit, Set<ConstraintVariable>> relevantVarsByUnit) {
+ for (Iterator<ConstraintVariable> it= fRelevantVars.iterator(); it.hasNext();) {
+ ConstraintVariable cv= it.next();
+ if (!(cv instanceof ExpressionVariable) && !(cv instanceof ReturnTypeVariable)){
+ continue;
+ }
+ ICompilationUnit icu= cv.generated_5479417507488142983(relevantVarsByUnit);
+ }
+ }
+
+ public ASTNode findDeclaration(CompilationUnit root, ConstraintVariable cv) throws JavaModelException {
+
+ return cv.generated_3373575336371797778(root, this);
+ }
+
+ public static Type getType(ASTNode node) {
+ switch(node.getNodeType()){
+ case ASTNode.SINGLE_VARIABLE_DECLARATION:
+ return ((SingleVariableDeclaration) node).getType();
+ case ASTNode.FIELD_DECLARATION:
+ return ((FieldDeclaration) node).getType();
+ case ASTNode.VARIABLE_DECLARATION_STATEMENT:
+ return ((VariableDeclarationStatement) node).getType();
+ case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
+ return ((VariableDeclarationExpression) node).getType();
+ case ASTNode.METHOD_DECLARATION:
+ return ((MethodDeclaration)node).getReturnType2();
+ case ASTNode.PARAMETERIZED_TYPE:
+ return ((ParameterizedType)node).getType();
+ default:
+ Assert.isTrue(false);
+ return null;
+ }
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName()
+ */
+ @Override
+ public String getName() {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_name;
+ }
+
+ // ------------------------------------------------------------------------------------------------- //
+ // Method for examining if a suitable kind of ASTNode was selected. Information about this node and
+ // its parents in the AST are stored in fields fBinding, theMethod, and theField
+
+ /**
+ * Determines what kind of ASTNode has been selected.
+ * @param node the node
+ * @return A non-null String containing an error message
+ * is returned if the ChangeTypeRefactoring refactoring cannot be applied to the selected ASTNode.
+ * A return value of null indicates a valid selection.
+ */
+ private String determineSelection(ASTNode node) {
+ if (node == null) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_invalidSelection;
+ } else {
+
+ if (DEBUG) System.out.println("node nodeType= " + node.getClass().getName()); //$NON-NLS-1$
+ if (DEBUG) System.out.println("parent nodeType= " + node.getParent().getClass().getName()); //$NON-NLS-1$
+ if (DEBUG) System.out.println("GrandParent nodeType= " + node.getParent().getParent().getClass().getName()); //$NON-NLS-1$
+
+ ASTNode parent= node.getParent();
+ ASTNode grandParent= parent.getParent();
+ if (grandParent == null)
+ return nodeTypeNotSupported();
+
+ // adjustment needed if part of a parameterized type is selected
+ if (grandParent.getNodeType() == ASTNode.PARAMETERIZED_TYPE){
+ node= grandParent;
+ }
+
+ // adjustment needed if part of a qualified name is selected
+ ASTNode current= null;
+ if (node.getNodeType() == ASTNode.QUALIFIED_NAME){
+ current= node;
+ while (current.getNodeType() == ASTNode.QUALIFIED_NAME){
+ current= current.getParent();
+ }
+ if (current.getNodeType() != ASTNode.SIMPLE_TYPE){
+ return nodeTypeNotSupported();
+ }
+ node= current.getParent();
+ } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME){
+ current= parent;
+ while (current.getNodeType() == ASTNode.QUALIFIED_NAME){
+ current= current.getParent();
+ }
+ if (current.getNodeType() != ASTNode.SIMPLE_TYPE){
+ return nodeTypeNotSupported();
+ }
+ node= current.getParent();
+ }
+
+ fObject= node.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
+ switch (node.getNodeType()) {
+ case ASTNode.SIMPLE_NAME :
+ return simpleNameSelected((SimpleName)node);
+ case ASTNode.VARIABLE_DECLARATION_STATEMENT :
+ return variableDeclarationStatementSelected((VariableDeclarationStatement) node);
+ case ASTNode.FIELD_DECLARATION :
+ return fieldDeclarationSelected((FieldDeclaration) node);
+ case ASTNode.SINGLE_VARIABLE_DECLARATION :
+ return singleVariableDeclarationSelected((SingleVariableDeclaration) node);
+ case ASTNode.PARAMETERIZED_TYPE:
+ return parameterizedTypeSelected((ParameterizedType) node);
+ default :
+ return nodeTypeNotSupported();
+ }
+ }
+ }
+ /**
+ * The selection corresponds to an ASTNode on which "ChangeTypeRefactoring" is not defined.
+ * @return the message
+ */
+ private static String nodeTypeNotSupported() {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
+ }
+
+ /**
+ * The selection corresponds to a SingleVariableDeclaration
+ * @param svd
+ * @return the message
+ */
+ private String singleVariableDeclarationSelected(SingleVariableDeclaration svd) {
+ SimpleName name = svd.getName();
+ setSelectionRanges(name);
+ return simpleNameSelected(name);
+ }
+
+ /**
+ * The selection corresponds to a ParameterizedType (return type of method)
+ * @param pt the type
+ * @return the message
+ */
+ private String parameterizedTypeSelected(ParameterizedType pt) {
+ ASTNode parent= pt.getParent();
+ if (parent.getNodeType() == ASTNode.METHOD_DECLARATION){
+ fMethodBinding= ((MethodDeclaration)parent).resolveBinding();
+ fParamIndex= -1;
+ fEffectiveSelectionStart= pt.getStartPosition();
+ fEffectiveSelectionLength= pt.getLength();
+ setOriginalType(pt.resolveBinding());
+ } else if (parent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION){
+ return singleVariableDeclarationSelected((SingleVariableDeclaration)parent);
+ } else if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
+ return variableDeclarationStatementSelected((VariableDeclarationStatement)parent);
+ } else if (parent.getNodeType() == ASTNode.FIELD_DECLARATION){
+ return fieldDeclarationSelected((FieldDeclaration)parent);
+ } else {
+ return nodeTypeNotSupported();
+ }
+ return null;
+ }
+
+ /**
+ * The selection corresponds to a VariableDeclarationStatement
+ * @param vds the name
+ * @return the message
+ */
+ private String variableDeclarationStatementSelected(VariableDeclarationStatement vds) {
+ if (vds.fragments().size() != 1) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ } else {
+ VariableDeclarationFragment elem= (VariableDeclarationFragment) vds.fragments().iterator().next();
+ SimpleName name= elem.getName();
+ setSelectionRanges(name);
+ return simpleNameSelected(name);
+ }
+ }
+
+ /**
+ * The selection corresponds to a FieldDeclaration
+ * @param fieldDeclaration the field
+ * @return the message
+ */
+ private String fieldDeclarationSelected(FieldDeclaration fieldDeclaration) {
+ if (fieldDeclaration.fragments().size() != 1) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ } else {
+ VariableDeclarationFragment elem= (VariableDeclarationFragment) fieldDeclaration.fragments().iterator().next();
+ fFieldBinding= elem.resolveBinding();
+ SimpleName name= elem.getName();
+ setSelectionRanges(name);
+ return simpleNameSelected(name);
+ }
+ }
+
+ /**
+ * The selection corresponds to a SimpleName
+ * @param simpleName the name
+ * @return the message
+ */
+ private String simpleNameSelected(SimpleName simpleName) {
+ ASTNode parent= simpleName.getParent();
+ ASTNode grandParent= parent.getParent();
+
+ if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
+ VariableDeclarationStatement vds= (VariableDeclarationStatement)parent;
+ if (vds.fragments().size() > 1){
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ }
+ } else if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ if (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
+ VariableDeclarationStatement vds= (VariableDeclarationStatement)grandParent;
+ if (vds.fragments().size() > 1) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ }
+ setSelectionRanges(simpleName);
+ } else if (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
+ VariableDeclarationExpression vde= (VariableDeclarationExpression)grandParent;
+ if (vde.fragments().size() > 1) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ }
+ setSelectionRanges(simpleName);
+ } else if (grandParent.getNodeType() == ASTNode.FIELD_DECLARATION) {
+ FieldDeclaration fd= (FieldDeclaration)grandParent;
+ if (fd.fragments().size() > 1){
+ return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
+ }
+ VariableDeclarationFragment fragment = (VariableDeclarationFragment)parent;
+ fFieldBinding= fragment.resolveBinding();
+ setSelectionRanges(fragment.getName());
+ } else {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
+ }
+ } else if (parent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION) {
+ SingleVariableDeclaration singleVariableDeclaration= (SingleVariableDeclaration) parent;
+ if (singleVariableDeclaration.getType() instanceof UnionType) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_uniontypeNotSupported;
+ }
+ if ((grandParent.getNodeType() == ASTNode.METHOD_DECLARATION)) {
+ fMethodBinding= ((MethodDeclaration)grandParent).resolveBinding();
+ setOriginalType(simpleName.resolveTypeBinding());
+ fParamIndex= ((MethodDeclaration)grandParent).parameters().indexOf(parent);
+ fParamName= singleVariableDeclaration.getName().getIdentifier();
+ } else {
+ setSelectionRanges(singleVariableDeclaration.getName());
+ }
+ } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE && (grandParent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION)) {
+ ASTNode greatGrandParent= grandParent.getParent();
+ SingleVariableDeclaration singleVariableDeclaration= (SingleVariableDeclaration) grandParent;
+ if (singleVariableDeclaration.getExtraDimensions() > 0 || singleVariableDeclaration.isVarargs()) {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported;
+ }
+ if (greatGrandParent != null && greatGrandParent.getNodeType() == ASTNode.METHOD_DECLARATION) {
+ fMethodBinding= ((MethodDeclaration)greatGrandParent).resolveBinding();
+ fParamIndex= ((MethodDeclaration)greatGrandParent).parameters().indexOf(grandParent);
+ fParamName= singleVariableDeclaration.getName().getIdentifier();
+ setSelectionRanges(simpleName);
+ } else {
+ setSelectionRanges(singleVariableDeclaration.getName());
+ }
+ } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE && grandParent.getNodeType() == ASTNode.METHOD_DECLARATION) {
+ fMethodBinding= ((MethodDeclaration)grandParent).resolveBinding();
+ setOriginalType(fMethodBinding.getReturnType());
+ fParamIndex= -1;
+ } else if (parent.getNodeType() == ASTNode.METHOD_DECLARATION &&
+ grandParent.getNodeType() == ASTNode.TYPE_DECLARATION) {
+ MethodDeclaration methodDeclaration= (MethodDeclaration)parent;
+ if (methodDeclaration.getName().equals(simpleName) || methodDeclaration.thrownExceptions().contains(simpleName)){
+ return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
+ }
+ fMethodBinding= ((MethodDeclaration)parent).resolveBinding();
+ fParamIndex= -1;
+ } else if (
+ parent.getNodeType() == ASTNode.SIMPLE_TYPE && (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT)) {
+ return variableDeclarationStatementSelected((VariableDeclarationStatement) grandParent);
+ } else if (parent.getNodeType() == ASTNode.CAST_EXPRESSION) {
+ ASTNode decl= findDeclaration(parent.getRoot(), fSelectionStart, fSelectionLength+1);
+ VariableDeclarationFragment fragment= (VariableDeclarationFragment)decl;
+ SimpleName name = fragment.getName();
+ setSelectionRanges(name);
+ } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE &&
+ grandParent.getNodeType() == ASTNode.FIELD_DECLARATION) {
+ return fieldDeclarationSelected((FieldDeclaration) grandParent);
+ } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE &&
+ grandParent.getNodeType() == ASTNode.ARRAY_TYPE){
+ return RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported;
+ } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME){
+ setSelectionRanges(simpleName);
+ } else {
+ return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
+ }
+ return null;
+ }
+
+ // ------------------------------------------------------------------------------------------------- //
+ // Methods for examining & solving type constraints. This includes:
+ // (1) locating the ConstraintVariable corresponding to the selected ASTNode
+ // (2) finding all ConstraintVariables "related" to (1) via overriding, method calls, field access
+ // (3) find all ITypeConstraints of interest that mention ConstraintVariables in (2)
+ // (4) determining all ITypes for which the ITypeConstraints in (3) are satisfied
+
+ /**
+ * Find a ConstraintVariable that corresponds to the selected ASTNode.
+ * @param pm
+ * @return the ConstraintVariable
+ */
+ private ConstraintVariable findConstraintVariableForSelectedNode(IProgressMonitor pm) {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 100);
+ ICompilationUnit[] cus= { fCu }; // only search in CU containing selection
+
+ if (DEBUG){
+ System.out.println("Effective selection: " + fEffectiveSelectionStart + "/" + fEffectiveSelectionLength); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ Collection<ITypeConstraint> allConstraints= getConstraints(cus, new SubProgressMonitor(pm, 50));
+
+ IProgressMonitor subMonitor= new SubProgressMonitor(pm, 50);
+ subMonitor.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, allConstraints.size());
+ for (Iterator<ITypeConstraint> it= allConstraints.iterator(); it.hasNext(); ) {
+ subMonitor.worked(1);
+ ITypeConstraint tc= it.next();
+ if (! (tc instanceof SimpleTypeConstraint))
+ continue;
+ SimpleTypeConstraint stc= (SimpleTypeConstraint) tc;
+ if (matchesSelection(stc.getLeft()))
+ return stc.getLeft();
+ if (matchesSelection(stc.getRight()))
+ return stc.getRight();
+ }
+ subMonitor.done();
+ pm.done();
+ Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_noMatchingConstraintVariable);
+ return null;
+ }
+
+ /**
+ * Determine if a given ConstraintVariable matches the selected ASTNode.
+ * @param cv the ConstraintVariable
+ * @return <code>true</code> if the given ConstraintVariable matches the selected ASTNode
+ */
+ private boolean matchesSelection(ConstraintVariable cv){
+ return cv.generated_131493753158714679(this);
+ }
+
+ /**
+ * Determine the set of constraint variables related to the selected
+ * expression. In addition to the expression itself, this consists of
+ * any expression that is defines-equal to it, and any expression equal
+ * to it.
+ * @param cv
+ * @param pm
+ * @return the constraint variables
+ * @throws CoreException
+ */
+ public Collection<ConstraintVariable> findRelevantConstraintVars(ConstraintVariable cv, IProgressMonitor pm) throws CoreException {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 150);
+ Collection<ConstraintVariable> result= new HashSet<ConstraintVariable>();
+ result.add(cv);
+ ICompilationUnit[] cus= collectAffectedUnits(new SubProgressMonitor(pm, 50));
+ Collection<ITypeConstraint> allConstraints= getConstraints(cus, new SubProgressMonitor(pm, 50));
+
+ List<ConstraintVariable> workList= new ArrayList<ConstraintVariable>(result);
+ while(! workList.isEmpty()){
+
+ pm.worked(10);
+
+ ConstraintVariable first= workList.remove(0);
+ for (Iterator<ITypeConstraint> iter= allConstraints.iterator(); iter.hasNext();) {
+ pm.worked(1);
+ ITypeConstraint typeConstraint= iter.next();
+ if (! typeConstraint.isSimpleTypeConstraint())
+ continue;
+ SimpleTypeConstraint stc= (SimpleTypeConstraint)typeConstraint;
+ if (! stc.isDefinesConstraint() && ! stc.isEqualsConstraint())
+ continue;
+ ConstraintVariable match= match(first, stc.getLeft(), stc.getRight());
+ match.generated_2347781020236993056(result, workList);
+ }
+ }
+
+ pm.done();
+
+ return result;
+ }
+
+ private static ConstraintVariable match(ConstraintVariable matchee, ConstraintVariable left, ConstraintVariable right) {
+ if (matchee.equals(left))
+ return right;
+ if (matchee.equals(right))
+ return left;
+ return null;
+ }
+
+ /**
+ * Select the type constraints that involve the selected ASTNode.
+ * @param relevantConstraintVars
+ * @param pm
+ * @return the result
+ * @throws CoreException
+ */
+ private Collection<ITypeConstraint> findRelevantConstraints(Collection<ConstraintVariable> relevantConstraintVars,
+ IProgressMonitor pm) throws CoreException {
+
+ ICompilationUnit[] cus= collectAffectedUnits(new SubProgressMonitor(pm, 100));
+
+ fAllConstraints= getConstraints(cus, new SubProgressMonitor(pm, 900));
+
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 1000 + fAllConstraints.size());
+
+
+ if (DEBUG) printCollection("type constraints: ", fAllConstraints); //$NON-NLS-1$
+ Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
+ for (Iterator<ITypeConstraint> it= fAllConstraints.iterator(); it.hasNext(); ) {
+ ITypeConstraint tc= it.next();
+ if (tc.isSimpleTypeConstraint()) {
+ SimpleTypeConstraint stc= (SimpleTypeConstraint) tc;
+ if (stc.isDefinesConstraint() || stc.isEqualsConstraint())
+ continue;
+ if (stc.getLeft().equals(stc.getRight()))
+ continue;
+ if (isNull(stc.getLeft()))
+ continue;
+ stc.generated_7576130079388084015(relevantConstraintVars, result, tc);
+ } else {
+ CompositeOrTypeConstraint cotc= (CompositeOrTypeConstraint) tc;
+ ITypeConstraint[] components= cotc.getConstraints();
+ for (int i= 0; i < components.length; i++) {
+ ITypeConstraint component= components[i];
+ SimpleTypeConstraint simpleComponent= (SimpleTypeConstraint) component;
+ if (relevantConstraintVars.contains(simpleComponent.getLeft()))
+ result.add(tc);
+ }
+ }
+ pm.worked(1);
+ }
+ if (DEBUG)
+ printCollection("selected constraints: ", result); //$NON-NLS-1$
+ pm.done();
+ return result;
+ }
+
+ /**
+ * Finds the declaration of the ASTNode in a given AST at a specified offset and with a specified length
+ * @param root the AST
+ * @param start start
+ * @param length length
+ * @return the declaring node
+ */
+ private static ASTNode findDeclaration(final ASTNode root, final int start, final int length){
+ ASTNode node= NodeFinder.perform(root, start, length);
+ Assert.isTrue(node instanceof SimpleName, String.valueOf(node.getNodeType()));
+ Assert.isTrue(root instanceof CompilationUnit, String.valueOf(root.getNodeType()));
+ return ((CompilationUnit)root).findDeclaringNode(((SimpleName)node).resolveBinding());
+ }
+
+ // For debugging
+ static String print(Collection<ITypeBinding> types){
+ if (types.isEmpty())
+ return "{ }"; //$NON-NLS-1$
+ String result = "{ "; //$NON-NLS-1$
+ for (Iterator<ITypeBinding> it=types.iterator(); it.hasNext(); ){
+ ITypeBinding type= it.next();
+ result += type.getQualifiedName();
+ if (it.hasNext()){
+ result += ", "; //$NON-NLS-1$
+ } else {
+ result += " }"; //$NON-NLS-1$
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Determines the set of types for which a set of type constraints is satisfied.
+ * @param originalType
+ * @param relevantVars
+ * @param relevantConstraints
+ * @param pm
+ * @return the valid types
+ * @throws JavaModelException
+ */
+ private Collection<ITypeBinding> computeValidTypes(ITypeBinding originalType,
+ Collection<ConstraintVariable> relevantVars,
+ Collection<ITypeConstraint> relevantConstraints,
+ IProgressMonitor pm) throws JavaModelException {
+
+ Collection<ITypeBinding> result= new HashSet<ITypeBinding>();
+
+ Collection<ITypeBinding> allTypes = new HashSet<ITypeBinding>();
+ allTypes.addAll(getAllSuperTypes(originalType));
+
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, allTypes.size());
+
+ for (Iterator<ITypeBinding> it= allTypes.iterator(); it.hasNext(); ) {
+ ITypeBinding type= it.next();
+ if (isValid(type, relevantVars, relevantConstraints, new SubProgressMonitor(pm, 1))) {
+ result.add(type);
+ }
+ }
+ // "changing" to the original type is a no-op
+ result.remove(originalType);
+
+ // TODO: remove all types that are not visible --- need to check visibility in the CUs for
+ // all relevant constraint variables
+
+ pm.done();
+
+ return result;
+ }
+
+ /**
+ * Determines if a given type satisfies a set of type constraints.
+ * @param type
+ * @param relevantVars
+ * @param constraints
+ * @param pm
+ * @return <code>true</code> if a the type satisfies a set of type constraints.
+ * @throws JavaModelException
+ */
+ private boolean isValid(ITypeBinding type,
+ Collection<ConstraintVariable> relevantVars,
+ Collection<ITypeConstraint> constraints,
+ IProgressMonitor pm) throws JavaModelException {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, constraints.size());
+ for (Iterator<ITypeConstraint> it= constraints.iterator(); it.hasNext(); ) {
+ ITypeConstraint tc= it.next();
+ if (tc instanceof SimpleTypeConstraint) {
+ if (!(isValidSimpleConstraint(type, relevantVars, (SimpleTypeConstraint) tc)))
+ return false;
+ } else if (tc instanceof CompositeOrTypeConstraint) {
+ if (!(isValidOrConstraint(type, relevantVars, (CompositeOrTypeConstraint) tc)))
+ return false;
+ }
+ pm.worked(1);
+ }
+ pm.done();
+ return true;
+ }
+
+ private boolean isValidSimpleConstraint(ITypeBinding type,
+ Collection<ConstraintVariable> relevantVars,
+ SimpleTypeConstraint stc){
+ return stc.generated_4288874648754587294(type, relevantVars, this);
+ }
+
+ private boolean isValidOrConstraint(ITypeBinding type,
+ Collection<ConstraintVariable> relevantVars,
+ CompositeOrTypeConstraint cotc){
+ ITypeConstraint[] components= cotc.getConstraints();
+ for (int i= 0; i < components.length; i++) {
+ if (components[i] instanceof SimpleTypeConstraint) {
+ SimpleTypeConstraint sc= (SimpleTypeConstraint) components[i];
+ if (relevantVars.contains(sc.getLeft())) { // upper bound
+ if (isSubTypeOf(type, findType(sc.getRight())))
+ return true;
+ } else if (relevantVars.contains(sc.getRight())) { // lower bound
+ if (isSubTypeOf(findType(sc.getLeft()), type))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ public ITypeBinding findType(ConstraintVariable cv) {
+ return cv.getBinding();
+ }
+
+ /**
+ * Gather constraints associated with a set of compilation units.
+ * @param referringCus
+ * @param pm
+ * @return the constraints
+ */
+ private Collection<ITypeConstraint> getConstraints(ICompilationUnit[] referringCus, IProgressMonitor pm) {
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, referringCus.length);
+ Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
+ for (int i= 0; i < referringCus.length; i++) {
+ result.addAll(getConstraints(referringCus[i]));
+ pm.worked(1);
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ }
+ pm.done();
+ return result;
+ }
+
+ private List<ITypeConstraint> getConstraints(ICompilationUnit unit) {
+ if (fConstraintCache.containsKey(unit))
+ return fConstraintCache.get(unit);
+
+ CompilationUnit cu= ASTCreator.createAST(unit, null);
+
+ // only generate type constraints for relevant MethodDeclaration subtrees
+ List<ITypeConstraint> constraints= fCollector.generated_1070897917906180055(this, unit, cu);
+ return constraints;
+ }
+
+ /**
+ * update a CompilationUnit's imports after changing the type of declarations
+ * @param astRoot the AST
+ * @param rootEdit the resulting edit
+ * @return the type name to use
+ * @throws CoreException
+ */
+ private String updateImports(CompilationUnit astRoot, MultiTextEdit rootEdit) throws CoreException{
+ ImportRewrite rewrite= StubUtility.createImportRewrite(astRoot, true);
+ String typeName= rewrite.addImport(fSelectedType.getQualifiedName());
+ rootEdit.addChild(rewrite.rewriteImports(null));
+ return typeName;
+ }
+
+ // ------------------------------------------------------------------------------------------------- //
+ // Miscellaneous helper methods
+
+ /**
+ * Returns the Collection of types that can be given to the selected declaration.
+ * @return return the valid type bindings
+ */
+ public Collection<ITypeBinding> getValidTypes() {
+ return fValidTypes;
+ }
+
+ public ITypeBinding getOriginalType(){
+ return fSelectionTypeBinding;
+ }
+
+ private void setOriginalType(ITypeBinding originalType){
+ fSelectionTypeBinding= originalType;
+ fSelectedType= findSuperTypeByName(originalType, fSelectedTypeName);
+ }
+
+ public String getTarget() {
+ String typeName= fSelectionTypeBinding == null ? "" : fSelectionTypeBinding.getName() + " "; //$NON-NLS-1$//$NON-NLS-2$
+ if (fFieldBinding != null) {
+ return typeName + fFieldBinding.getName();
+ } else if (fMethodBinding != null) {
+ if (fParamIndex == -1) {
+ return typeName + fMethodBinding.getName() + "(...)"; //$NON-NLS-1$
+ } else {
+ return typeName + fParamName;
+ }
+ } else if (fSelectionBinding != null) {
+ return typeName + fSelectionBinding.getName();
+ } else {
+ return typeName;
+ }
+ }
+
+ /**
+ * Returns the Collection<String> of names of types that can be given to the selected declaration.
+ * (used in tests only)
+ * @return Collection<String> of names of types that can be given to the selected declaration
+ */
+ public Collection<String> getValidTypeNames() {
+ Collection<String> typeNames= new ArrayList<String>();
+ for (Iterator<ITypeBinding> it= fValidTypes.iterator(); it.hasNext();) {
+ ITypeBinding type= it.next();
+ typeNames.add(type.getQualifiedName());
+ }
+
+ return typeNames;
+ }
+
+ /**
+ * Find the ASTNode for the given source text selection, if it is a type
+ * declaration, or null otherwise.
+ * @param unit The compilation unit in which the selection was made
+ * @param offset
+ * @param length
+ * @return ASTNode
+ */
+ private ASTNode getTargetNode(ICompilationUnit unit, int offset, int length) {
+ CompilationUnit root= ASTCreator.createAST(unit, null);
+ ASTNode node= NodeFinder.perform(root, offset, length);
+ return node;
+ }
+
+ /**
+ * Determines the set of compilation units that may give rise to type constraints that
+ * we are interested in. This involves searching for overriding/overridden methods,
+ * method calls, field accesses.
+ * @param pm the monitor
+ * @return the affected units
+ * @throws CoreException
+ */
+ private ICompilationUnit[] collectAffectedUnits(IProgressMonitor pm) throws CoreException {
+ // BUG: currently, no type constraints are generated for methods that are related
+ // but that do not override each other. As a result, we may miss certain relevant
+ // variables
+
+ pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 100);
+
+ if (fAffectedUnits != null) {
+ if (DEBUG) printCollection("affected units: ", Arrays.asList(fAffectedUnits)); //$NON-NLS-1$
+ pm.worked(100);
+ return fAffectedUnits;
+ }
+ if (fMethodBinding != null) {
+ if (fMethodBinding != null) {
+
+
+ IMethod selectedMethod= (IMethod) fMethodBinding.getJavaElement();
+ if (selectedMethod == null) {
+ // can't happen since we checked it up front in check initial conditions
+ Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_no_method);
+ }
+
+ // the following code fragment appears to be the source of a memory leak, when
+ // GT is repeatedly applied
+
+ IMethod root= selectedMethod;
+ if (! root.getDeclaringType().isInterface() && MethodChecks.isVirtual(root)) {
+ final SubProgressMonitor subMonitor= new SubProgressMonitor(pm, 5);
+ IMethod inInterface= MethodChecks.isDeclaredInInterface(root, root.getDeclaringType().newTypeHierarchy(new SubProgressMonitor(subMonitor, 1)), subMonitor);
+ if (inInterface != null && !inInterface.equals(root))
+ root= inInterface;
+ }
+
+ // end code fragment
+
+ IMethod[] rippleMethods= RippleMethodFinder2.getRelatedMethods(
+ root, new SubProgressMonitor(pm, 15), null);
+ SearchPattern pattern= RefactoringSearchEngine.createOrPattern(
+ rippleMethods, IJavaSearchConstants.ALL_OCCURRENCES);
+
+ // To compute the scope we have to use the selected method. Otherwise we
+ // might start from the wrong project.
+ IJavaSearchScope scope= RefactoringScopeFactory.create(selectedMethod);
+ CollectingSearchRequestor csr= new CollectingSearchRequestor();
+
+ SearchResultGroup[] groups= RefactoringSearchEngine.search(
+ pattern,
+ null,
+ scope,
+ csr,
+ new SubProgressMonitor(pm, 80),
+ new RefactoringStatus()); //TODO: deal with errors from non-CU matches
+
+ fAffectedUnits= getCus(groups);
+ }
+ } else if (fFieldBinding != null) {
+ IField iField= (IField) fFieldBinding.getJavaElement();
+ if (iField == null) {
+ // can't happen since we checked it up front in check initial conditions
+ Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_no_filed);
+ }
+ SearchPattern pattern= SearchPattern.createPattern(
+ iField, IJavaSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
+ IJavaSearchScope scope= RefactoringScopeFactory.create(iField);
+ CollectingSearchRequestor csr= new CollectingSearchRequestor();
+ SearchResultGroup[] groups=
+ RefactoringSearchEngine.search(pattern, null, scope, csr, new SubProgressMonitor(pm, 100),
+ new RefactoringStatus()); //TODO: deal with errors from non-CU matches
+ fAffectedUnits= getCus(groups);
+ } else {
+ // otherwise, selection was a local variable and we only have to search the CU
+ // containing the selection
+ fAffectedUnits= new ICompilationUnit[] { fCu };
+ }
+ if (DEBUG) {
+ System.out.println("Determining affected CUs:"); //$NON-NLS-1$
+ for (int i= 0; i < fAffectedUnits.length; i++) {
+ System.out.println(" affected CU: " + fAffectedUnits[i].getElementName()); //$NON-NLS-1$
+ }
+ }
+ pm.done();
+ return fAffectedUnits;
+ }
+
+ public void setSelectedType(ITypeBinding type){
+ fSelectedType= type;
+ }
+
+ // -------------------------------------------------------------------------------------------- //
+ // TODO The following utility methods should probably be moved to another class
+
+ /**
+ * Determines if a constraint variable corresponds to the constant "null".
+ * @param cv
+ * @return <code>true</code> if the constraint variable corresponds to the constant "null".
+ */
+ private static boolean isNull(ConstraintVariable cv) {
+ return cv instanceof ExpressionVariable && ((ExpressionVariable)cv).getExpressionType() == ASTNode.NULL_LITERAL;
+ }
+
+
+ /*
+ * For debugging.
+ */
+ void printCollection(String title, Collection<?> l) {
+ System.out.println(l.size() + " " + title); //$NON-NLS-1$
+ for (Iterator<?> it= l.iterator(); it.hasNext();) {
+ System.out.println(" " + it.next()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns the compilation units that contain the search results.
+ * @param groups
+ * @return the CUs
+ */
+ private ICompilationUnit[] getCus(SearchResultGroup[] groups) {
+ List<ICompilationUnit> result= new ArrayList<ICompilationUnit>(groups.length);
+ for (int i= 0; i < groups.length; i++) {
+ SearchResultGroup group= groups[i];
+ group.generated_3750388673956263140(result, this);
+ }
+ return result.toArray(new ICompilationUnit[result.size()]);
+ }
+
+ /**
+ * This always includes the type itself. It will include type
+ * Object for any type other than Object
+ * @param type
+ * @return the super types
+ */
+ public Set<ITypeBinding> getAllSuperTypes(ITypeBinding type){
+ Set<ITypeBinding> result= new HashSet<ITypeBinding>();
+ result.add(type);
+ if (type.getSuperclass() != null){
+ result.addAll(getAllSuperTypes(type.getSuperclass()));
+ }
+ ITypeBinding[] interfaces= type.getInterfaces();
+ for (int i=0; i < interfaces.length; i++){
+ result.addAll(getAllSuperTypes(interfaces[i]));
+ }
+ if ((type != fObject) && !contains(result, fObject)){
+ result.add(fObject);
+ }
+ return result;
+ }
+
+ private ITypeBinding findSuperTypeByName(ITypeBinding type, String superTypeName){
+ Set<ITypeBinding> superTypes= getAllSuperTypes(type);
+ for (Iterator<ITypeBinding> it= superTypes.iterator(); it.hasNext(); ){
+ ITypeBinding sup= it.next();
+ if (sup.getQualifiedName().equals(superTypeName)){
+ return sup;
+ }
+ }
+ return null;
+ }
+
+ public boolean isSubTypeOf(ITypeBinding type1, ITypeBinding type2){
+
+ // to ensure that, e.g., Comparable<String> is considered a subtype of raw Comparable
+ if (type1.isParameterizedType() && type1.getTypeDeclaration().isEqualTo(type2.getTypeDeclaration())){
+ return true;
+ }
+ Set<ITypeBinding> superTypes= getAllSuperTypes(type1);
+ return contains(superTypes, type2);
+ }
+
+ private static boolean contains(Collection<ITypeBinding> c, ITypeBinding binding){
+ for (Iterator<ITypeBinding> it=c.iterator(); it.hasNext(); ){
+ ITypeBinding b = it.next();
+ if (Bindings.equals(b, binding)) return true;
+ }
+ return false;
+ }
+
+ private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
+ return arguments.generated_6800022014899271440(this);
+ }
+
+ public void generated_4646330632040955199(ITypeBinding type, Set<ITypeBinding> result) {
+ if (getOriginalType().isInterface() && type != getObject()){
+ result.add(getObject());
+ }
+ }
+}