]>
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.generics; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.Arrays; | |
15 | import java.util.HashMap; | |
16 | import java.util.HashSet; | |
17 | import java.util.Iterator; | |
18 | import java.util.List; | |
19 | import java.util.Map; | |
20 | import java.util.Map.Entry; | |
21 | import java.util.Set; | |
22 | ||
23 | import org.eclipse.core.runtime.CoreException; | |
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | import org.eclipse.core.runtime.ISafeRunnable; | |
26 | import org.eclipse.core.runtime.IStatus; | |
27 | import org.eclipse.core.runtime.OperationCanceledException; | |
28 | import org.eclipse.core.runtime.SafeRunner; | |
29 | import org.eclipse.core.runtime.Status; | |
30 | import org.eclipse.core.runtime.SubProgressMonitor; | |
31 | ||
32 | import org.eclipse.core.resources.IFile; | |
33 | ||
34 | import org.eclipse.ltk.core.refactoring.Change; | |
35 | import org.eclipse.ltk.core.refactoring.ChangeDescriptor; | |
36 | import org.eclipse.ltk.core.refactoring.Refactoring; | |
37 | import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; | |
38 | import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; | |
39 | import org.eclipse.ltk.core.refactoring.RefactoringStatus; | |
40 | ||
41 | import org.eclipse.jdt.core.BindingKey; | |
42 | import org.eclipse.jdt.core.ICompilationUnit; | |
43 | import org.eclipse.jdt.core.IJavaElement; | |
44 | import org.eclipse.jdt.core.IJavaProject; | |
45 | import org.eclipse.jdt.core.compiler.IProblem; | |
46 | import org.eclipse.jdt.core.dom.ASTNode; | |
47 | import org.eclipse.jdt.core.dom.ASTParser; | |
48 | import org.eclipse.jdt.core.dom.ASTRequestor; | |
49 | import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; | |
50 | import org.eclipse.jdt.core.dom.CastExpression; | |
51 | import org.eclipse.jdt.core.dom.ClassInstanceCreation; | |
52 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
53 | import org.eclipse.jdt.core.dom.Expression; | |
54 | import org.eclipse.jdt.core.dom.IBinding; | |
55 | import org.eclipse.jdt.core.dom.Name; | |
56 | import org.eclipse.jdt.core.dom.ParameterizedType; | |
57 | import org.eclipse.jdt.core.dom.ParenthesizedExpression; | |
58 | import org.eclipse.jdt.core.dom.SimpleType; | |
59 | import org.eclipse.jdt.core.dom.Type; | |
60 | import org.eclipse.jdt.core.dom.TypeLiteral; | |
61 | import org.eclipse.jdt.core.refactoring.CompilationUnitChange; | |
62 | import org.eclipse.jdt.core.refactoring.IJavaRefactorings; | |
63 | import org.eclipse.jdt.core.refactoring.descriptors.InferTypeArgumentsDescriptor; | |
64 | ||
65 | import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; | |
66 | import org.eclipse.jdt.internal.corext.SourceRangeFactory; | |
67 | import org.eclipse.jdt.internal.corext.refactoring.Checks; | |
68 | import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; | |
69 | import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; | |
70 | import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; | |
71 | import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; | |
72 | import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; | |
73 | import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange; | |
74 | import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsUpdate.CuUpdate; | |
75 | import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; | |
76 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType; | |
77 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.typesets.EnumeratedTypeSet; | |
78 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet; | |
79 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CastVariable2; | |
80 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CollectionElementVariable2; | |
81 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ConstraintVariable2; | |
82 | import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeVariable2; | |
83 | import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; | |
84 | import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; | |
85 | import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; | |
86 | import org.eclipse.jdt.internal.corext.util.JavaModelUtil; | |
87 | import org.eclipse.jdt.internal.corext.util.Messages; | |
88 | ||
89 | import org.eclipse.jdt.ui.JavaElementLabels; | |
90 | ||
91 | import org.eclipse.jdt.internal.ui.IJavaStatusConstants; | |
92 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
93 | import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; | |
94 | import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; | |
95 | ||
96 | public class InferTypeArgumentsRefactoring extends Refactoring { | |
97 | ||
98 | private static final String ATTRIBUTE_CLONE= "clone"; //$NON-NLS-1$ | |
99 | private static final String ATTRIBUTE_LEAVE= "leave"; //$NON-NLS-1$ | |
100 | ||
101 | private static final String REWRITTEN= "InferTypeArgumentsRefactoring.rewritten"; //$NON-NLS-1$ | |
102 | ||
103 | private TextChangeManager fChangeManager; | |
104 | private IJavaElement[] fElements; | |
105 | private InferTypeArgumentsTCModel fTCModel; | |
106 | ||
107 | private boolean fAssumeCloneReturnsSameType; | |
108 | private boolean fLeaveUnconstrainedRaw; | |
109 | ||
110 | /** | |
111 | * Creates a new infer type arguments refactoring. | |
112 | * @param elements the elements to process, or <code>null</code> if invoked by scripting | |
113 | */ | |
114 | public InferTypeArgumentsRefactoring(IJavaElement[] elements) { | |
115 | fElements= elements; | |
116 | } | |
117 | ||
118 | public InferTypeArgumentsRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) { | |
119 | this(null); | |
120 | RefactoringStatus initializeStatus= initialize(arguments); | |
121 | status.merge(initializeStatus); | |
122 | } | |
123 | ||
124 | /* | |
125 | * @see org.eclipse.ltk.core.refactoring.Refactoring#getName() | |
126 | */ | |
127 | @Override | |
128 | public String getName() { | |
129 | return RefactoringCoreMessages.InferTypeArgumentsRefactoring_name; | |
130 | } | |
131 | ||
132 | public void setAssumeCloneReturnsSameType(boolean assume) { | |
133 | fAssumeCloneReturnsSameType= assume; | |
134 | } | |
135 | ||
136 | public boolean getAssumeCloneReturnsSameType() { | |
137 | return fAssumeCloneReturnsSameType; | |
138 | } | |
139 | ||
140 | public void setLeaveUnconstrainedRaw(boolean raw) { | |
141 | fLeaveUnconstrainedRaw= raw; | |
142 | } | |
143 | ||
144 | public boolean getLeaveUnconstrainedRaw() { | |
145 | return fLeaveUnconstrainedRaw; | |
146 | } | |
147 | ||
148 | /* | |
149 | * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor) | |
150 | */ | |
151 | @Override | |
152 | public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { | |
153 | RefactoringStatus result= check15(); | |
154 | pm.done(); | |
155 | return result; | |
156 | } | |
157 | ||
158 | /* | |
159 | * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor) | |
160 | */ | |
161 | @Override | |
162 | public RefactoringStatus checkFinalConditions(final IProgressMonitor pm) throws CoreException, OperationCanceledException { | |
163 | HashMap<IJavaProject, ArrayList<IJavaElement>> projectsToElements= getJavaElementsPerProject(fElements); | |
164 | pm.beginTask("", projectsToElements.size() + 2); //$NON-NLS-1$ | |
165 | final RefactoringStatus result= new RefactoringStatus(); | |
166 | try { | |
167 | fTCModel= new InferTypeArgumentsTCModel(); | |
168 | final InferTypeArgumentsConstraintCreator unitCollector= new InferTypeArgumentsConstraintCreator(fTCModel, fAssumeCloneReturnsSameType); | |
169 | ||
170 | for (Iterator<Entry<IJavaProject, ArrayList<IJavaElement>>> iter= projectsToElements.entrySet().iterator(); iter.hasNext(); ) { | |
171 | Entry<IJavaProject, ArrayList<IJavaElement>> entry= iter.next(); | |
172 | IJavaProject project= entry.getKey(); | |
173 | ArrayList<IJavaElement> javaElementsList= entry.getValue(); | |
174 | IJavaElement[] javaElements= javaElementsList.toArray(new IJavaElement[javaElementsList.size()]); | |
175 | List<ICompilationUnit> cus= Arrays.asList(JavaModelUtil.getAllCompilationUnits(javaElements)); | |
176 | ||
177 | int batchSize= 150; | |
178 | int batches= ((cus.size()-1) / batchSize) + 1; | |
179 | SubProgressMonitor projectMonitor= new SubProgressMonitor(pm, 1); | |
180 | projectMonitor.beginTask("", batches); //$NON-NLS-1$ | |
181 | projectMonitor.setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_building); | |
182 | for (int i= 0; i < batches; i++) { | |
183 | List<ICompilationUnit> batch= cus.subList(i * batchSize, Math.min(cus.size(), (i + 1) * batchSize)); | |
184 | ICompilationUnit[] batchCus= batch.toArray(new ICompilationUnit[batch.size()]); | |
185 | final SubProgressMonitor batchMonitor= new SubProgressMonitor(projectMonitor, 1); | |
186 | batchMonitor.subTask(RefactoringCoreMessages.InferTypeArgumentsRefactoring_calculating_dependencies); | |
187 | ||
188 | ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL); | |
189 | parser.setProject(project); | |
190 | parser.setCompilerOptions(RefactoringASTParser.getCompilerOptions(project)); | |
191 | parser.setResolveBindings(true); | |
192 | parser.createASTs(batchCus, new String[0], new ASTRequestor() { | |
193 | @Override | |
194 | public void acceptAST(final ICompilationUnit source, final CompilationUnit ast) { | |
195 | batchMonitor.subTask(BasicElementLabels.getFileName(source)); | |
196 | ||
197 | SafeRunner.run(new ISafeRunnable() { | |
198 | public void run() throws Exception { | |
199 | IProblem[] problems= ast.getProblems(); | |
200 | for (int p= 0; p < problems.length; p++) { | |
201 | if (problems[p].isError()) { | |
202 | String cuName= JavaElementLabels.getElementLabel(source, JavaElementLabels.CU_QUALIFIED); | |
203 | String msg= Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_error_in_cu_skipped, new Object[] {cuName}); | |
204 | result.addError(msg, JavaStatusContext.create(source, SourceRangeFactory.create(problems[p]))); | |
205 | return; | |
206 | } | |
207 | } | |
208 | ast.accept(unitCollector); | |
209 | } | |
210 | public void handleException(Throwable exception) { | |
211 | String cuName= JavaElementLabels.getElementLabel(source, JavaElementLabels.CU_QUALIFIED); | |
212 | String msg= Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_internal_error, new Object[] {cuName}); | |
213 | JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, msg, null)); | |
214 | String msg2= Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_error_skipped, new Object[] {cuName}); | |
215 | result.addError(msg2, JavaStatusContext.create(source)); | |
216 | } | |
217 | }); | |
218 | ||
219 | fTCModel.newCu(); | |
220 | } | |
221 | @Override | |
222 | public void acceptBinding(String bindingKey, IBinding binding) { | |
223 | //do nothing | |
224 | } | |
225 | }, batchMonitor); | |
226 | } | |
227 | ||
228 | projectMonitor.done(); | |
229 | fTCModel.newCu(); | |
230 | } | |
231 | ||
232 | // Display.getDefault().syncExec(new Runnable() { | |
233 | // public void run() { | |
234 | // MessageDialog.openInformation(Display.getCurrent().getActiveShell(), "Debugging...", "after constraint gen"); | |
235 | // } | |
236 | // }); | |
237 | ||
238 | pm.setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_solving); | |
239 | InferTypeArgumentsConstraintsSolver solver= new InferTypeArgumentsConstraintsSolver(fTCModel); | |
240 | InferTypeArgumentsUpdate updates= solver.solveConstraints(new SubProgressMonitor(pm, 1)); | |
241 | solver= null; //free caches | |
242 | ||
243 | fChangeManager= new TextChangeManager(); | |
244 | rewriteDeclarations(updates, new SubProgressMonitor(pm, 1)); | |
245 | ||
246 | IFile[] filesToModify= ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); | |
247 | result.merge(Checks.validateModifiesFiles(filesToModify, getValidationContext())); | |
248 | return result; | |
249 | } finally { | |
250 | pm.done(); | |
251 | clearGlobalState(); | |
252 | } | |
253 | } | |
254 | ||
255 | private void clearGlobalState() { | |
256 | TypeSet.resetCount(); | |
257 | EnumeratedTypeSet.resetCount(); | |
258 | fTCModel= null; | |
259 | } | |
260 | ||
261 | private HashMap<IJavaProject, ArrayList<IJavaElement>> getJavaElementsPerProject(IJavaElement[] elements) { | |
262 | HashMap<IJavaProject, ArrayList<IJavaElement>> result= new HashMap<IJavaProject, ArrayList<IJavaElement>>(); | |
263 | for (int i= 0; i < elements.length; i++) { | |
264 | IJavaElement element= elements[i]; | |
265 | IJavaProject javaProject= element.getJavaProject(); | |
266 | ArrayList<IJavaElement> javaElements= result.get(javaProject); | |
267 | if (javaElements == null) { | |
268 | javaElements= new ArrayList<IJavaElement>(); | |
269 | result.put(javaProject, javaElements); | |
270 | } | |
271 | javaElements.add(element); | |
272 | } | |
273 | return result; | |
274 | } | |
275 | ||
276 | private RefactoringStatus check15() throws CoreException { | |
277 | RefactoringStatus result= new RefactoringStatus(); | |
278 | HashSet<IJavaProject> checkedProjects= new HashSet<IJavaProject>(); | |
279 | ||
280 | for (int i= 0; i < fElements.length; i++) { | |
281 | IJavaProject javaProject= fElements[i].getJavaProject(); | |
282 | if (! checkedProjects.contains(javaProject)) { | |
283 | if (! JavaModelUtil.is50OrHigher(javaProject)) { | |
284 | String message= Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_not50, BasicElementLabels.getJavaElementName(javaProject.getElementName())); | |
285 | result.addFatalError(message); | |
286 | } else if (! JavaModelUtil.is50OrHigherJRE(javaProject)) { | |
287 | String message= Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_not50Library, BasicElementLabels.getJavaElementName(javaProject.getElementName())); | |
288 | result.addFatalError(message); | |
289 | } | |
290 | checkedProjects.add(javaProject); | |
291 | } | |
292 | } | |
293 | return result; | |
294 | } | |
295 | ||
296 | private void rewriteDeclarations(InferTypeArgumentsUpdate update, IProgressMonitor pm) throws CoreException { | |
297 | HashMap<ICompilationUnit, CuUpdate> updates= update.getUpdates(); | |
298 | ||
299 | Set<Entry<ICompilationUnit, CuUpdate>> entrySet= updates.entrySet(); | |
300 | pm.beginTask("", entrySet.size()); //$NON-NLS-1$ | |
301 | pm.setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_creatingChanges); | |
302 | for (Iterator<Entry<ICompilationUnit, CuUpdate>> iter= entrySet.iterator(); iter.hasNext();) { | |
303 | if (pm.isCanceled()) | |
304 | throw new OperationCanceledException(); | |
305 | ||
306 | Entry<ICompilationUnit, CuUpdate> entry= iter.next(); | |
307 | ICompilationUnit cu= entry.getKey(); | |
308 | pm.worked(1); | |
309 | pm.subTask(BasicElementLabels.getFileName(cu)); | |
310 | ||
311 | CompilationUnitRewrite rewrite= new CompilationUnitRewrite(cu); | |
312 | rewrite.setResolveBindings(false); | |
313 | CuUpdate cuUpdate= entry.getValue(); | |
314 | ||
315 | for (Iterator<CollectionElementVariable2> cvIter= cuUpdate.getDeclarations().iterator(); cvIter.hasNext();) { | |
316 | ConstraintVariable2 cv= cvIter.next(); | |
317 | rewriteConstraintVariable(cv, rewrite, fTCModel, fLeaveUnconstrainedRaw, null); | |
318 | } | |
319 | ||
320 | for (Iterator<CastVariable2> castsIter= cuUpdate.getCastsToRemove().iterator(); castsIter.hasNext();) { | |
321 | CastVariable2 castCv= castsIter.next(); | |
322 | rewriteCastVariable(castCv, rewrite, fTCModel); | |
323 | } | |
324 | ||
325 | CompilationUnitChange change= rewrite.createChange(true); | |
326 | if (change != null) { | |
327 | fChangeManager.manage(cu, change); | |
328 | } | |
329 | } | |
330 | ||
331 | } | |
332 | ||
333 | public static ParameterizedType[] inferArguments(SimpleType[] types, InferTypeArgumentsUpdate update, InferTypeArgumentsTCModel model, CompilationUnitRewrite rewrite) { | |
334 | for (int i= 0; i < types.length; i++) { | |
335 | types[i].setProperty(REWRITTEN, null); | |
336 | } | |
337 | List<ParameterizedType> result= new ArrayList<ParameterizedType>(); | |
338 | HashMap<ICompilationUnit, CuUpdate> updates= update.getUpdates(); | |
339 | Set<Entry<ICompilationUnit, CuUpdate>> entrySet= updates.entrySet(); | |
340 | for (Iterator<Entry<ICompilationUnit, CuUpdate>> iter= entrySet.iterator(); iter.hasNext();) { | |
341 | ||
342 | Entry<ICompilationUnit, CuUpdate> entry= iter.next(); | |
343 | ||
344 | rewrite.setResolveBindings(false); | |
345 | CuUpdate cuUpdate= entry.getValue(); | |
346 | ||
347 | for (Iterator<CollectionElementVariable2> cvIter= cuUpdate.getDeclarations().iterator(); cvIter.hasNext();) { | |
348 | ConstraintVariable2 cv= cvIter.next(); | |
349 | ParameterizedType newNode= rewriteConstraintVariable(cv, rewrite, model, false, types); | |
350 | if (newNode != null) | |
351 | result.add(newNode); | |
352 | } | |
353 | } | |
354 | return result.toArray(new ParameterizedType[result.size()]); | |
355 | } | |
356 | ||
357 | private static ParameterizedType rewriteConstraintVariable(ConstraintVariable2 cv, CompilationUnitRewrite rewrite, InferTypeArgumentsTCModel tCModel, boolean leaveUnconstraindRaw, SimpleType[] types) { | |
358 | if (cv instanceof CollectionElementVariable2) { | |
359 | ConstraintVariable2 parentElement= ((CollectionElementVariable2) cv).getParentConstraintVariable(); | |
360 | if (parentElement instanceof TypeVariable2) { | |
361 | TypeVariable2 typeCv= (TypeVariable2) parentElement; | |
362 | return rewriteTypeVariable(typeCv, rewrite, tCModel, leaveUnconstraindRaw, types); | |
363 | } else { | |
364 | //only rewrite type variables | |
365 | } | |
366 | } | |
367 | return null; | |
368 | } | |
369 | ||
370 | private static ParameterizedType rewriteTypeVariable(TypeVariable2 typeCv, CompilationUnitRewrite rewrite, InferTypeArgumentsTCModel tCModel, boolean leaveUnconstraindRaw, SimpleType[] types) { | |
371 | ASTNode node= typeCv.getRange().getNode(rewrite.getRoot()); | |
372 | if (node instanceof Name && node.getParent() instanceof Type) { | |
373 | Type originalType= (Type) node.getParent(); | |
374 | ||
375 | if (types != null && !has(types, originalType)) | |
376 | return null; | |
377 | ||
378 | // Must rewrite all type arguments in one batch. Do the rewrite when the first one is encountered; skip the others. | |
379 | Object rewritten= originalType.getProperty(REWRITTEN); | |
380 | if (rewritten == REWRITTEN) | |
381 | return null; | |
382 | originalType.setProperty(REWRITTEN, REWRITTEN); | |
383 | ||
384 | ArrayList<CollectionElementVariable2> typeArgumentCvs= getTypeArgumentCvs(typeCv, tCModel); | |
385 | Type[] typeArguments= getTypeArguments(originalType, typeArgumentCvs, rewrite, tCModel, leaveUnconstraindRaw); | |
386 | if (typeArguments == null) | |
387 | return null; | |
388 | ||
389 | Type movingType= (Type) rewrite.getASTRewrite().createMoveTarget(originalType); | |
390 | ParameterizedType newType= rewrite.getAST().newParameterizedType(movingType); | |
391 | ||
392 | for (int i= 0; i < typeArguments.length; i++) { | |
393 | newType.typeArguments().add(typeArguments[i]); | |
394 | } | |
395 | ||
396 | rewrite.getASTRewrite().replace(originalType, newType, rewrite.createGroupDescription(RefactoringCoreMessages.InferTypeArgumentsRefactoring_addTypeArguments)); | |
397 | return newType; | |
398 | } else {//TODO: other node types? | |
399 | return null; | |
400 | } | |
401 | } | |
402 | ||
403 | private static boolean has(SimpleType[] types, Type originalType) { | |
404 | for (int i= 0; i < types.length; i++) { | |
405 | if (types[i] == originalType) | |
406 | return true; | |
407 | } | |
408 | return false; | |
409 | } | |
410 | ||
411 | /** | |
412 | * @param baseType the base type | |
413 | * @param typeArgumentCvs type argument constraint variables | |
414 | * @param rewrite the cu rewrite | |
415 | * @param tCModel the type constraints model | |
416 | * @param leaveUnconstraindRaw <code>true</code> to keep unconstrained type references raw, | |
417 | * <code>false</code> to infer <code><?></code> if possible | |
418 | * @return the new type arguments, or <code>null</code> iff an argument could not be inferred | |
419 | */ | |
420 | private static Type[] getTypeArguments(Type baseType, ArrayList<CollectionElementVariable2> typeArgumentCvs, CompilationUnitRewrite rewrite, InferTypeArgumentsTCModel tCModel, boolean leaveUnconstraindRaw) { | |
421 | if (typeArgumentCvs.size() == 0) | |
422 | return null; | |
423 | ||
424 | Type[] typeArguments= new Type[typeArgumentCvs.size()]; | |
425 | for (int i= 0; i < typeArgumentCvs.size(); i++) { | |
426 | CollectionElementVariable2 elementCv= typeArgumentCvs.get(i); | |
427 | Type typeArgument; | |
428 | TType chosenType= InferTypeArgumentsConstraintsSolver.getChosenType(elementCv); | |
429 | if (chosenType != null) { | |
430 | if (chosenType.isWildcardType() && ! unboundedWildcardAllowed(baseType)) | |
431 | return null; // can't e.g. write "new ArrayList<?>()". | |
432 | if (chosenType.isParameterizedType()) // workaround for bug 99124 | |
433 | chosenType= chosenType.getTypeDeclaration(); | |
434 | BindingKey bindingKey= new BindingKey(chosenType.getBindingKey()); | |
435 | typeArgument= rewrite.getImportRewrite().addImportFromSignature(bindingKey.toSignature(), rewrite.getAST()); | |
436 | ArrayList<CollectionElementVariable2> nestedTypeArgumentCvs= getTypeArgumentCvs(elementCv, tCModel); | |
437 | Type[] nestedTypeArguments= getTypeArguments(typeArgument, nestedTypeArgumentCvs, rewrite, tCModel, leaveUnconstraindRaw); //recursion | |
438 | if (nestedTypeArguments != null) { | |
439 | ParameterizedType parameterizedType= rewrite.getAST().newParameterizedType(typeArgument); | |
440 | for (int j= 0; j < nestedTypeArguments.length; j++) | |
441 | parameterizedType.typeArguments().add(nestedTypeArguments[j]); | |
442 | typeArgument= parameterizedType; | |
443 | } | |
444 | ||
445 | } else { // couldn't infer an element type (no constraints) | |
446 | if (leaveUnconstraindRaw) { | |
447 | // every guess could be wrong => leave the whole thing raw | |
448 | return null; | |
449 | } else { | |
450 | if (unboundedWildcardAllowed(baseType)) { | |
451 | typeArgument= rewrite.getAST().newWildcardType(); | |
452 | } else { | |
453 | String object= rewrite.getImportRewrite().addImport("java.lang.Object"); //$NON-NLS-1$ | |
454 | typeArgument= (Type) rewrite.getASTRewrite().createStringPlaceholder(object, ASTNode.SIMPLE_TYPE); | |
455 | } | |
456 | } | |
457 | // ASTNode baseTypeParent= baseType.getParent(); | |
458 | // if (baseTypeParent instanceof ClassInstanceCreation) { | |
459 | // //No ? allowed. Take java.lang.Object. | |
460 | // typeArgument= rewrite.getAST().newSimpleType(rewrite.getAST().newName(rewrite.getImportRewrite().addImport("java.lang.Object"))); //$NON-NLS-1$ | |
461 | // } else if (baseTypeParent instanceof ArrayCreation || baseTypeParent instanceof InstanceofExpression) { | |
462 | // //Only ? allowed. | |
463 | // typeArgument= rewrite.getAST().newWildcardType(); | |
464 | // } else { | |
465 | // //E.g. field type: can put anything. Choosing ? in order to be most constraining. | |
466 | // typeArgument= rewrite.getAST().newWildcardType(); | |
467 | // } | |
468 | } | |
469 | typeArguments[i]= typeArgument; | |
470 | } | |
471 | return typeArguments; | |
472 | } | |
473 | ||
474 | private static ArrayList<CollectionElementVariable2> getTypeArgumentCvs(ConstraintVariable2 baseCv, InferTypeArgumentsTCModel tCModel) { | |
475 | Map<String, CollectionElementVariable2> elementCvs= tCModel.getElementVariables(baseCv); | |
476 | ArrayList<CollectionElementVariable2> typeArgumentCvs= new ArrayList<CollectionElementVariable2>(); | |
477 | for (Iterator<CollectionElementVariable2> iter= elementCvs.values().iterator(); iter.hasNext();) { | |
478 | CollectionElementVariable2 elementCv= iter.next(); | |
479 | int index= elementCv.getDeclarationTypeVariableIndex(); | |
480 | if (index != CollectionElementVariable2.NOT_DECLARED_TYPE_VARIABLE_INDEX) { | |
481 | while (index >= typeArgumentCvs.size()) | |
482 | typeArgumentCvs.add(null); // fill with null until set(index, ..) is possible | |
483 | typeArgumentCvs.set(index, elementCv); | |
484 | } | |
485 | } | |
486 | return typeArgumentCvs; | |
487 | } | |
488 | ||
489 | private static boolean unboundedWildcardAllowed(Type originalType) { | |
490 | ASTNode parent= originalType.getParent(); | |
491 | while (parent instanceof Type) | |
492 | parent= parent.getParent(); | |
493 | ||
494 | if (parent instanceof ClassInstanceCreation) { | |
495 | return false; | |
496 | } else if (parent instanceof AbstractTypeDeclaration) { | |
497 | return false; | |
498 | } else if (parent instanceof TypeLiteral) { | |
499 | return false; | |
500 | } | |
501 | return true; | |
502 | } | |
503 | ||
504 | private static ASTNode rewriteCastVariable(CastVariable2 castCv, CompilationUnitRewrite rewrite, InferTypeArgumentsTCModel tCModel) {//, List positionGroups) { | |
505 | ASTNode node= castCv.getRange().getNode(rewrite.getRoot()); | |
506 | ||
507 | ConstraintVariable2 expressionVariable= castCv.getExpressionVariable(); | |
508 | ConstraintVariable2 methodReceiverCv= tCModel.getMethodReceiverCv(expressionVariable); | |
509 | if (methodReceiverCv != null) { | |
510 | TType chosenReceiverType= InferTypeArgumentsConstraintsSolver.getChosenType(methodReceiverCv); | |
511 | if (chosenReceiverType == null) | |
512 | return null; | |
513 | else if (! InferTypeArgumentsTCModel.isAGenericType(chosenReceiverType)) | |
514 | return null; | |
515 | else if (hasUnboundElement(methodReceiverCv, tCModel)) | |
516 | return null; | |
517 | } | |
518 | ||
519 | CastExpression castExpression= (CastExpression) node; | |
520 | Expression expression= castExpression.getExpression(); | |
521 | ASTNode nodeToReplace; | |
522 | if (castExpression.getParent() instanceof ParenthesizedExpression) | |
523 | nodeToReplace= castExpression.getParent(); | |
524 | else | |
525 | nodeToReplace= castExpression; | |
526 | ||
527 | Expression newExpression= (Expression) rewrite.getASTRewrite().createMoveTarget(expression); | |
528 | rewrite.getASTRewrite().replace(nodeToReplace, newExpression, rewrite.createGroupDescription(RefactoringCoreMessages.InferTypeArgumentsRefactoring_removeCast)); | |
529 | rewrite.getImportRemover().registerRemovedNode(nodeToReplace); | |
530 | return newExpression; | |
531 | } | |
532 | ||
533 | private static boolean hasUnboundElement(ConstraintVariable2 methodReceiverCv, InferTypeArgumentsTCModel tCModel) { | |
534 | ArrayList<CollectionElementVariable2> typeArgumentCvs= getTypeArgumentCvs(methodReceiverCv, tCModel); | |
535 | for (Iterator<CollectionElementVariable2> iter= typeArgumentCvs.iterator(); iter.hasNext();) { | |
536 | CollectionElementVariable2 elementCv= iter.next(); | |
537 | TType chosenElementType= InferTypeArgumentsConstraintsSolver.getChosenType(elementCv); | |
538 | if (chosenElementType == null) | |
539 | return true; | |
540 | } | |
541 | return false; | |
542 | } | |
543 | ||
544 | /* | |
545 | * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) | |
546 | */ | |
547 | @Override | |
548 | public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { | |
549 | pm.beginTask("", 1); //$NON-NLS-1$ | |
550 | try { | |
551 | DynamicValidationStateChange result= new DynamicValidationStateChange(RefactoringCoreMessages.InferTypeArgumentsRefactoring_name, fChangeManager.getAllChanges()) { | |
552 | ||
553 | @Override | |
554 | public final ChangeDescriptor getDescriptor() { | |
555 | final Map<String, String> arguments= new HashMap<String, String>(); | |
556 | final IJavaProject project= getSingleProject(); | |
557 | final String description= RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description; | |
558 | final String header= project != null ? Messages.format(RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description_project, BasicElementLabels.getJavaElementName(project.getElementName())) : RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description; | |
559 | final String name= project != null ? project.getElementName() : null; | |
560 | final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(name, this, header); | |
561 | final String[] settings= new String[fElements.length]; | |
562 | for (int index= 0; index < settings.length; index++) | |
563 | settings[index]= JavaElementLabels.getTextLabel(fElements[index], JavaElementLabels.ALL_FULLY_QUALIFIED); | |
564 | comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.InferTypeArgumentsRefactoring_original_elements, settings)); | |
565 | if (fAssumeCloneReturnsSameType) | |
566 | comment.addSetting(RefactoringCoreMessages.InferTypeArgumentsRefactoring_assume_clone); | |
567 | if (fLeaveUnconstrainedRaw) | |
568 | comment.addSetting(RefactoringCoreMessages.InferTypeArgumentsRefactoring_leave_unconstrained); | |
569 | final InferTypeArgumentsDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInferTypeArgumentsDescriptor(name, description, comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE); | |
570 | for (int index= 0; index < fElements.length; index++) | |
571 | arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (index + 1), JavaRefactoringDescriptorUtil.elementToHandle(name, fElements[index])); | |
572 | arguments.put(ATTRIBUTE_CLONE, Boolean.valueOf(fAssumeCloneReturnsSameType).toString()); | |
573 | arguments.put(ATTRIBUTE_LEAVE, Boolean.valueOf(fLeaveUnconstrainedRaw).toString()); | |
574 | return new RefactoringChangeDescriptor(descriptor); | |
575 | } | |
576 | }; | |
577 | return result; | |
578 | } finally { | |
579 | pm.done(); | |
580 | } | |
581 | } | |
582 | ||
583 | private IJavaProject getSingleProject() { | |
584 | IJavaProject first= null; | |
585 | for (int index= 0; index < fElements.length; index++) { | |
586 | final IJavaProject project= fElements[index].getJavaProject(); | |
587 | if (project != null) { | |
588 | if (first == null) | |
589 | first= project; | |
590 | else if (!project.equals(first)) | |
591 | return null; | |
592 | } | |
593 | } | |
594 | return first; | |
595 | } | |
596 | ||
597 | private RefactoringStatus initialize(JavaRefactoringArguments arguments) { | |
598 | final String clone= arguments.getAttribute(ATTRIBUTE_CLONE); | |
599 | if (clone != null) { | |
600 | fAssumeCloneReturnsSameType= Boolean.valueOf(clone).booleanValue(); | |
601 | } else | |
602 | return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_CLONE)); | |
603 | final String leave= arguments.getAttribute(ATTRIBUTE_LEAVE); | |
604 | if (leave != null) { | |
605 | fLeaveUnconstrainedRaw= Boolean.valueOf(leave).booleanValue(); | |
606 | } else | |
607 | return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_LEAVE)); | |
608 | int count= 1; | |
609 | final List<IJavaElement> elements= new ArrayList<IJavaElement>(); | |
610 | String handle= null; | |
611 | String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count; | |
612 | final RefactoringStatus status= new RefactoringStatus(); | |
613 | while ((handle= arguments.getAttribute(attribute)) != null) { | |
614 | final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false); | |
615 | if (element == null || !element.exists()) | |
616 | return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INFER_TYPE_ARGUMENTS); | |
617 | else | |
618 | elements.add(element); | |
619 | count++; | |
620 | attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count; | |
621 | } | |
622 | fElements= elements.toArray(new IJavaElement[elements.size()]); | |
623 | if (elements.isEmpty()) | |
624 | return JavaRefactoringDescriptorUtil.createInputFatalStatus(null, getName(), IJavaRefactorings.INFER_TYPE_ARGUMENTS); | |
625 | if (!status.isOK()) | |
626 | return status; | |
627 | return new RefactoringStatus(); | |
628 | } | |
629 | } |