]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeTypeRefactoring.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / structure / ChangeTypeRefactoring.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.structure;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.NullProgressMonitor;
28 import org.eclipse.core.runtime.OperationCanceledException;
29 import org.eclipse.core.runtime.SubProgressMonitor;
30
31 import org.eclipse.text.edits.MultiTextEdit;
32 import org.eclipse.text.edits.TextEditGroup;
33
34 import org.eclipse.ltk.core.refactoring.Change;
35 import org.eclipse.ltk.core.refactoring.Refactoring;
36 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
37 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
38
39 import org.eclipse.jdt.core.ICompilationUnit;
40 import org.eclipse.jdt.core.IField;
41 import org.eclipse.jdt.core.IJavaElement;
42 import org.eclipse.jdt.core.IJavaProject;
43 import org.eclipse.jdt.core.IMethod;
44 import org.eclipse.jdt.core.JavaModelException;
45 import org.eclipse.jdt.core.dom.AST;
46 import org.eclipse.jdt.core.dom.ASTNode;
47 import org.eclipse.jdt.core.dom.CompilationUnit;
48 import org.eclipse.jdt.core.dom.Expression;
49 import org.eclipse.jdt.core.dom.FieldDeclaration;
50 import org.eclipse.jdt.core.dom.IBinding;
51 import org.eclipse.jdt.core.dom.IMethodBinding;
52 import org.eclipse.jdt.core.dom.ITypeBinding;
53 import org.eclipse.jdt.core.dom.IVariableBinding;
54 import org.eclipse.jdt.core.dom.MethodDeclaration;
55 import org.eclipse.jdt.core.dom.NodeFinder;
56 import org.eclipse.jdt.core.dom.ParameterizedType;
57 import org.eclipse.jdt.core.dom.QualifiedName;
58 import org.eclipse.jdt.core.dom.SimpleName;
59 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
60 import org.eclipse.jdt.core.dom.Type;
61 import org.eclipse.jdt.core.dom.UnionType;
62 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
63 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
64 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
65 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
66 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
67 import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
68 import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
69 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
70 import org.eclipse.jdt.core.refactoring.descriptors.GeneralizeTypeDescriptor;
71 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
72 import org.eclipse.jdt.core.search.IJavaSearchConstants;
73 import org.eclipse.jdt.core.search.IJavaSearchScope;
74 import org.eclipse.jdt.core.search.SearchPattern;
75
76 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
77 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
78 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
79 import org.eclipse.jdt.internal.corext.dom.Bindings;
80 import org.eclipse.jdt.internal.corext.refactoring.Checks;
81 import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor;
82 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
83 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
84 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
85 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
86 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
87 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
88 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
89 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
90 import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
91 import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
92 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ASTCreator;
93 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.CompositeOrTypeConstraint;
94 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintCollector;
95 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintOperator;
96 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintVariable;
97 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ConstraintVariableFactory;
98 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ExpressionVariable;
99 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.FullConstraintCreator;
100 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ITypeConstraint;
101 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ParameterTypeVariable;
102 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ReturnTypeVariable;
103 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.SimpleTypeConstraint;
104 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.TypeConstraintFactory;
105 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.TypeVariable;
106 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
107 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
108 import org.eclipse.jdt.internal.corext.util.Messages;
109 import org.eclipse.jdt.internal.corext.util.SearchUtils;
110
111 import org.eclipse.jdt.ui.JavaElementLabels;
112
113 import org.eclipse.jdt.internal.ui.JavaPlugin;
114 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
115 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
116 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
117
118 /**
119  * @author tip
120  */
121 public class ChangeTypeRefactoring extends Refactoring {
122
123         private static final String ATTRIBUTE_TYPE= "type"; //$NON-NLS-1$
124
125         private final Map<ICompilationUnit, List<ITypeConstraint>> fConstraintCache;
126         /**
127          * Offset of the selected text area.
128          */
129         private int fSelectionStart;
130
131         /**
132          * Length of the selected text area.
133          */
134         private int fSelectionLength;
135
136         /**
137          * Offset of the effective selection
138          */
139         private int fEffectiveSelectionStart;
140
141         /**
142          * Length of the effective selection
143          */
144         private int fEffectiveSelectionLength;
145
146         /**
147          * ICompilationUnit containing the selection.
148          */
149         private ICompilationUnit fCu;
150
151         /**
152          * If the selection corresponds to a method parameter/return type, this field stores
153          * a reference to its IMethodBinding, otherwise this field remains null. Used during
154          * search for references in other CUs, and for determining the ConstraintVariable
155          * that corresponds to the selection
156          */
157         private IMethodBinding fMethodBinding;
158
159         /**
160          * If the selection corresponds to a method parameter, this field stores the parameter
161          * index (0 = first parameter for static methods, 0 = this for nonstatic methods). The
162          * value -1 is stored in the field if the selection corresponds to a method return type.
163          */
164         private int fParamIndex;
165
166         /**
167          * The name of the selected parameter, or <code>null</code>.
168          */
169         private String fParamName;
170
171         /**
172          * If the selection corresponds to a field, this field stores a reference to its IVariableBinding,
173          * otherwise this field remains null. Used during search for references in other CUs.
174          */
175         private IVariableBinding fFieldBinding;
176
177         /**
178          * The compilation units that contain constraint variables related to the selection
179          */
180         private ICompilationUnit[] fAffectedUnits;
181
182         /**
183          * The constraint variables that are of interest to this refactoring. This includes
184          * the constraint var. corresponding to the text selection, and possibly additional
185          * elements due to method overriding, method calls, etc.
186          */
187         private Collection<ConstraintVariable> fRelevantVars;
188
189         /**
190          * The set of types (other than the original type) that can be given to
191          * the selected ASTNode.
192          */
193         private final Collection<ITypeBinding> fValidTypes;
194
195         /**
196          * The type constraints that are related to the selected ASTNode.
197          */
198         private Collection<ITypeConstraint> fRelevantConstraints;
199
200         /**
201          * All type constraints in affected compilation units.
202          */
203         private Collection<ITypeConstraint> fAllConstraints;
204
205         /**
206          * The name of the new type of the selected declaration.
207          */
208         private String fSelectedTypeName;
209
210         /**
211          * The new type of the selected declaration.
212          */
213         private ITypeBinding fSelectedType;
214
215         /**
216          * Organizes SearchResults by CompilationUnit
217          */
218         private Map<ICompilationUnit, SearchResultGroup> fCuToSearchResultGroup= new HashMap<ICompilationUnit, SearchResultGroup>();
219
220
221         /**
222          * ITypeBinding for java.lang.Object
223          */
224         private ITypeBinding fObject;
225
226         public ITypeBinding getObject(){
227                 return fObject;
228         }
229
230         /**
231          * Control debugging output.
232          */
233         private static final boolean DEBUG= false;
234
235         private ConstraintVariable fCv;
236         private IBinding fSelectionBinding;
237         private ITypeBinding fSelectionTypeBinding;
238         private ConstraintCollector fCollector;
239
240         public ChangeTypeRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength) {
241                 this(cu, selectionStart, selectionLength, null);
242         }
243
244         /**
245          * Constructor for ChangeTypeRefactoring (invoked from tests only)
246          * @param cu the compilation unit
247          * @param selectionStart selection offset
248          * @param selectionLength selection length
249          * @param selectedType selected type
250          */
251         public ChangeTypeRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength, String selectedType) {
252                 Assert.isTrue(selectionStart >= 0);
253                 Assert.isTrue(selectionLength >= 0);
254
255                 fSelectionStart= selectionStart;
256                 fSelectionLength= selectionLength;
257
258                 fEffectiveSelectionStart= selectionStart;
259                 fEffectiveSelectionLength= selectionLength;
260
261                 fCu= cu;
262
263                 if (selectedType != null)
264                         fSelectedTypeName= selectedType;
265
266                 fConstraintCache= new HashMap<ICompilationUnit, List<ITypeConstraint>>();
267                 fValidTypes= new HashSet<ITypeBinding>();
268         }
269
270     public ChangeTypeRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
271                 this(null, 0, 0, null);
272                 RefactoringStatus initializeStatus= initialize(arguments);
273                 status.merge(initializeStatus);
274     }
275
276         // ------------------------------------------------------------------------------------------------- //
277
278         /*
279          * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
280          */
281         @Override
282         public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
283                 if (fCu == null || !fCu.isStructureKnown())
284                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_invalidSelection);
285                 return checkSelection(new SubProgressMonitor(pm, 1));
286         }
287
288         private void setSelectionRanges(Expression exp){
289                 fEffectiveSelectionStart= exp.getStartPosition();
290                 fEffectiveSelectionLength= exp.getLength();
291                 fSelectionBinding= ExpressionVariable.resolveBinding(exp);
292                 setOriginalType(exp.resolveTypeBinding());
293         }
294
295         /**
296          * Check if the right type of AST Node is selected. Create the TypeHierarchy needed to
297          * bring up the wizard.
298          * @param pm progress monitor
299          * @return returns the resulting status
300          */
301         private RefactoringStatus checkSelection(IProgressMonitor pm) {
302                 try {
303                         pm.beginTask("", 5); //$NON-NLS-1$
304
305                         ASTNode node= getTargetNode(fCu, fSelectionStart, fSelectionLength);
306                         if (DEBUG) {
307                                 System.out.println(
308                                         "selection: [" //$NON-NLS-1$
309                                                 + fSelectionStart
310                                                 + "," //$NON-NLS-1$
311                                                 + (fSelectionStart + fSelectionLength)
312                                                 + "] in " //$NON-NLS-1$
313                                                 + fCu.getElementName());
314                                 System.out.println("node= " + node + ", type= " + node.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
315                         }
316
317                         TypeConstraintFactory typeConstraintFactory = new TypeConstraintFactory(){
318                                 @Override
319                                 public boolean filter(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator o){
320                                         if (o.isStrictSubtypeOperator()) //TODO: explain why these can be excluded
321                                                 return true;
322                                         //Don't create constraint if fSelectionTypeBinding is not involved:
323                                         if (v1.getBinding() != null && v2.getBinding() != null
324                                                         && ! Bindings.equals(v1.getBinding(), fSelectionTypeBinding)
325                                                         && ! Bindings.equals(v2.getBinding(), fSelectionTypeBinding)) {
326                                                 if (PRINT_STATS) fNrFiltered++;
327                                                 return true;
328                                         }
329                                         return super.filter(v1, v2, o);
330                                 }
331                         };
332                         fCollector= new ConstraintCollector(new FullConstraintCreator(new ConstraintVariableFactory(), typeConstraintFactory));
333                         String selectionValid= determineSelection(node);
334                         if (selectionValid != null){
335                                 if (DEBUG){
336                                         System.out.println("invalid selection: " + selectionValid); //$NON-NLS-1$
337                                 }
338                                 return RefactoringStatus.createFatalErrorStatus(selectionValid);
339                         }
340
341                         if (fMethodBinding != null) {
342                                 IMethod selectedMethod= (IMethod) fMethodBinding.getJavaElement();
343                                 if (selectedMethod == null){
344                                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_insideLocalTypesNotSupported);
345                                 }
346                         }
347
348                         pm.worked(1);
349
350                         RefactoringStatus result= new RefactoringStatus();
351
352                         if (DEBUG){
353                                 System.out.println("fSelectionTypeBinding: " + fSelectionTypeBinding.getName()); //$NON-NLS-1$
354                         }
355
356                         // produce error message if array or primitive type is selected
357                         if (fSelectionTypeBinding.isArray()){
358                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported);
359                         }
360                         if (fSelectionTypeBinding.isPrimitive()){
361                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_primitivesNotSupported);
362                         }
363                         if (checkOverriddenBinaryMethods())
364                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnBinary);
365
366                         if (fSelectionTypeBinding.isLocal()){
367                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_localTypesNotSupported);
368                         }
369
370                         if (fFieldBinding != null && fFieldBinding.getDeclaringClass().isLocal()){
371                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_insideLocalTypesNotSupported);
372                         }
373
374                         if (fSelectionTypeBinding.isTypeVariable()){
375                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_typeParametersNotSupported);
376                         }
377
378                         if (fSelectionTypeBinding.isEnum()){
379                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeTypeRefactoring_enumsNotSupported);
380                         }
381
382                         pm.worked(1);
383
384                         if (fSelectedType != null){ // if invoked from unit test, compute valid types here
385                                 computeValidTypes(new NullProgressMonitor());
386                         }
387                         return result;
388                 } finally {
389                         pm.done();
390                 }
391         }
392
393         private boolean checkOverriddenBinaryMethods() {
394                 if (fMethodBinding != null){
395                         Set<ITypeBinding> declaringSupertypes= getDeclaringSuperTypes(fMethodBinding);
396                         for (Iterator<ITypeBinding> iter= declaringSupertypes.iterator(); iter.hasNext();) {
397                                 ITypeBinding superType= iter.next();
398                                 IMethodBinding overriddenMethod= findMethod(fMethodBinding, superType);
399                                 Assert.isNotNull(overriddenMethod);//because we asked for declaring types
400                                 IMethod iMethod= (IMethod) overriddenMethod.getJavaElement();
401                                 if (iMethod.isBinary()){
402                                         return true;
403                                 }
404                         }
405                 }
406                 return false;
407         }
408
409         // copied from FullConstraintCreator
410         private static IMethodBinding findMethod(IMethodBinding methodBinding, ITypeBinding type) {
411                   if (methodBinding.getDeclaringClass().equals(type))
412                           return methodBinding;
413                   return Bindings.findOverriddenMethodInType(type, methodBinding);
414         }
415
416         // copied from FullConstraintCreator
417         private static Set<ITypeBinding> getDeclaringSuperTypes(IMethodBinding methodBinding) {
418                 ITypeBinding[] allSuperTypes= Bindings.getAllSuperTypes(methodBinding.getDeclaringClass());
419                 Set<ITypeBinding> result= new HashSet<ITypeBinding>();
420                 for (int i= 0; i < allSuperTypes.length; i++) {
421                         ITypeBinding type= allSuperTypes[i];
422                         if (findMethod(methodBinding, type) != null)
423                                 result.add(type);
424                 }
425                 return result;
426         }
427
428         /**
429          * Do the actual work of computing allowable types. Invoked by the wizard when
430          * "compute" button is pressed
431          * @param pm the progress monitor
432          * @return the valid types
433          */
434         public Collection<ITypeBinding> computeValidTypes(IProgressMonitor pm) {
435
436                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_checking_preconditions, 100);
437
438                 try {
439                         fCv= findConstraintVariableForSelectedNode(new SubProgressMonitor(pm, 3));
440                         if (DEBUG) System.out.println("selected CV: " + fCv +  //$NON-NLS-1$
441                                                                                   " (" + fCv.getClass().getName() +  //$NON-NLS-1$
442                                                                                   ")");  //$NON-NLS-1$
443
444                         if (pm.isCanceled())
445                                 throw new OperationCanceledException();
446                         fRelevantVars= findRelevantConstraintVars(fCv, new SubProgressMonitor(pm, 50));
447
448                         if (DEBUG)
449                                 printCollection("relevant vars:", fRelevantVars); //$NON-NLS-1$
450
451                         if (pm.isCanceled())
452                                 throw new OperationCanceledException();
453                         fRelevantConstraints= findRelevantConstraints(fRelevantVars, new SubProgressMonitor(pm, 30));
454
455                         if (pm.isCanceled())
456                                 throw new OperationCanceledException();
457                         fValidTypes.addAll(computeValidTypes(fSelectionTypeBinding, fRelevantVars,
458                                                                                                  fRelevantConstraints, new SubProgressMonitor(pm, 20)));
459
460                         if (DEBUG)
461                                 printCollection("valid types:", getValidTypeNames()); //$NON-NLS-1$
462                 } catch (CoreException e) {
463                         JavaPlugin.logErrorMessage("Error occurred during computation of valid types: " + e.toString()); //$NON-NLS-1$
464                         fValidTypes.clear(); // error occurred during computation of valid types
465                 }
466
467                 pm.done();
468
469                 return fValidTypes;
470         }
471
472
473         /*
474          * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
475          */
476         @Override
477         public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
478                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_checking_preconditions, 1);
479
480                 RefactoringStatus result= Checks.validateModifiesFiles(
481                         ResourceUtil.getFiles(fAffectedUnits), getValidationContext());
482
483                 pm.done();
484                 return result;
485         }
486 // TODO: do sanity check somewhere if the refactoring changes any files.
487         /*
488          * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
489          */
490         @Override
491         public Change createChange(IProgressMonitor pm) throws CoreException {
492                 pm.beginTask(RefactoringCoreMessages.ChangeTypeMessages_CreateChangesForChangeType, 1);
493                 try {
494                         Map<ICompilationUnit, Set<ConstraintVariable>> relevantVarsByUnit= new HashMap<ICompilationUnit, Set<ConstraintVariable>>();
495                         groupChangesByCompilationUnit(relevantVarsByUnit);
496                         final Map<String, String> arguments= new HashMap<String, String>();
497                         String project= null;
498                         IJavaProject javaProject= fCu.getJavaProject();
499                         if (javaProject != null)
500                                 project= javaProject.getElementName();
501                         final String description= RefactoringCoreMessages.ChangeTypeRefactoring_descriptor_description_short;
502                         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)});
503                         final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
504                         comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_original_element_pattern, BindingLabelProvider.getBindingLabel(fSelectionBinding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
505                         comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_original_type_pattern, BindingLabelProvider.getBindingLabel(getOriginalType(), JavaElementLabels.ALL_FULLY_QUALIFIED)));
506                         comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_refactored_type_pattern, BindingLabelProvider.getBindingLabel(fSelectedType, JavaElementLabels.ALL_FULLY_QUALIFIED)));
507                         final GeneralizeTypeDescriptor descriptor= RefactoringSignatureDescriptorFactory.createGeneralizeTypeDescriptor(project, description, comment.asString(), arguments, (RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT));
508                         arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCu));
509                         arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
510                         arguments.put(ATTRIBUTE_TYPE, fSelectedType.getQualifiedName());
511                         final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.ChangeTypeRefactoring_allChanges);
512                         for (Iterator<ICompilationUnit>it= relevantVarsByUnit.keySet().iterator(); it.hasNext();) {
513                                 ICompilationUnit icu= it.next();
514                                 Set<ConstraintVariable> cVars= relevantVarsByUnit.get(icu);
515                                 CompilationUnitChange cuChange= new CompilationUnitChange(getName(), icu);
516                                 addAllChangesFor(icu, cVars, cuChange);
517                                 result.add(cuChange);
518                                 pm.worked(1);
519                                 if (pm.isCanceled())
520                                         throw new OperationCanceledException();
521                         }
522                         return result;
523                 } finally {
524                         pm.done();
525                 }
526         }
527
528         /**
529          * Apply all changes related to a single ICompilationUnit
530          * @param icu the compilation unit
531          * @param vars
532          * @param unitChange
533          * @throws CoreException
534          */
535         private void addAllChangesFor(ICompilationUnit icu, Set<ConstraintVariable> vars, CompilationUnitChange unitChange) throws CoreException {
536                 CompilationUnit unit= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(icu, false);
537                 ASTRewrite unitRewriter= ASTRewrite.create(unit.getAST());
538                 MultiTextEdit root= new MultiTextEdit();
539                 unitChange.setEdit(root); // Adam sez don't need this, but then unitChange.addGroupDescription() fails an assertion!
540
541                 String typeName= updateImports(unit, root);
542                 updateCu(unit, vars, unitChange, unitRewriter, typeName);
543                 root.addChild(unitRewriter.rewriteAST());
544         }
545
546         private class SourceRangeComputer extends TargetSourceRangeComputer {
547                 @Override
548                 public SourceRange computeSourceRange(ASTNode node) {
549                         return new SourceRange(node.getStartPosition(),node.getLength());
550                 }
551         }
552
553         private void updateCu(CompilationUnit unit, Set<ConstraintVariable> vars, CompilationUnitChange unitChange,
554                 ASTRewrite unitRewriter, String typeName) throws JavaModelException {
555
556         // use custom SourceRangeComputer to avoid losing comments
557                 unitRewriter.setTargetSourceRangeComputer(new SourceRangeComputer());
558
559                 for (Iterator<ConstraintVariable> it=vars.iterator(); it.hasNext(); ){
560                         ConstraintVariable cv = it.next();
561                         ASTNode decl= findDeclaration(unit, cv);
562                         if ((decl instanceof SimpleName || decl instanceof QualifiedName) && cv instanceof ExpressionVariable) {
563                                 ASTNode gp= decl.getParent().getParent();
564                                 updateType(unit, getType(gp), unitChange, unitRewriter, typeName);   // local variable or parameter
565                         } else if (decl instanceof MethodDeclaration || decl instanceof FieldDeclaration) {
566                                 updateType(unit, getType(decl), unitChange, unitRewriter, typeName); // method return or field type
567                         } else if (decl instanceof ParameterizedType){
568                                 updateType(unit, getType(decl), unitChange, unitRewriter, typeName);
569                         }
570                 }
571         }
572
573         private void updateType(CompilationUnit cu, Type oldType, CompilationUnitChange unitChange,
574                                                         ASTRewrite unitRewriter, String typeName) {
575
576                 String oldName= fSelectionTypeBinding.getName();
577                 String[] keys= { BasicElementLabels.getJavaElementName(oldName), BasicElementLabels.getJavaElementName(typeName)};
578                 String description= Messages.format(RefactoringCoreMessages.ChangeTypeRefactoring_typeChange, keys);
579                 TextEditGroup gd= new TextEditGroup(description);
580                 AST     ast= cu.getAST();
581
582                 ASTNode nodeToReplace= oldType;
583                 if (fSelectionTypeBinding.isParameterizedType() && !fSelectionTypeBinding.isRawType()){
584                         if (oldType.isSimpleType()){
585                                 nodeToReplace= oldType.getParent();
586                         }
587                 }
588
589             //TODO handle types other than simple & parameterized (e.g., arrays)
590                 Assert.isTrue(fSelectedType.isClass() || fSelectedType.isInterface());
591
592                 Type newType= null;
593                 if (!fSelectedType.isParameterizedType()){
594                         newType= ast.newSimpleType(ASTNodeFactory.newName(ast, typeName));
595                 } else {
596                         newType= createParameterizedType(ast, fSelectedType);
597                 }
598
599                 unitRewriter.replace(nodeToReplace, newType, gd);
600                 unitChange.addTextEditGroup(gd);
601         }
602
603         /**
604          * Creates the appropriate ParameterizedType node. Recursion is needed to
605          * handle the nested case (e.g., Vector<Vector<String>>).
606          * @param ast
607          * @param typeBinding
608          * @return the created type
609          */
610         private Type createParameterizedType(AST ast, ITypeBinding typeBinding){
611                 if (typeBinding.isParameterizedType() && !typeBinding.isRawType()){
612                         Type baseType= ast.newSimpleType(ASTNodeFactory.newName(ast, typeBinding.getErasure().getName()));
613                         ParameterizedType newType= ast.newParameterizedType(baseType);
614                         for (int i=0; i < typeBinding.getTypeArguments().length; i++){
615                                 ITypeBinding typeArg= typeBinding.getTypeArguments()[i];
616                                 Type argType= createParameterizedType(ast, typeArg); // recursive call
617                                 newType.typeArguments().add(argType);
618                         }
619                         return newType;
620                 } else {
621                         if (!typeBinding.isTypeVariable()){
622                                 return ast.newSimpleType(ASTNodeFactory.newName(ast, typeBinding.getErasure().getName()));
623                         } else {
624                                 return ast.newSimpleType(ast.newSimpleName(typeBinding.getName()));
625                         }
626                 }
627         }
628
629
630
631         private void groupChangesByCompilationUnit(Map<ICompilationUnit, Set<ConstraintVariable>> relevantVarsByUnit) {
632                 for (Iterator<ConstraintVariable> it= fRelevantVars.iterator(); it.hasNext();) {
633                         ConstraintVariable cv= it.next();
634                         if (!(cv instanceof ExpressionVariable) && !(cv instanceof ReturnTypeVariable)){
635                                 continue;
636                         }
637                         ICompilationUnit icu = null;
638                         if (cv instanceof ExpressionVariable) {
639                                 ExpressionVariable ev = (ExpressionVariable)cv;
640                                 icu = ev.getCompilationUnitRange().getCompilationUnit();
641                         } else if (cv instanceof ReturnTypeVariable){
642                                 ReturnTypeVariable rtv = (ReturnTypeVariable)cv;
643                                 IMethodBinding mb= rtv.getMethodBinding();
644                                 icu= ((IMethod) mb.getJavaElement()).getCompilationUnit();
645                         }
646                         if (!relevantVarsByUnit.containsKey(icu)){
647                                 relevantVarsByUnit.put(icu, new HashSet<ConstraintVariable>());
648                         }
649                         relevantVarsByUnit.get(icu).add(cv);
650                 }
651         }
652
653         private ASTNode findDeclaration(CompilationUnit root, ConstraintVariable cv) throws JavaModelException {
654
655                 if (fFieldBinding != null){
656                         IField f= (IField) fFieldBinding.getJavaElement();
657                         return ASTNodeSearchUtil.getFieldDeclarationNode(f, root);
658                 }
659
660                 if (cv instanceof ExpressionVariable){
661                         for (Iterator<ITypeConstraint> iter= fAllConstraints.iterator(); iter.hasNext();) {
662                                 ITypeConstraint constraint= iter.next();
663                                 if (constraint.isSimpleTypeConstraint()){
664                                         SimpleTypeConstraint stc= (SimpleTypeConstraint)constraint;
665                                         if (stc.isDefinesConstraint() && stc.getLeft().equals(cv)){
666                                                 ConstraintVariable right= stc.getRight();
667                                                 if (right instanceof TypeVariable){
668                                                         TypeVariable typeVariable= (TypeVariable)right;
669                                                         return NodeFinder.perform(root, typeVariable.getCompilationUnitRange().getSourceRange());
670                                                 }
671                                         }
672                                 }
673                         }
674                 } else if (cv instanceof ReturnTypeVariable) {
675                         ReturnTypeVariable rtv= (ReturnTypeVariable) cv;
676                         IMethodBinding mb= rtv.getMethodBinding();
677                         IMethod im= (IMethod) mb.getJavaElement();
678                         return ASTNodeSearchUtil.getMethodDeclarationNode(im, root);
679                 }
680                 return null;
681         }
682
683         private static Type getType(ASTNode node) {
684                 switch(node.getNodeType()){
685                         case ASTNode.SINGLE_VARIABLE_DECLARATION:
686                                 return ((SingleVariableDeclaration) node).getType();
687                         case ASTNode.FIELD_DECLARATION:
688                                 return ((FieldDeclaration) node).getType();
689                         case ASTNode.VARIABLE_DECLARATION_STATEMENT:
690                                 return ((VariableDeclarationStatement) node).getType();
691                         case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
692                                 return ((VariableDeclarationExpression) node).getType();
693                         case ASTNode.METHOD_DECLARATION:
694                                 return ((MethodDeclaration)node).getReturnType2();
695                         case ASTNode.PARAMETERIZED_TYPE:
696                                 return ((ParameterizedType)node).getType();
697                         default:
698                                 Assert.isTrue(false);
699                                 return null;
700                 }
701         }
702
703         /*
704          * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName()
705          */
706         @Override
707         public String getName() {
708                 return RefactoringCoreMessages.ChangeTypeRefactoring_name;
709         }
710
711         // ------------------------------------------------------------------------------------------------- //
712         // Method for examining if a suitable kind of ASTNode was selected. Information about this node and
713         // its parents in the AST are stored in fields fBinding, theMethod, and theField
714
715         /**
716          * Determines what kind of ASTNode has been selected.
717          * @param node the node
718          * @return  A non-null String containing an error message
719          * is returned if the ChangeTypeRefactoring refactoring cannot be applied to the selected ASTNode.
720          * A return value of null indicates a valid selection.
721          */
722         private String determineSelection(ASTNode node) {
723                 if (node == null) {
724                         return RefactoringCoreMessages.ChangeTypeRefactoring_invalidSelection;
725                 } else {
726
727                         if (DEBUG) System.out.println("node nodeType= " + node.getClass().getName()); //$NON-NLS-1$
728                         if (DEBUG) System.out.println("parent nodeType= " + node.getParent().getClass().getName()); //$NON-NLS-1$
729                         if (DEBUG) System.out.println("GrandParent nodeType= " + node.getParent().getParent().getClass().getName()); //$NON-NLS-1$
730
731                         ASTNode parent= node.getParent();
732                         ASTNode grandParent= parent.getParent();
733                         if (grandParent == null)
734                                 return nodeTypeNotSupported();
735
736                         // adjustment needed if part of a parameterized type is selected
737                         if (grandParent.getNodeType() == ASTNode.PARAMETERIZED_TYPE){
738                                 node= grandParent;
739                         }
740
741                         // adjustment needed if part of a qualified name is selected
742                         ASTNode current= null;
743                         if (node.getNodeType() == ASTNode.QUALIFIED_NAME){
744                                 current= node;
745                                 while (current.getNodeType() == ASTNode.QUALIFIED_NAME){
746                                         current= current.getParent();
747                                 }
748                                 if (current.getNodeType() != ASTNode.SIMPLE_TYPE){
749                                         return nodeTypeNotSupported();
750                                 }
751                                 node= current.getParent();
752                         } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME){
753                                 current= parent;
754                                 while (current.getNodeType() == ASTNode.QUALIFIED_NAME){
755                                         current= current.getParent();
756                                 }
757                                 if (current.getNodeType() != ASTNode.SIMPLE_TYPE){
758                                         return nodeTypeNotSupported();
759                                 }
760                                 node= current.getParent();
761                         }
762
763                         fObject= node.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
764                         switch (node.getNodeType()) {
765                                 case ASTNode.SIMPLE_NAME :
766                                         return simpleNameSelected((SimpleName)node);
767                                 case ASTNode.VARIABLE_DECLARATION_STATEMENT :
768                                         return variableDeclarationStatementSelected((VariableDeclarationStatement) node);
769                                 case ASTNode.FIELD_DECLARATION :
770                                         return fieldDeclarationSelected((FieldDeclaration) node);
771                                 case ASTNode.SINGLE_VARIABLE_DECLARATION :
772                                         return singleVariableDeclarationSelected((SingleVariableDeclaration) node);
773                                 case ASTNode.PARAMETERIZED_TYPE:
774                                         return parameterizedTypeSelected((ParameterizedType) node);
775                                 default :
776                                         return nodeTypeNotSupported();
777                         }
778                 }
779         }
780         /**
781          * The selection corresponds to an ASTNode on which "ChangeTypeRefactoring" is not defined.
782          * @return the message
783          */
784         private static String nodeTypeNotSupported() {
785                 return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
786         }
787
788         /**
789           * The selection corresponds to a SingleVariableDeclaration
790          * @param svd
791          * @return the message
792           */
793         private String singleVariableDeclarationSelected(SingleVariableDeclaration svd) {
794                 SimpleName name = svd.getName();
795                 setSelectionRanges(name);
796                 return simpleNameSelected(name);
797         }
798
799         /**
800           * The selection corresponds to a ParameterizedType (return type of method)
801          * @param pt the type
802          * @return the message
803           */
804         private String parameterizedTypeSelected(ParameterizedType pt) {
805                 ASTNode parent= pt.getParent();
806                 if (parent.getNodeType() == ASTNode.METHOD_DECLARATION){
807                         fMethodBinding= ((MethodDeclaration)parent).resolveBinding();
808                         fParamIndex= -1;
809                         fEffectiveSelectionStart= pt.getStartPosition();
810                         fEffectiveSelectionLength= pt.getLength();
811                         setOriginalType(pt.resolveBinding());
812                 } else if (parent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION){
813                         return singleVariableDeclarationSelected((SingleVariableDeclaration)parent);
814                 } else if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
815                         return variableDeclarationStatementSelected((VariableDeclarationStatement)parent);
816                 } else if (parent.getNodeType() == ASTNode.FIELD_DECLARATION){
817                         return fieldDeclarationSelected((FieldDeclaration)parent);
818                 } else {
819                         return nodeTypeNotSupported();
820                 }
821                 return null;
822         }
823
824         /**
825          * The selection corresponds to a VariableDeclarationStatement
826          * @param vds the name
827          * @return the message
828          */
829         private String variableDeclarationStatementSelected(VariableDeclarationStatement vds) {
830                 if (vds.fragments().size() != 1) {
831                         return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
832                 } else {
833                         VariableDeclarationFragment elem= (VariableDeclarationFragment) vds.fragments().iterator().next();
834                         SimpleName name= elem.getName();
835                         setSelectionRanges(name);
836                         return simpleNameSelected(name);
837                 }
838         }
839
840         /**
841          * The selection corresponds to a FieldDeclaration
842          * @param fieldDeclaration the field
843          * @return the message
844          */
845         private String fieldDeclarationSelected(FieldDeclaration fieldDeclaration) {
846                 if (fieldDeclaration.fragments().size() != 1) {
847                         return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
848                 } else {
849                         VariableDeclarationFragment elem= (VariableDeclarationFragment) fieldDeclaration.fragments().iterator().next();
850                         fFieldBinding= elem.resolveBinding();
851                         SimpleName name= elem.getName();
852                         setSelectionRanges(name);
853                         return simpleNameSelected(name);
854                 }
855         }
856
857         /**
858          * The selection corresponds to a SimpleName
859          * @param simpleName the name
860          * @return the message
861          */
862         private String simpleNameSelected(SimpleName simpleName) {
863                 ASTNode parent= simpleName.getParent();
864                 ASTNode grandParent= parent.getParent();
865
866                 if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
867                         VariableDeclarationStatement vds= (VariableDeclarationStatement)parent;
868                         if (vds.fragments().size() > 1){
869                                 return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
870                         }
871                 } else if (parent.getNodeType() == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
872                         if (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
873                                 VariableDeclarationStatement vds= (VariableDeclarationStatement)grandParent;
874                                 if (vds.fragments().size() > 1) {
875                                         return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
876                                 }
877                                 setSelectionRanges(simpleName);
878                         } else if (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
879                                 VariableDeclarationExpression vde= (VariableDeclarationExpression)grandParent;
880                                 if (vde.fragments().size() > 1) {
881                                         return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
882                                 }
883                                 setSelectionRanges(simpleName);
884                         } else if (grandParent.getNodeType() == ASTNode.FIELD_DECLARATION) {
885                                 FieldDeclaration fd= (FieldDeclaration)grandParent;
886                                 if (fd.fragments().size() > 1){
887                                         return RefactoringCoreMessages.ChangeTypeRefactoring_multiDeclarationsNotSupported;
888                                 }
889                                 VariableDeclarationFragment fragment = (VariableDeclarationFragment)parent;
890                                 fFieldBinding= fragment.resolveBinding();
891                                 setSelectionRanges(fragment.getName());
892                         } else {
893                                 return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
894                         }
895                 } else if (parent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION) {
896                         SingleVariableDeclaration singleVariableDeclaration= (SingleVariableDeclaration) parent;
897                         if (singleVariableDeclaration.getType() instanceof UnionType) {
898                                 return RefactoringCoreMessages.ChangeTypeRefactoring_uniontypeNotSupported;
899                         }
900                         if ((grandParent.getNodeType() == ASTNode.METHOD_DECLARATION)) {
901                                 fMethodBinding= ((MethodDeclaration)grandParent).resolveBinding();
902                                 setOriginalType(simpleName.resolveTypeBinding());
903                                 fParamIndex= ((MethodDeclaration)grandParent).parameters().indexOf(parent);
904                                 fParamName= singleVariableDeclaration.getName().getIdentifier();
905                         } else {
906                                 setSelectionRanges(singleVariableDeclaration.getName());
907                         }
908                 } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE && (grandParent.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION)) {
909                         ASTNode greatGrandParent= grandParent.getParent();
910                         SingleVariableDeclaration singleVariableDeclaration= (SingleVariableDeclaration) grandParent;
911                         if (singleVariableDeclaration.getExtraDimensions() > 0 || singleVariableDeclaration.isVarargs()) {
912                                 return RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported;
913                         }
914                         if (greatGrandParent != null && greatGrandParent.getNodeType() == ASTNode.METHOD_DECLARATION) {
915                                 fMethodBinding= ((MethodDeclaration)greatGrandParent).resolveBinding();
916                                 fParamIndex= ((MethodDeclaration)greatGrandParent).parameters().indexOf(grandParent);
917                                 fParamName= singleVariableDeclaration.getName().getIdentifier();
918                                 setSelectionRanges(simpleName);
919                         } else {
920                                 setSelectionRanges(singleVariableDeclaration.getName());
921                         }
922                 } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE && grandParent.getNodeType() == ASTNode.METHOD_DECLARATION) {
923                         fMethodBinding= ((MethodDeclaration)grandParent).resolveBinding();
924                         setOriginalType(fMethodBinding.getReturnType());
925                         fParamIndex= -1;
926                 } else if (parent.getNodeType() == ASTNode.METHOD_DECLARATION &&
927                                 grandParent.getNodeType() == ASTNode.TYPE_DECLARATION) {
928                         MethodDeclaration methodDeclaration= (MethodDeclaration)parent;
929                         if (methodDeclaration.getName().equals(simpleName) || methodDeclaration.thrownExceptions().contains(simpleName)){
930                                 return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
931                         }
932                         fMethodBinding= ((MethodDeclaration)parent).resolveBinding();
933                         fParamIndex= -1;
934                 } else if (
935                                 parent.getNodeType() == ASTNode.SIMPLE_TYPE && (grandParent.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT)) {
936                         return variableDeclarationStatementSelected((VariableDeclarationStatement) grandParent);
937                 } else if (parent.getNodeType() == ASTNode.CAST_EXPRESSION) {
938                         ASTNode decl= findDeclaration(parent.getRoot(), fSelectionStart, fSelectionLength+1);
939                         VariableDeclarationFragment fragment= (VariableDeclarationFragment)decl;
940                         SimpleName name = fragment.getName();
941                         setSelectionRanges(name);
942                 } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE &&
943                                 grandParent.getNodeType() == ASTNode.FIELD_DECLARATION) {
944                         return fieldDeclarationSelected((FieldDeclaration) grandParent);
945                 } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE &&
946                                 grandParent.getNodeType() == ASTNode.ARRAY_TYPE){
947                         return RefactoringCoreMessages.ChangeTypeRefactoring_arraysNotSupported;
948                 } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME){
949                         setSelectionRanges(simpleName);
950                 } else {
951                         return RefactoringCoreMessages.ChangeTypeRefactoring_notSupportedOnNodeType;
952                 }
953                 return null;
954         }
955
956         // ------------------------------------------------------------------------------------------------- //
957     // Methods for examining & solving type constraints. This includes:
958     //  (1) locating the ConstraintVariable corresponding to the selected ASTNode
959     //  (2) finding all ConstraintVariables "related" to (1) via overriding, method calls, field access
960     //  (3) find all ITypeConstraints of interest that mention ConstraintVariables in (2)
961     //  (4) determining all ITypes for which the ITypeConstraints in (3) are satisfied
962
963         /**
964          * Find a ConstraintVariable that corresponds to the selected ASTNode.
965          * @param pm
966          * @return the ConstraintVariable
967          */
968         private ConstraintVariable findConstraintVariableForSelectedNode(IProgressMonitor pm) {
969                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 100);
970                 ICompilationUnit[] cus= { fCu }; // only search in CU containing selection
971
972                 if (DEBUG){
973                         System.out.println("Effective selection: " + fEffectiveSelectionStart + "/" + fEffectiveSelectionLength); //$NON-NLS-1$ //$NON-NLS-2$
974                 }
975
976                 Collection<ITypeConstraint> allConstraints= getConstraints(cus, new SubProgressMonitor(pm, 50));
977
978                 IProgressMonitor subMonitor= new SubProgressMonitor(pm, 50);
979                 subMonitor.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, allConstraints.size());
980                 for (Iterator<ITypeConstraint> it= allConstraints.iterator(); it.hasNext(); ) {
981                         subMonitor.worked(1);
982                         ITypeConstraint tc= it.next();
983                         if (! (tc instanceof SimpleTypeConstraint))
984                                 continue;
985                         SimpleTypeConstraint stc= (SimpleTypeConstraint) tc;
986                         if (matchesSelection(stc.getLeft()))
987                                 return stc.getLeft();
988                         if (matchesSelection(stc.getRight()))
989                                 return stc.getRight();
990                 }
991                 subMonitor.done();
992                 pm.done();
993                 Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_noMatchingConstraintVariable);
994                 return null;
995         }
996
997         /**
998          * Determine if a given ConstraintVariable matches the selected ASTNode.
999          * @param cv the ConstraintVariable
1000          * @return <code>true</code> if the given ConstraintVariable matches the selected ASTNode
1001          */
1002         private boolean matchesSelection(ConstraintVariable cv){
1003                 if (cv instanceof ExpressionVariable){
1004                         ExpressionVariable ev= (ExpressionVariable)cv;
1005                         return (fSelectionBinding != null && Bindings.equals(fSelectionBinding, ev.getExpressionBinding()));
1006                 } else if (cv instanceof ParameterTypeVariable){
1007                         ParameterTypeVariable ptv = (ParameterTypeVariable)cv;
1008                         if (fMethodBinding != null && Bindings.equals(ptv.getMethodBinding(), fMethodBinding) &&
1009                                 ptv.getParameterIndex() == fParamIndex){
1010                                 return true;
1011                         }
1012                 } else if (cv instanceof ReturnTypeVariable){
1013                         ReturnTypeVariable rtv = (ReturnTypeVariable)cv;
1014                         if (fMethodBinding != null && Bindings.equals(rtv.getMethodBinding(), fMethodBinding) &&
1015                                 fParamIndex == -1){
1016                                 return true;
1017                         }
1018                 }
1019                 return false;
1020         }
1021
1022         /**
1023          * Determine the set of constraint variables related to the selected
1024          * expression. In addition to the expression itself, this consists of
1025          * any expression that is defines-equal to it, and any expression equal
1026          * to it.
1027          * @param cv
1028          * @param pm
1029          * @return the constraint variables
1030          * @throws CoreException
1031          */
1032         private Collection<ConstraintVariable> findRelevantConstraintVars(ConstraintVariable cv, IProgressMonitor pm) throws CoreException {
1033                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 150);
1034                 Collection<ConstraintVariable> result= new HashSet<ConstraintVariable>();
1035                 result.add(cv);
1036                 ICompilationUnit[] cus= collectAffectedUnits(new SubProgressMonitor(pm, 50));
1037                 Collection<ITypeConstraint> allConstraints= getConstraints(cus, new SubProgressMonitor(pm, 50));
1038
1039                 List<ConstraintVariable> workList= new ArrayList<ConstraintVariable>(result);
1040                 while(! workList.isEmpty()){
1041
1042                         pm.worked(10);
1043
1044                         ConstraintVariable first= workList.remove(0);
1045                         for (Iterator<ITypeConstraint> iter= allConstraints.iterator(); iter.hasNext();) {
1046                                 pm.worked(1);
1047                                 ITypeConstraint typeConstraint= iter.next();
1048                                 if (! typeConstraint.isSimpleTypeConstraint())
1049                                         continue;
1050                                 SimpleTypeConstraint stc= (SimpleTypeConstraint)typeConstraint;
1051                                 if (! stc.isDefinesConstraint() && ! stc.isEqualsConstraint())
1052                                         continue;
1053                                 ConstraintVariable match= match(first, stc.getLeft(), stc.getRight());
1054                                 if (match instanceof ExpressionVariable
1055                                 || match instanceof ParameterTypeVariable
1056                                 || match instanceof ReturnTypeVariable){
1057                                         if (! result.contains(match)){
1058                                                 workList.add(match);
1059                                                 result.add(match);
1060                                         }
1061                                 }
1062                         }
1063                 }
1064
1065                 pm.done();
1066
1067                 return result;
1068         }
1069         private static ConstraintVariable match(ConstraintVariable matchee, ConstraintVariable left, ConstraintVariable right) {
1070                 if (matchee.equals(left))
1071                         return right;
1072                 if (matchee.equals(right))
1073                         return left;
1074                 return null;
1075         }
1076
1077         /**
1078          * Select the type constraints that involve the selected ASTNode.
1079          * @param relevantConstraintVars
1080          * @param pm
1081          * @return the result
1082          * @throws CoreException
1083          */
1084         private Collection<ITypeConstraint> findRelevantConstraints(Collection<ConstraintVariable> relevantConstraintVars,
1085                                                                                                                                         IProgressMonitor pm) throws CoreException {
1086
1087                 ICompilationUnit[] cus= collectAffectedUnits(new SubProgressMonitor(pm, 100));
1088
1089                 fAllConstraints= getConstraints(cus, new SubProgressMonitor(pm, 900));
1090
1091                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 1000 + fAllConstraints.size());
1092
1093
1094                 if (DEBUG) printCollection("type constraints: ", fAllConstraints); //$NON-NLS-1$
1095                 Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
1096                 for (Iterator<ITypeConstraint> it= fAllConstraints.iterator(); it.hasNext(); ) {
1097                         ITypeConstraint tc= it.next();
1098                         if (tc.isSimpleTypeConstraint()) {
1099                                 SimpleTypeConstraint stc= (SimpleTypeConstraint) tc;
1100                                 if (stc.isDefinesConstraint() || stc.isEqualsConstraint())
1101                                         continue;
1102                                 if (stc.getLeft().equals(stc.getRight()))
1103                                         continue;
1104                                 if (isNull(stc.getLeft()))
1105                                         continue;
1106                                 if (relevantConstraintVars.contains(stc.getLeft()) || relevantConstraintVars.contains(stc.getRight()))
1107                                         result.add(tc);
1108                         } else {
1109                                 CompositeOrTypeConstraint cotc= (CompositeOrTypeConstraint) tc;
1110                                 ITypeConstraint[] components= cotc.getConstraints();
1111                                 for (int i= 0; i < components.length; i++) {
1112                                         ITypeConstraint component= components[i];
1113                                         SimpleTypeConstraint simpleComponent= (SimpleTypeConstraint) component;
1114                                         if (relevantConstraintVars.contains(simpleComponent.getLeft()))
1115                                                 result.add(tc);
1116                                 }
1117                         }
1118                         pm.worked(1);
1119                 }
1120                 if (DEBUG)
1121                         printCollection("selected constraints: ", result); //$NON-NLS-1$
1122                 pm.done();
1123                 return result;
1124         }
1125
1126         /**
1127          * Finds the declaration of the ASTNode in a given AST at a specified offset and with a specified length
1128          * @param root the AST
1129          * @param start start
1130          * @param length length
1131          * @return the declaring node
1132          */
1133         private static ASTNode findDeclaration(final ASTNode root, final int start, final int length){
1134                 ASTNode node= NodeFinder.perform(root, start, length);
1135                 Assert.isTrue(node instanceof SimpleName, String.valueOf(node.getNodeType()));
1136                 Assert.isTrue(root instanceof CompilationUnit, String.valueOf(root.getNodeType()));
1137                 return ((CompilationUnit)root).findDeclaringNode(((SimpleName)node).resolveBinding());
1138         }
1139
1140         // For debugging
1141         static String print(Collection<ITypeBinding> types){
1142                 if (types.isEmpty())
1143                         return "{ }"; //$NON-NLS-1$
1144                 String result = "{ "; //$NON-NLS-1$
1145                 for (Iterator<ITypeBinding> it=types.iterator(); it.hasNext(); ){
1146                         ITypeBinding type= it.next();
1147                         result += type.getQualifiedName();
1148                         if (it.hasNext()){
1149                                 result += ", ";  //$NON-NLS-1$
1150                         } else {
1151                                 result += " }"; //$NON-NLS-1$
1152                         }
1153                 }
1154                 return result;
1155         }
1156
1157
1158         /**
1159          * Determines the set of types for which a set of type constraints is satisfied.
1160          * @param originalType
1161          * @param relevantVars
1162          * @param relevantConstraints
1163          * @param pm
1164          * @return the valid types
1165          * @throws JavaModelException
1166          */
1167         private Collection<ITypeBinding> computeValidTypes(ITypeBinding originalType,
1168                                                                                                         Collection<ConstraintVariable> relevantVars,
1169                                                                                                         Collection<ITypeConstraint> relevantConstraints,
1170                                                                                                         IProgressMonitor pm) throws JavaModelException {
1171
1172                 Collection<ITypeBinding> result= new HashSet<ITypeBinding>();
1173
1174                 Collection<ITypeBinding> allTypes = new HashSet<ITypeBinding>();
1175                 allTypes.addAll(getAllSuperTypes(originalType));
1176
1177                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, allTypes.size());
1178
1179                 for (Iterator<ITypeBinding> it= allTypes.iterator(); it.hasNext(); ) {
1180                         ITypeBinding type= it.next();
1181                         if (isValid(type, relevantVars, relevantConstraints, new SubProgressMonitor(pm, 1))) {
1182                                 result.add(type);
1183                         }
1184                 }
1185                 // "changing" to the original type is a no-op
1186                 result.remove(originalType);
1187
1188                 // TODO: remove all types that are not visible --- need to check visibility in the CUs for
1189                 //       all relevant constraint variables
1190
1191                 pm.done();
1192
1193                 return result;
1194         }
1195
1196         /**
1197          * Determines if a given type satisfies a set of type constraints.
1198          * @param type
1199          * @param relevantVars
1200          * @param constraints
1201          * @param pm
1202          * @return <code>true</code> if a the type satisfies a set of type constraints.
1203          * @throws JavaModelException
1204          */
1205         private boolean isValid(ITypeBinding type,
1206                                                     Collection<ConstraintVariable> relevantVars,
1207                                                     Collection<ITypeConstraint> constraints,
1208                                                         IProgressMonitor pm) throws JavaModelException {
1209                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, constraints.size());
1210                 for (Iterator<ITypeConstraint> it= constraints.iterator(); it.hasNext(); ) {
1211                         ITypeConstraint tc= it.next();
1212                         if (tc instanceof SimpleTypeConstraint) {
1213                                 if (!(isValidSimpleConstraint(type,  relevantVars, (SimpleTypeConstraint) tc)))
1214                                         return false;
1215                         } else if (tc instanceof CompositeOrTypeConstraint) {
1216                                 if (!(isValidOrConstraint(type,  relevantVars, (CompositeOrTypeConstraint) tc)))
1217                                         return false;
1218                         }
1219                         pm.worked(1);
1220                 }
1221                 pm.done();
1222                 return true;
1223         }
1224
1225         private boolean isValidSimpleConstraint(ITypeBinding type,
1226                                                                                         Collection<ConstraintVariable> relevantVars,
1227                                                                                         SimpleTypeConstraint stc){
1228                 if (relevantVars.contains(stc.getLeft())) { // upper bound
1229                         if (!isSubTypeOf(type, findType(stc.getRight()))) {
1230                                 return false;
1231                         }
1232                 }
1233                 return true;
1234         }
1235
1236         private boolean isValidOrConstraint(ITypeBinding type,
1237                                                                                 Collection<ConstraintVariable> relevantVars,
1238                                                                                 CompositeOrTypeConstraint cotc){
1239                 ITypeConstraint[] components= cotc.getConstraints();
1240                 for (int i= 0; i < components.length; i++) {
1241                         if (components[i] instanceof SimpleTypeConstraint) {
1242                                 SimpleTypeConstraint sc= (SimpleTypeConstraint) components[i];
1243                                 if (relevantVars.contains(sc.getLeft())) { // upper bound
1244                                         if (isSubTypeOf(type, findType(sc.getRight())))
1245                                                 return true;
1246                                 } else if (relevantVars.contains(sc.getRight())) { // lower bound
1247                                         if (isSubTypeOf(findType(sc.getLeft()), type))
1248                                                 return true;
1249                                 }
1250                         }
1251                 }
1252                 return false;
1253         }
1254
1255
1256         private ITypeBinding findType(ConstraintVariable cv) {
1257                 return cv.getBinding();
1258         }
1259
1260         /**
1261          * Gather constraints associated with a set of compilation units.
1262          * @param referringCus
1263          * @param pm
1264          * @return the constraints
1265          */
1266         private Collection<ITypeConstraint> getConstraints(ICompilationUnit[] referringCus, IProgressMonitor pm) {
1267                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, referringCus.length);
1268                 Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
1269                 for (int i= 0; i < referringCus.length; i++) {
1270                         result.addAll(getConstraints(referringCus[i]));
1271                         pm.worked(1);
1272                         if (pm.isCanceled())
1273                                 throw new OperationCanceledException();
1274                 }
1275                 pm.done();
1276                 return result;
1277         }
1278
1279         private List<ITypeConstraint> getConstraints(ICompilationUnit unit) {
1280                 if (fConstraintCache.containsKey(unit))
1281                         return fConstraintCache.get(unit);
1282
1283                 CompilationUnit cu= ASTCreator.createAST(unit, null);
1284
1285                 // only generate type constraints for relevant MethodDeclaration subtrees
1286                 if (fMethodBinding != null && fCuToSearchResultGroup.containsKey(unit)){
1287                         SearchResultGroup group= fCuToSearchResultGroup.get(unit);
1288                         ASTNode[] nodes= ASTNodeSearchUtil.getAstNodes(group.getSearchResults(), cu);
1289                         for (int i=0; i < nodes.length; i++){
1290                                 ASTNode node = nodes[i];
1291                                 if (fMethodBinding != null){
1292                                         // find MethodDeclaration above it in the tree
1293                                         ASTNode n= node;
1294                                         while (!(n instanceof MethodDeclaration)){
1295                                                 n = n.getParent();
1296                                         }
1297                                         MethodDeclaration md = (MethodDeclaration)n;
1298                                         md.accept(fCollector);
1299                                 }
1300                         }
1301                 } else {
1302                         cu.accept(fCollector);
1303                 }
1304                 List<ITypeConstraint> constraints= Arrays.asList(fCollector.getConstraints());
1305                 fConstraintCache.put(unit, constraints);
1306                 return constraints;
1307         }
1308
1309         /**
1310          * update a CompilationUnit's imports after changing the type of declarations
1311          * @param astRoot the AST
1312          * @param rootEdit the resulting edit
1313          * @return the type name to use
1314          * @throws CoreException
1315          */
1316         private String updateImports(CompilationUnit astRoot, MultiTextEdit rootEdit) throws CoreException{
1317                 ImportRewrite rewrite= StubUtility.createImportRewrite(astRoot, true);
1318                 String typeName= rewrite.addImport(fSelectedType.getQualifiedName());
1319                 rootEdit.addChild(rewrite.rewriteImports(null));
1320                 return typeName;
1321         }
1322
1323         //      ------------------------------------------------------------------------------------------------- //
1324         // Miscellaneous helper methods
1325
1326         /**
1327          * Returns the Collection of types that can be given to the selected declaration.
1328          * @return return the valid type bindings
1329          */
1330         public Collection<ITypeBinding> getValidTypes() {
1331                 return fValidTypes;
1332         }
1333
1334         public ITypeBinding getOriginalType(){
1335                 return fSelectionTypeBinding;
1336         }
1337
1338         private void setOriginalType(ITypeBinding originalType){
1339                 fSelectionTypeBinding= originalType;
1340                 fSelectedType= findSuperTypeByName(originalType, fSelectedTypeName);
1341         }
1342
1343         public String getTarget() {
1344                 String typeName= fSelectionTypeBinding == null ? "" : fSelectionTypeBinding.getName() + " ";  //$NON-NLS-1$//$NON-NLS-2$
1345                 if (fFieldBinding != null) {
1346                         return typeName + fFieldBinding.getName();
1347                 } else if (fMethodBinding != null) {
1348                         if (fParamIndex == -1) {
1349                                 return typeName + fMethodBinding.getName() + "(...)"; //$NON-NLS-1$
1350                         } else {
1351                                 return typeName + fParamName;
1352                         }
1353                 } else if (fSelectionBinding != null) {
1354                         return typeName + fSelectionBinding.getName();
1355                 } else {
1356                         return typeName;
1357                 }
1358         }
1359
1360         /**
1361          * Returns the Collection<String> of names of types that can be given to the selected declaration.
1362          * (used in tests only)
1363          * @return Collection<String> of names of types that can be given to the selected declaration
1364          */
1365         public Collection<String> getValidTypeNames() {
1366                 Collection<String> typeNames= new ArrayList<String>();
1367                 for (Iterator<ITypeBinding> it= fValidTypes.iterator(); it.hasNext();) {
1368                         ITypeBinding type= it.next();
1369                         typeNames.add(type.getQualifiedName());
1370                 }
1371
1372                 return typeNames;
1373         }
1374
1375         /**
1376          * Find the ASTNode for the given source text selection, if it is a type
1377          * declaration, or null otherwise.
1378          * @param unit The compilation unit in which the selection was made
1379          * @param offset
1380          * @param length
1381          * @return ASTNode
1382          */
1383         private ASTNode getTargetNode(ICompilationUnit unit, int offset, int length) {
1384                 CompilationUnit root= ASTCreator.createAST(unit, null);
1385                 ASTNode node= NodeFinder.perform(root, offset, length);
1386                 return node;
1387         }
1388
1389         /**
1390          * Determines the set of compilation units that may give rise to type constraints that
1391          * we are interested in. This involves searching for overriding/overridden methods,
1392          * method calls, field accesses.
1393          * @param pm the monitor
1394          * @return the affected units
1395          * @throws CoreException
1396          */
1397         private ICompilationUnit[] collectAffectedUnits(IProgressMonitor pm) throws CoreException {
1398                 // BUG: currently, no type constraints are generated for methods that are related
1399                 // but that do not override each other. As a result, we may miss certain relevant
1400                 // variables
1401
1402                 pm.beginTask(RefactoringCoreMessages.ChangeTypeRefactoring_analyzingMessage, 100);
1403
1404                 if (fAffectedUnits != null) {
1405                         if (DEBUG) printCollection("affected units: ", Arrays.asList(fAffectedUnits)); //$NON-NLS-1$
1406                         pm.worked(100);
1407                         return fAffectedUnits;
1408                 }
1409                 if (fMethodBinding != null) {
1410                         if (fMethodBinding != null) {
1411
1412
1413                                 IMethod selectedMethod= (IMethod) fMethodBinding.getJavaElement();
1414                                 if (selectedMethod == null) {
1415                                         // can't happen since we checked it up front in check initial conditions
1416                                         Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_no_method);
1417                                 }
1418
1419                                 // the following code fragment appears to be the source of a memory leak, when
1420                                 // GT is repeatedly applied
1421
1422                                 IMethod root= selectedMethod;
1423                                 if (! root.getDeclaringType().isInterface() && MethodChecks.isVirtual(root)) {
1424                                         final SubProgressMonitor subMonitor= new SubProgressMonitor(pm, 5);
1425                                         IMethod inInterface= MethodChecks.isDeclaredInInterface(root, root.getDeclaringType().newTypeHierarchy(new SubProgressMonitor(subMonitor, 1)), subMonitor);
1426                                         if (inInterface != null && !inInterface.equals(root))
1427                                                 root= inInterface;
1428                                 }
1429
1430                                 // end code fragment
1431
1432                                 IMethod[] rippleMethods= RippleMethodFinder2.getRelatedMethods(
1433                                                 root, new SubProgressMonitor(pm, 15), null);
1434                                 SearchPattern pattern= RefactoringSearchEngine.createOrPattern(
1435                                                 rippleMethods, IJavaSearchConstants.ALL_OCCURRENCES);
1436
1437                                 // To compute the scope we have to use the selected method. Otherwise we
1438                                 // might start from the wrong project.
1439                                 IJavaSearchScope scope= RefactoringScopeFactory.create(selectedMethod);
1440                                 CollectingSearchRequestor csr= new CollectingSearchRequestor();
1441
1442                                 SearchResultGroup[] groups= RefactoringSearchEngine.search(
1443                                         pattern,
1444                                         null,
1445                                         scope,
1446                                         csr,
1447                                         new SubProgressMonitor(pm, 80),
1448                                         new RefactoringStatus()); //TODO: deal with errors from non-CU matches
1449
1450                                 fAffectedUnits= getCus(groups);
1451                         }
1452                 } else if (fFieldBinding != null) {
1453                         IField iField= (IField) fFieldBinding.getJavaElement();
1454                         if (iField == null) {
1455                                 // can't happen since we checked it up front in check initial conditions
1456                                 Assert.isTrue(false, RefactoringCoreMessages.ChangeTypeRefactoring_no_filed);
1457                         }
1458                         SearchPattern pattern= SearchPattern.createPattern(
1459                                         iField, IJavaSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
1460                         IJavaSearchScope scope= RefactoringScopeFactory.create(iField);
1461                         CollectingSearchRequestor csr= new CollectingSearchRequestor();
1462                         SearchResultGroup[] groups=
1463                                 RefactoringSearchEngine.search(pattern, null, scope, csr, new SubProgressMonitor(pm, 100),
1464                                                 new RefactoringStatus()); //TODO: deal with errors from non-CU matches
1465                         fAffectedUnits= getCus(groups);
1466                 } else {
1467                         // otherwise, selection was a local variable and we only have to search the CU
1468                         // containing the selection
1469                         fAffectedUnits= new ICompilationUnit[] { fCu };
1470                 }
1471                 if (DEBUG) {
1472                         System.out.println("Determining affected CUs:"); //$NON-NLS-1$
1473                         for (int i= 0; i < fAffectedUnits.length; i++) {
1474                                 System.out.println("  affected CU: " + fAffectedUnits[i].getElementName()); //$NON-NLS-1$
1475                         }
1476                 }
1477                 pm.done();
1478                 return fAffectedUnits;
1479         }
1480
1481         public void setSelectedType(ITypeBinding type){
1482                 fSelectedType= type;
1483         }
1484
1485         //      -------------------------------------------------------------------------------------------- //
1486         // TODO The following utility methods should probably be moved to another class
1487
1488         /**
1489          * Determines if a constraint variable corresponds to the constant "null".
1490          * @param cv
1491          * @return <code>true</code> if the constraint variable corresponds to the constant "null".
1492          */
1493         private static boolean isNull(ConstraintVariable cv) {
1494                 return cv instanceof ExpressionVariable && ((ExpressionVariable)cv).getExpressionType() == ASTNode.NULL_LITERAL;
1495         }
1496
1497
1498         /*
1499          * For debugging.
1500          */
1501         void printCollection(String title, Collection<?> l) {
1502                 System.out.println(l.size() + " " + title); //$NON-NLS-1$
1503                 for (Iterator<?> it= l.iterator(); it.hasNext();) {
1504                         System.out.println("  " + it.next()); //$NON-NLS-1$
1505                 }
1506         }
1507
1508         /**
1509          * Returns the compilation units that contain the search results.
1510          * @param groups
1511          * @return the CUs
1512          */
1513         private ICompilationUnit[] getCus(SearchResultGroup[] groups) {
1514                 List<ICompilationUnit> result= new ArrayList<ICompilationUnit>(groups.length);
1515                 for (int i= 0; i < groups.length; i++) {
1516                         SearchResultGroup group= groups[i];
1517                         ICompilationUnit cu= group.getCompilationUnit();
1518                         if (cu != null) {
1519                                 result.add(cu);
1520                                 fCuToSearchResultGroup.put(cu, group);
1521                         }
1522                 }
1523                 return result.toArray(new ICompilationUnit[result.size()]);
1524         }
1525
1526     /**
1527          * This always includes the type itself. It will include type
1528          * Object for any type other than Object
1529      * @param type
1530      * @return the super types
1531          */
1532         public Set<ITypeBinding> getAllSuperTypes(ITypeBinding type){
1533                 Set<ITypeBinding> result= new HashSet<ITypeBinding>();
1534                 result.add(type);
1535                 if (type.getSuperclass() != null){
1536                         result.addAll(getAllSuperTypes(type.getSuperclass()));
1537                 }
1538                 ITypeBinding[] interfaces= type.getInterfaces();
1539                 for (int i=0; i < interfaces.length; i++){
1540                         result.addAll(getAllSuperTypes(interfaces[i]));
1541                 }
1542                 if ((type != fObject) && !contains(result, fObject)){
1543                         result.add(fObject);
1544                 }
1545                 return result;
1546         }
1547
1548     private ITypeBinding findSuperTypeByName(ITypeBinding type, String superTypeName){
1549         Set<ITypeBinding> superTypes= getAllSuperTypes(type);
1550         for (Iterator<ITypeBinding> it= superTypes.iterator(); it.hasNext(); ){
1551                 ITypeBinding sup= it.next();
1552                 if (sup.getQualifiedName().equals(superTypeName)){
1553                         return sup;
1554                 }
1555         }
1556         return null;
1557     }
1558
1559         public boolean isSubTypeOf(ITypeBinding type1, ITypeBinding type2){
1560
1561                 // to ensure that, e.g., Comparable<String> is considered a subtype of raw Comparable
1562                 if (type1.isParameterizedType() && type1.getTypeDeclaration().isEqualTo(type2.getTypeDeclaration())){
1563                         return true;
1564                 }
1565                 Set<ITypeBinding> superTypes= getAllSuperTypes(type1);
1566                 return contains(superTypes, type2);
1567         }
1568
1569         private static boolean contains(Collection<ITypeBinding> c, ITypeBinding binding){
1570                 for (Iterator<ITypeBinding> it=c.iterator(); it.hasNext(); ){
1571                         ITypeBinding b = it.next();
1572                         if (Bindings.equals(b, binding)) return true;
1573                 }
1574                 return false;
1575         }
1576
1577         private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
1578                 final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
1579                 if (selection != null) {
1580                         int offset= -1;
1581                         int length= -1;
1582                         final StringTokenizer tokenizer= new StringTokenizer(selection);
1583                         if (tokenizer.hasMoreTokens())
1584                                 offset= Integer.valueOf(tokenizer.nextToken()).intValue();
1585                         if (tokenizer.hasMoreTokens())
1586                                 length= Integer.valueOf(tokenizer.nextToken()).intValue();
1587                         if (offset >= 0 && length >= 0) {
1588                                 fSelectionStart= offset;
1589                                 fSelectionLength= length;
1590                         } else
1591                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
1592                 } else
1593                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
1594                 final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1595                 if (handle != null) {
1596                         final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
1597                         if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
1598                                 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.GENERALIZE_TYPE);
1599                         else
1600                                 fCu= (ICompilationUnit) element;
1601                 } else
1602                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1603                 final String type= arguments.getAttribute(ATTRIBUTE_TYPE);
1604                 if (type != null && !"".equals(type)) //$NON-NLS-1$
1605                         fSelectedTypeName= type;
1606                 else
1607                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_TYPE));
1608                 return new RefactoringStatus();
1609         }
1610 }