]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/generics/InferTypeArgumentsRefactoring.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / generics / InferTypeArgumentsRefactoring.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.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>&lt;?&gt;</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 }