]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
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 | } |