001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.generics;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.HashMap;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020: import java.util.Map.Entry;
021:
022: import org.eclipse.core.runtime.CoreException;
023: import org.eclipse.core.runtime.IProgressMonitor;
024: import org.eclipse.core.runtime.ISafeRunnable;
025: import org.eclipse.core.runtime.IStatus;
026: import org.eclipse.core.runtime.OperationCanceledException;
027: import org.eclipse.core.runtime.SafeRunner;
028: import org.eclipse.core.runtime.Status;
029: import org.eclipse.core.runtime.SubProgressMonitor;
030:
031: import org.eclipse.core.resources.IFile;
032:
033: import org.eclipse.ltk.core.refactoring.Change;
034: import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
035: import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
036: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
037: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
038: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
039:
040: import org.eclipse.jdt.core.BindingKey;
041: import org.eclipse.jdt.core.ICompilationUnit;
042: import org.eclipse.jdt.core.IJavaElement;
043: import org.eclipse.jdt.core.IJavaProject;
044: import org.eclipse.jdt.core.compiler.IProblem;
045: import org.eclipse.jdt.core.dom.AST;
046: import org.eclipse.jdt.core.dom.ASTNode;
047: import org.eclipse.jdt.core.dom.ASTParser;
048: import org.eclipse.jdt.core.dom.ASTRequestor;
049: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
050: import org.eclipse.jdt.core.dom.CastExpression;
051: import org.eclipse.jdt.core.dom.ClassInstanceCreation;
052: import org.eclipse.jdt.core.dom.CompilationUnit;
053: import org.eclipse.jdt.core.dom.Expression;
054: import org.eclipse.jdt.core.dom.IBinding;
055: import org.eclipse.jdt.core.dom.Name;
056: import org.eclipse.jdt.core.dom.ParameterizedType;
057: import org.eclipse.jdt.core.dom.ParenthesizedExpression;
058: import org.eclipse.jdt.core.dom.SimpleType;
059: import org.eclipse.jdt.core.dom.Type;
060: import org.eclipse.jdt.core.dom.TypeLiteral;
061: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
062: import org.eclipse.jdt.core.refactoring.descriptors.InferTypeArgumentsDescriptor;
063:
064: import org.eclipse.jdt.internal.corext.SourceRange;
065: import org.eclipse.jdt.internal.corext.refactoring.Checks;
066: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
067: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
068: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
069: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
070: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
071: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
072: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
073: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
074: import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsUpdate.CuUpdate;
075: import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
076: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
077: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.typesets.EnumeratedTypeSet;
078: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet;
079: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CastVariable2;
080: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CollectionElementVariable2;
081: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ConstraintVariable2;
082: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeVariable2;
083: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
084: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
085: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
086: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
087: import org.eclipse.jdt.internal.corext.util.Messages;
088:
089: import org.eclipse.jdt.ui.JavaElementLabels;
090:
091: import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
092: import org.eclipse.jdt.internal.ui.JavaPlugin;
093:
094: public class InferTypeArgumentsRefactoring extends
095: ScriptableRefactoring {
096:
097: private static final String ATTRIBUTE_CLONE = "clone"; //$NON-NLS-1$
098: private static final String ATTRIBUTE_LEAVE = "leave"; //$NON-NLS-1$
099:
100: private static final String REWRITTEN = "InferTypeArgumentsRefactoring.rewritten"; //$NON-NLS-1$
101:
102: private TextChangeManager fChangeManager;
103: private IJavaElement[] fElements;
104: private InferTypeArgumentsTCModel fTCModel;
105:
106: private boolean fAssumeCloneReturnsSameType;
107: private boolean fLeaveUnconstrainedRaw;
108:
109: /**
110: * Creates a new infer type arguments refactoring.
111: * @param elements the elements to process, or <code>null</code> if invoked by scripting
112: */
113: public InferTypeArgumentsRefactoring(IJavaElement[] elements) {
114: fElements = elements;
115: }
116:
117: /*
118: * @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
119: */
120: public String getName() {
121: return RefactoringCoreMessages.InferTypeArgumentsRefactoring_name;
122: }
123:
124: public void setAssumeCloneReturnsSameType(boolean assume) {
125: fAssumeCloneReturnsSameType = assume;
126: }
127:
128: public boolean getAssumeCloneReturnsSameType() {
129: return fAssumeCloneReturnsSameType;
130: }
131:
132: public void setLeaveUnconstrainedRaw(boolean raw) {
133: fLeaveUnconstrainedRaw = raw;
134: }
135:
136: public boolean getLeaveUnconstrainedRaw() {
137: return fLeaveUnconstrainedRaw;
138: }
139:
140: /*
141: * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
142: */
143: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
144: throws CoreException, OperationCanceledException {
145: RefactoringStatus result = check15();
146: pm.done();
147: return result;
148: }
149:
150: /*
151: * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor)
152: */
153: public RefactoringStatus checkFinalConditions(
154: final IProgressMonitor pm) throws CoreException,
155: OperationCanceledException {
156: HashMap/*<IJavaProject, List<JavaElement>>*/projectsToElements = getJavaElementsPerProject(fElements);
157: pm.beginTask("", projectsToElements.size() + 2); //$NON-NLS-1$
158: final RefactoringStatus result = new RefactoringStatus();
159: try {
160: fTCModel = new InferTypeArgumentsTCModel();
161: final InferTypeArgumentsConstraintCreator unitCollector = new InferTypeArgumentsConstraintCreator(
162: fTCModel, fAssumeCloneReturnsSameType);
163:
164: for (Iterator iter = projectsToElements.entrySet()
165: .iterator(); iter.hasNext();) {
166: Entry entry = (Entry) iter.next();
167: IJavaProject project = (IJavaProject) entry.getKey();
168: List javaElementsList = (List) entry.getValue();
169: IJavaElement[] javaElements = (IJavaElement[]) javaElementsList
170: .toArray(new IJavaElement[javaElementsList
171: .size()]);
172: List cus = Arrays.asList(JavaModelUtil
173: .getAllCompilationUnits(javaElements));
174:
175: int batchSize = 150;
176: int batches = ((cus.size() - 1) / batchSize) + 1;
177: SubProgressMonitor projectMonitor = new SubProgressMonitor(
178: pm, 1);
179: projectMonitor.beginTask("", batches); //$NON-NLS-1$
180: projectMonitor
181: .setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_building);
182: for (int i = 0; i < batches; i++) {
183: List batch = cus.subList(i * batchSize, Math.min(
184: cus.size(), (i + 1) * batchSize));
185: ICompilationUnit[] batchCus = (ICompilationUnit[]) batch
186: .toArray(new ICompilationUnit[batch.size()]);
187: final SubProgressMonitor batchMonitor = new SubProgressMonitor(
188: projectMonitor, 1);
189: batchMonitor
190: .subTask(RefactoringCoreMessages.InferTypeArgumentsRefactoring_calculating_dependencies);
191:
192: ASTParser parser = ASTParser.newParser(AST.JLS3);
193: parser.setProject(project);
194: parser.setCompilerOptions(RefactoringASTParser
195: .getCompilerOptions(project));
196: parser.setResolveBindings(true);
197: parser.createASTs(batchCus, new String[0],
198: new ASTRequestor() {
199: public void acceptAST(
200: final ICompilationUnit source,
201: final CompilationUnit ast) {
202: batchMonitor.subTask(source
203: .getElementName());
204:
205: SafeRunner.run(new ISafeRunnable() {
206: public void run()
207: throws Exception {
208: IProblem[] problems = ast
209: .getProblems();
210: for (int p = 0; p < problems.length; p++) {
211: if (problems[p]
212: .isError()) {
213: String cuName = JavaElementLabels
214: .getElementLabel(
215: source,
216: JavaElementLabels.CU_QUALIFIED);
217: String msg = Messages
218: .format(
219: RefactoringCoreMessages.InferTypeArgumentsRefactoring_error_in_cu_skipped,
220: new Object[] { cuName });
221: result
222: .addError(
223: msg,
224: JavaStatusContext
225: .create(
226: source,
227: new SourceRange(
228: problems[p])));
229: return;
230: }
231: }
232: ast.accept(unitCollector);
233: }
234:
235: public void handleException(
236: Throwable exception) {
237: String cuName = JavaElementLabels
238: .getElementLabel(
239: source,
240: JavaElementLabels.CU_QUALIFIED);
241: String msg = Messages
242: .format(
243: RefactoringCoreMessages.InferTypeArgumentsRefactoring_internal_error,
244: new Object[] { cuName });
245: JavaPlugin
246: .log(new Status(
247: IStatus.ERROR,
248: JavaPlugin
249: .getPluginId(),
250: IJavaStatusConstants.INTERNAL_ERROR,
251: msg, null));
252: String msg2 = Messages
253: .format(
254: RefactoringCoreMessages.InferTypeArgumentsRefactoring_error_skipped,
255: new Object[] { cuName });
256: result
257: .addError(
258: msg2,
259: JavaStatusContext
260: .create(source));
261: }
262: });
263:
264: fTCModel.newCu();
265: }
266:
267: public void acceptBinding(
268: String bindingKey,
269: IBinding binding) {
270: //do nothing
271: }
272: }, batchMonitor);
273: }
274:
275: projectMonitor.done();
276: fTCModel.newCu();
277: }
278:
279: // Display.getDefault().syncExec(new Runnable() {
280: // public void run() {
281: // MessageDialog.openInformation(Display.getCurrent().getActiveShell(), "Debugging...", "after constraint gen");
282: // }
283: // });
284:
285: pm
286: .setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_solving);
287: InferTypeArgumentsConstraintsSolver solver = new InferTypeArgumentsConstraintsSolver(
288: fTCModel);
289: InferTypeArgumentsUpdate updates = solver
290: .solveConstraints(new SubProgressMonitor(pm, 1));
291: solver = null; //free caches
292:
293: fChangeManager = new TextChangeManager();
294: rewriteDeclarations(updates, new SubProgressMonitor(pm, 1));
295:
296: IFile[] filesToModify = ResourceUtil
297: .getFiles(fChangeManager.getAllCompilationUnits());
298: result.merge(Checks.validateModifiesFiles(filesToModify,
299: getValidationContext()));
300: return result;
301: } finally {
302: pm.done();
303: clearGlobalState();
304: }
305: }
306:
307: private void clearGlobalState() {
308: TypeSet.resetCount();
309: EnumeratedTypeSet.resetCount();
310: fTCModel = null;
311: }
312:
313: private HashMap getJavaElementsPerProject(IJavaElement[] elements) {
314: HashMap/*<IJavaProject, List<JavaElement>>*/result = new HashMap/*<IJavaProject, List<JavaElement>>*/();
315: for (int i = 0; i < fElements.length; i++) {
316: IJavaElement element = fElements[i];
317: IJavaProject javaProject = element.getJavaProject();
318: ArrayList javaElements = (ArrayList) result
319: .get(javaProject);
320: if (javaElements == null) {
321: javaElements = new ArrayList();
322: result.put(javaProject, javaElements);
323: }
324: javaElements.add(element);
325: }
326: return result;
327: }
328:
329: private RefactoringStatus check15() throws CoreException {
330: RefactoringStatus result = new RefactoringStatus();
331: HashSet/*<IJavaProject>*/checkedProjects = new HashSet/*<IJavaProject>*/();
332:
333: for (int i = 0; i < fElements.length; i++) {
334: IJavaProject javaProject = fElements[i].getJavaProject();
335: if (!checkedProjects.contains(javaProject)) {
336: if (!JavaModelUtil.is50OrHigher(javaProject)) {
337: String message = Messages
338: .format(
339: RefactoringCoreMessages.InferTypeArgumentsRefactoring_not50,
340: javaProject.getElementName());
341: result.addFatalError(message);
342: } else if (!JavaModelUtil.is50OrHigherJRE(javaProject)) {
343: String message = Messages
344: .format(
345: RefactoringCoreMessages.InferTypeArgumentsRefactoring_not50Library,
346: javaProject.getElementName());
347: result.addFatalError(message);
348: }
349: checkedProjects.add(javaProject);
350: }
351: }
352: return result;
353: }
354:
355: private void rewriteDeclarations(InferTypeArgumentsUpdate update,
356: IProgressMonitor pm) throws CoreException {
357: HashMap/*<ICompilationUnit, CuUpdate>*/updates = update
358: .getUpdates();
359:
360: Set entrySet = updates.entrySet();
361: pm.beginTask("", entrySet.size()); //$NON-NLS-1$
362: pm
363: .setTaskName(RefactoringCoreMessages.InferTypeArgumentsRefactoring_creatingChanges);
364: for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
365: if (pm.isCanceled())
366: throw new OperationCanceledException();
367:
368: Map.Entry entry = (Map.Entry) iter.next();
369: ICompilationUnit cu = (ICompilationUnit) entry.getKey();
370: pm.worked(1);
371: pm.subTask(cu.getElementName());
372:
373: CompilationUnitRewrite rewrite = new CompilationUnitRewrite(
374: cu);
375: rewrite.setResolveBindings(false);
376: CuUpdate cuUpdate = (CuUpdate) entry.getValue();
377:
378: for (Iterator cvIter = cuUpdate.getDeclarations()
379: .iterator(); cvIter.hasNext();) {
380: ConstraintVariable2 cv = (ConstraintVariable2) cvIter
381: .next();
382: rewriteConstraintVariable(cv, rewrite, fTCModel,
383: fLeaveUnconstrainedRaw, null);
384: }
385:
386: for (Iterator castsIter = cuUpdate.getCastsToRemove()
387: .iterator(); castsIter.hasNext();) {
388: CastVariable2 castCv = (CastVariable2) castsIter.next();
389: rewriteCastVariable(castCv, rewrite, fTCModel);
390: }
391:
392: CompilationUnitChange change = rewrite.createChange();
393: if (change != null) {
394: fChangeManager.manage(cu, change);
395: }
396: }
397:
398: }
399:
400: public static ParameterizedType[] inferArguments(
401: SimpleType[] types, InferTypeArgumentsUpdate update,
402: InferTypeArgumentsTCModel model,
403: CompilationUnitRewrite rewrite) {
404: for (int i = 0; i < types.length; i++) {
405: types[i].setProperty(REWRITTEN, null);
406: }
407: List result = new ArrayList();
408: HashMap/*<ICompilationUnit, CuUpdate>*/updates = update
409: .getUpdates();
410: Set entrySet = updates.entrySet();
411: for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
412:
413: Map.Entry entry = (Map.Entry) iter.next();
414:
415: rewrite.setResolveBindings(false);
416: CuUpdate cuUpdate = (CuUpdate) entry.getValue();
417:
418: for (Iterator cvIter = cuUpdate.getDeclarations()
419: .iterator(); cvIter.hasNext();) {
420: ConstraintVariable2 cv = (ConstraintVariable2) cvIter
421: .next();
422: ParameterizedType newNode = rewriteConstraintVariable(
423: cv, rewrite, model, false, types);
424: if (newNode != null)
425: result.add(newNode);
426: }
427: }
428: return (ParameterizedType[]) result
429: .toArray(new ParameterizedType[result.size()]);
430: }
431:
432: private static ParameterizedType rewriteConstraintVariable(
433: ConstraintVariable2 cv, CompilationUnitRewrite rewrite,
434: InferTypeArgumentsTCModel tCModel,
435: boolean leaveUnconstraindRaw, SimpleType[] types) {
436: if (cv instanceof CollectionElementVariable2) {
437: ConstraintVariable2 parentElement = ((CollectionElementVariable2) cv)
438: .getParentConstraintVariable();
439: if (parentElement instanceof TypeVariable2) {
440: TypeVariable2 typeCv = (TypeVariable2) parentElement;
441: return rewriteTypeVariable(typeCv, rewrite, tCModel,
442: leaveUnconstraindRaw, types);
443: } else {
444: //only rewrite type variables
445: }
446: }
447: return null;
448: }
449:
450: private static ParameterizedType rewriteTypeVariable(
451: TypeVariable2 typeCv, CompilationUnitRewrite rewrite,
452: InferTypeArgumentsTCModel tCModel,
453: boolean leaveUnconstraindRaw, SimpleType[] types) {
454: ASTNode node = typeCv.getRange().getNode(rewrite.getRoot());
455: if (node instanceof Name && node.getParent() instanceof Type) {
456: Type originalType = (Type) node.getParent();
457:
458: if (types != null && !has(types, originalType))
459: return null;
460:
461: // Must rewrite all type arguments in one batch. Do the rewrite when the first one is encountered; skip the others.
462: Object rewritten = originalType.getProperty(REWRITTEN);
463: if (rewritten == REWRITTEN)
464: return null;
465: originalType.setProperty(REWRITTEN, REWRITTEN);
466:
467: ArrayList typeArgumentCvs = getTypeArgumentCvs(typeCv,
468: tCModel);
469: Type[] typeArguments = getTypeArguments(originalType,
470: typeArgumentCvs, rewrite, tCModel,
471: leaveUnconstraindRaw);
472: if (typeArguments == null)
473: return null;
474:
475: Type movingType = (Type) rewrite.getASTRewrite()
476: .createMoveTarget(originalType);
477: ParameterizedType newType = rewrite.getAST()
478: .newParameterizedType(movingType);
479:
480: for (int i = 0; i < typeArguments.length; i++) {
481: newType.typeArguments().add(typeArguments[i]);
482: }
483:
484: rewrite
485: .getASTRewrite()
486: .replace(
487: originalType,
488: newType,
489: rewrite
490: .createGroupDescription(RefactoringCoreMessages.InferTypeArgumentsRefactoring_addTypeArguments));
491: return newType;
492: } else {//TODO: other node types?
493: return null;
494: }
495: }
496:
497: private static boolean has(SimpleType[] types, Type originalType) {
498: for (int i = 0; i < types.length; i++) {
499: if (types[i] == originalType)
500: return true;
501: }
502: return false;
503: }
504:
505: /**
506: * @param baseType
507: * @param typeArgumentCvs
508: * @param rewrite
509: * @param tCModel
510: * @param leaveUnconstraindRaw
511: * @return the new type arguments, or <code>null</code> iff an argument could not be infered
512: */
513: private static Type[] getTypeArguments(Type baseType,
514: ArrayList typeArgumentCvs, CompilationUnitRewrite rewrite,
515: InferTypeArgumentsTCModel tCModel,
516: boolean leaveUnconstraindRaw) {
517: if (typeArgumentCvs.size() == 0)
518: return null;
519:
520: Type[] typeArguments = new Type[typeArgumentCvs.size()];
521: for (int i = 0; i < typeArgumentCvs.size(); i++) {
522: CollectionElementVariable2 elementCv = (CollectionElementVariable2) typeArgumentCvs
523: .get(i);
524: Type typeArgument;
525: TType chosenType = InferTypeArgumentsConstraintsSolver
526: .getChosenType(elementCv);
527: if (chosenType != null) {
528: if (chosenType.isWildcardType()
529: && !unboundedWildcardAllowed(baseType))
530: return null; // can't e.g. write "new ArrayList<?>()".
531: if (chosenType.isParameterizedType()) // workaround for bug 99124
532: chosenType = chosenType.getTypeDeclaration();
533: BindingKey bindingKey = new BindingKey(chosenType
534: .getBindingKey());
535: typeArgument = rewrite.getImportRewrite()
536: .addImportFromSignature(
537: bindingKey.toSignature(),
538: rewrite.getAST());
539: ArrayList nestedTypeArgumentCvs = getTypeArgumentCvs(
540: elementCv, tCModel);
541: Type[] nestedTypeArguments = getTypeArguments(
542: typeArgument, nestedTypeArgumentCvs, rewrite,
543: tCModel, leaveUnconstraindRaw); //recursion
544: if (nestedTypeArguments != null) {
545: ParameterizedType parameterizedType = rewrite
546: .getAST()
547: .newParameterizedType(typeArgument);
548: for (int j = 0; j < nestedTypeArguments.length; j++)
549: parameterizedType.typeArguments().add(
550: nestedTypeArguments[j]);
551: typeArgument = parameterizedType;
552: }
553:
554: } else { // couldn't infer an element type (no constraints)
555: if (leaveUnconstraindRaw) {
556: // every guess could be wrong => leave the whole thing raw
557: return null;
558: } else {
559: if (unboundedWildcardAllowed(baseType)) {
560: typeArgument = rewrite.getAST()
561: .newWildcardType();
562: } else {
563: String object = rewrite.getImportRewrite()
564: .addImport("java.lang.Object"); //$NON-NLS-1$
565: typeArgument = (Type) rewrite.getASTRewrite()
566: .createStringPlaceholder(object,
567: ASTNode.SIMPLE_TYPE);
568: }
569: }
570: // ASTNode baseTypeParent= baseType.getParent();
571: // if (baseTypeParent instanceof ClassInstanceCreation) {
572: // //No ? allowed. Take java.lang.Object.
573: // typeArgument= rewrite.getAST().newSimpleType(rewrite.getAST().newName(rewrite.getImportRewrite().addImport("java.lang.Object"))); //$NON-NLS-1$
574: // } else if (baseTypeParent instanceof ArrayCreation || baseTypeParent instanceof InstanceofExpression) {
575: // //Only ? allowed.
576: // typeArgument= rewrite.getAST().newWildcardType();
577: // } else {
578: // //E.g. field type: can put anything. Choosing ? in order to be most constraining.
579: // typeArgument= rewrite.getAST().newWildcardType();
580: // }
581: }
582: typeArguments[i] = typeArgument;
583: }
584: return typeArguments;
585: }
586:
587: private static ArrayList/*<CollectionElementVariable2>*/getTypeArgumentCvs(
588: ConstraintVariable2 baseCv,
589: InferTypeArgumentsTCModel tCModel) {
590: Map elementCvs = tCModel.getElementVariables(baseCv);
591: ArrayList typeArgumentCvs = new ArrayList();
592: for (Iterator iter = elementCvs.values().iterator(); iter
593: .hasNext();) {
594: CollectionElementVariable2 elementCv = (CollectionElementVariable2) iter
595: .next();
596: int index = elementCv.getDeclarationTypeVariableIndex();
597: if (index != CollectionElementVariable2.NOT_DECLARED_TYPE_VARIABLE_INDEX) {
598: while (index >= typeArgumentCvs.size())
599: typeArgumentCvs.add(null); // fill with null until set(index, ..) is possible
600: typeArgumentCvs.set(index, elementCv);
601: }
602: }
603: return typeArgumentCvs;
604: }
605:
606: private static boolean unboundedWildcardAllowed(Type originalType) {
607: ASTNode parent = originalType.getParent();
608: while (parent instanceof Type)
609: parent = parent.getParent();
610:
611: if (parent instanceof ClassInstanceCreation) {
612: return false;
613: } else if (parent instanceof AbstractTypeDeclaration) {
614: return false;
615: } else if (parent instanceof TypeLiteral) {
616: return false;
617: }
618: return true;
619: }
620:
621: private static ASTNode rewriteCastVariable(CastVariable2 castCv,
622: CompilationUnitRewrite rewrite,
623: InferTypeArgumentsTCModel tCModel) {//, List positionGroups) {
624: ASTNode node = castCv.getRange().getNode(rewrite.getRoot());
625:
626: ConstraintVariable2 expressionVariable = castCv
627: .getExpressionVariable();
628: ConstraintVariable2 methodReceiverCv = tCModel
629: .getMethodReceiverCv(expressionVariable);
630: if (methodReceiverCv != null) {
631: TType chosenReceiverType = InferTypeArgumentsConstraintsSolver
632: .getChosenType(methodReceiverCv);
633: if (chosenReceiverType == null)
634: return null;
635: else if (!InferTypeArgumentsTCModel
636: .isAGenericType(chosenReceiverType))
637: return null;
638: else if (hasUnboundElement(methodReceiverCv, tCModel))
639: return null;
640: }
641:
642: CastExpression castExpression = (CastExpression) node;
643: Expression expression = castExpression.getExpression();
644: ASTNode nodeToReplace;
645: if (castExpression.getParent() instanceof ParenthesizedExpression)
646: nodeToReplace = castExpression.getParent();
647: else
648: nodeToReplace = castExpression;
649:
650: Expression newExpression = (Expression) rewrite.getASTRewrite()
651: .createMoveTarget(expression);
652: rewrite
653: .getASTRewrite()
654: .replace(
655: nodeToReplace,
656: newExpression,
657: rewrite
658: .createGroupDescription(RefactoringCoreMessages.InferTypeArgumentsRefactoring_removeCast));
659: rewrite.getImportRemover().registerRemovedNode(nodeToReplace);
660: return newExpression;
661: }
662:
663: private static boolean hasUnboundElement(
664: ConstraintVariable2 methodReceiverCv,
665: InferTypeArgumentsTCModel tCModel) {
666: ArrayList/*<CollectionElementVariable2>*/typeArgumentCvs = getTypeArgumentCvs(
667: methodReceiverCv, tCModel);
668: for (Iterator iter = typeArgumentCvs.iterator(); iter.hasNext();) {
669: CollectionElementVariable2 elementCv = (CollectionElementVariable2) iter
670: .next();
671: TType chosenElementType = InferTypeArgumentsConstraintsSolver
672: .getChosenType(elementCv);
673: if (chosenElementType == null)
674: return true;
675: }
676: return false;
677: }
678:
679: /*
680: * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
681: */
682: public Change createChange(IProgressMonitor pm)
683: throws CoreException, OperationCanceledException {
684: pm.beginTask("", 1); //$NON-NLS-1$
685: try {
686: DynamicValidationStateChange result = new DynamicValidationStateChange(
687: RefactoringCoreMessages.InferTypeArgumentsRefactoring_name,
688: fChangeManager.getAllChanges()) {
689:
690: public final ChangeDescriptor getDescriptor() {
691: final Map arguments = new HashMap();
692: final IJavaProject project = getSingleProject();
693: final String description = RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description;
694: final String header = project != null ? Messages
695: .format(
696: RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description_project,
697: project.getElementName())
698: : RefactoringCoreMessages.InferTypeArgumentsRefactoring_descriptor_description;
699: final String name = project != null ? project
700: .getElementName() : null;
701: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
702: name, this , header);
703: final String[] settings = new String[fElements.length];
704: for (int index = 0; index < settings.length; index++)
705: settings[index] = JavaElementLabels
706: .getTextLabel(
707: fElements[index],
708: JavaElementLabels.ALL_FULLY_QUALIFIED);
709: comment
710: .addSetting(JDTRefactoringDescriptorComment
711: .createCompositeSetting(
712: RefactoringCoreMessages.InferTypeArgumentsRefactoring_original_elements,
713: settings));
714: if (fAssumeCloneReturnsSameType)
715: comment
716: .addSetting(RefactoringCoreMessages.InferTypeArgumentsRefactoring_assume_clone);
717: if (fLeaveUnconstrainedRaw)
718: comment
719: .addSetting(RefactoringCoreMessages.InferTypeArgumentsRefactoring_leave_unconstrained);
720: final InferTypeArgumentsDescriptor descriptor = new InferTypeArgumentsDescriptor(
721: name,
722: description,
723: comment.asString(),
724: arguments,
725: RefactoringDescriptor.STRUCTURAL_CHANGE
726: | RefactoringDescriptor.MULTI_CHANGE);
727: for (int index = 0; index < fElements.length; index++)
728: arguments
729: .put(
730: JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
731: + (index + 1),
732: JavaRefactoringDescriptorUtil
733: .elementToHandle(
734: name,
735: fElements[index]));
736: arguments.put(ATTRIBUTE_CLONE, Boolean.valueOf(
737: fAssumeCloneReturnsSameType).toString());
738: arguments.put(ATTRIBUTE_LEAVE, Boolean.valueOf(
739: fLeaveUnconstrainedRaw).toString());
740: return new RefactoringChangeDescriptor(descriptor);
741: }
742: };
743: return result;
744: } finally {
745: pm.done();
746: }
747: }
748:
749: private IJavaProject getSingleProject() {
750: IJavaProject first = null;
751: for (int index = 0; index < fElements.length; index++) {
752: final IJavaProject project = fElements[index]
753: .getJavaProject();
754: if (project != null) {
755: if (first == null)
756: first = project;
757: else if (!project.equals(first))
758: return null;
759: }
760: }
761: return first;
762: }
763:
764: public RefactoringStatus initialize(
765: final RefactoringArguments arguments) {
766: if (arguments instanceof JavaRefactoringArguments) {
767: final JavaRefactoringArguments generic = (JavaRefactoringArguments) arguments;
768: final String clone = generic.getAttribute(ATTRIBUTE_CLONE);
769: if (clone != null) {
770: fAssumeCloneReturnsSameType = Boolean.valueOf(clone)
771: .booleanValue();
772: } else
773: return RefactoringStatus
774: .createFatalErrorStatus(Messages
775: .format(
776: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
777: ATTRIBUTE_CLONE));
778: final String leave = generic.getAttribute(ATTRIBUTE_LEAVE);
779: if (leave != null) {
780: fLeaveUnconstrainedRaw = Boolean.valueOf(leave)
781: .booleanValue();
782: } else
783: return RefactoringStatus
784: .createFatalErrorStatus(Messages
785: .format(
786: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
787: ATTRIBUTE_LEAVE));
788: int count = 1;
789: final List elements = new ArrayList();
790: String handle = null;
791: String attribute = JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
792: + count;
793: final RefactoringStatus status = new RefactoringStatus();
794: while ((handle = generic.getAttribute(attribute)) != null) {
795: final IJavaElement element = JavaRefactoringDescriptorUtil
796: .handleToElement(generic.getProject(), handle,
797: false);
798: if (element == null || !element.exists())
799: return createInputFatalStatus(element,
800: IJavaRefactorings.INFER_TYPE_ARGUMENTS);
801: else
802: elements.add(element);
803: count++;
804: attribute = JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
805: + count;
806: }
807: fElements = (IJavaElement[]) elements
808: .toArray(new IJavaElement[elements.size()]);
809: if (elements.isEmpty())
810: return createInputFatalStatus(null,
811: IJavaRefactorings.INFER_TYPE_ARGUMENTS);
812: if (!status.isOK())
813: return status;
814: } else
815: return RefactoringStatus
816: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
817: return new RefactoringStatus();
818: }
819: }
|