]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/IntroduceParameterRefactoring.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / code / IntroduceParameterRefactoring.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2012 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  *     Felix Pahl (fpahl@web.de) - contributed fix for:
11  *       o introduce parameter throws NPE if there are compiler errors
12  *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48325)
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.corext.refactoring.code;
15
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.LinkedHashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.StringTokenizer;
23
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.SubProgressMonitor;
28
29 import org.eclipse.jface.text.TextSelection;
30
31 import org.eclipse.ltk.core.refactoring.Change;
32 import org.eclipse.ltk.core.refactoring.Refactoring;
33 import org.eclipse.ltk.core.refactoring.RefactoringContribution;
34 import org.eclipse.ltk.core.refactoring.RefactoringCore;
35 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
36 import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
37 import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring;
38
39 import org.eclipse.jdt.core.ICompilationUnit;
40 import org.eclipse.jdt.core.IJavaElement;
41 import org.eclipse.jdt.core.IMethod;
42 import org.eclipse.jdt.core.JavaModelException;
43 import org.eclipse.jdt.core.SourceRange;
44 import org.eclipse.jdt.core.dom.AST;
45 import org.eclipse.jdt.core.dom.ASTNode;
46 import org.eclipse.jdt.core.dom.Annotation;
47 import org.eclipse.jdt.core.dom.ArrayInitializer;
48 import org.eclipse.jdt.core.dom.Assignment;
49 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
50 import org.eclipse.jdt.core.dom.Expression;
51 import org.eclipse.jdt.core.dom.FieldAccess;
52 import org.eclipse.jdt.core.dom.IBinding;
53 import org.eclipse.jdt.core.dom.ITypeBinding;
54 import org.eclipse.jdt.core.dom.MethodDeclaration;
55 import org.eclipse.jdt.core.dom.MethodInvocation;
56 import org.eclipse.jdt.core.dom.Name;
57 import org.eclipse.jdt.core.dom.NodeFinder;
58 import org.eclipse.jdt.core.dom.NullLiteral;
59 import org.eclipse.jdt.core.dom.ParameterizedType;
60 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
61 import org.eclipse.jdt.core.dom.QualifiedName;
62 import org.eclipse.jdt.core.dom.SimpleName;
63 import org.eclipse.jdt.core.dom.Type;
64 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
65 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
66 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
67 import org.eclipse.jdt.core.refactoring.descriptors.ChangeMethodSignatureDescriptor;
68 import org.eclipse.jdt.core.refactoring.descriptors.IntroduceParameterDescriptor;
69
70 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
71 import org.eclipse.jdt.internal.corext.Corext;
72 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
73 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
74 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
75 import org.eclipse.jdt.internal.corext.dom.Bindings;
76 import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
77 import org.eclipse.jdt.internal.corext.dom.fragments.ASTFragmentFactory;
78 import org.eclipse.jdt.internal.corext.dom.fragments.IASTFragment;
79 import org.eclipse.jdt.internal.corext.dom.fragments.IExpressionFragment;
80 import org.eclipse.jdt.internal.corext.refactoring.Checks;
81 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
82 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
83 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
84 import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
85 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
86 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
87 import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
88 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
89 import org.eclipse.jdt.internal.corext.refactoring.structure.BodyUpdater;
90 import org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor;
91 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
92 import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating;
93 import org.eclipse.jdt.internal.corext.util.Messages;
94
95 import org.eclipse.jdt.ui.JavaElementLabels;
96
97 import org.eclipse.jdt.internal.ui.JavaPlugin;
98 import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
99 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
100
101
102 public class IntroduceParameterRefactoring extends Refactoring implements IDelegateUpdating {
103
104         private static final String ATTRIBUTE_ARGUMENT= "argument"; //$NON-NLS-1$
105
106         private static final String[] KNOWN_METHOD_NAME_PREFIXES= {"get", "is"}; //$NON-NLS-2$ //$NON-NLS-1$
107
108         private ICompilationUnit fSourceCU;
109         private int fSelectionStart;
110         private int fSelectionLength;
111
112         private IMethod fMethod;
113         private Refactoring fChangeSignatureRefactoring;
114         private ChangeSignatureProcessor fChangeSignatureProcessor;
115         private ParameterInfo fParameter;
116         private String fParameterName;
117         private JavaRefactoringArguments fArguments;
118
119         private Expression fSelectedExpression;
120         private String[] fExcludedParameterNames;
121
122         /**
123          * Creates a new introduce parameter refactoring.
124          * @param unit the compilation unit, or <code>null</code> if invoked by scripting
125          * @param selectionStart start
126          * @param selectionLength length
127          */
128         public IntroduceParameterRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength) {
129                 Assert.isTrue(selectionStart >= 0);
130                 Assert.isTrue(selectionLength >= 0);
131                 fSourceCU= unit;
132                 fSelectionStart= selectionStart;
133                 fSelectionLength= selectionLength;
134         }
135
136     public IntroduceParameterRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
137                 this(null, 0, 0);
138                 RefactoringStatus initializeStatus= initialize(arguments);
139                 status.merge(initializeStatus);
140     }
141
142         // ------------------- IDelegateUpdating ----------------------
143
144         public boolean canEnableDelegateUpdating() {
145                 return true;
146         }
147
148         public boolean getDelegateUpdating() {
149                 return (fChangeSignatureProcessor != null) ? fChangeSignatureProcessor.getDelegateUpdating() : false;
150         }
151
152         public void setDelegateUpdating(boolean updating) {
153                 if (fChangeSignatureProcessor != null)
154                         fChangeSignatureProcessor.setDelegateUpdating(updating);
155         }
156
157         public void setDeprecateDelegates(boolean deprecate) {
158                 if (fChangeSignatureProcessor != null)
159                         fChangeSignatureProcessor.setDeprecateDelegates(deprecate);
160         }
161
162         public boolean getDeprecateDelegates() {
163                 return (fChangeSignatureProcessor != null) ? fChangeSignatureProcessor.getDeprecateDelegates() : false;
164         }
165
166         // ------------------- /IDelegateUpdating ---------------------
167
168         @Override
169         public String getName() {
170                 return RefactoringCoreMessages.IntroduceParameterRefactoring_name;
171         }
172
173         //--- checkActivation
174
175         @Override
176         public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
177                 try {
178                         pm.beginTask("", 7); //$NON-NLS-1$
179
180                         if (! fSourceCU.isStructureKnown())
181                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_syntax_error);
182
183                         IJavaElement enclosingElement= SelectionConverter.resolveEnclosingElement(fSourceCU, new TextSelection(fSelectionStart, fSelectionLength));
184                         if (! (enclosingElement instanceof IMethod))
185                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method);
186
187                         fMethod= (IMethod) enclosingElement;
188                         pm.worked(1);
189
190                         RefactoringStatus result= new RefactoringStatus();
191                         if (fArguments != null) {
192                                 // invoked by script
193                                 fChangeSignatureProcessor= new ChangeSignatureProcessor(fArguments, result);
194                                 if (!result.hasFatalError()) {
195                                         fChangeSignatureRefactoring= new ProcessorBasedRefactoring(fChangeSignatureProcessor);
196                                         fChangeSignatureRefactoring.setValidationContext(getValidationContext());
197                                         result.merge(fChangeSignatureProcessor.checkInitialConditions(new SubProgressMonitor(pm, 2)));
198                                         if (result.hasFatalError())
199                                                 return result;
200                                 } else {
201                                         pm.worked(2);
202                                         return result;
203                                 }
204                         } else {
205                                 // first try:
206                                 fChangeSignatureProcessor= RefactoringAvailabilityTester.isChangeSignatureAvailable(fMethod) ? new ChangeSignatureProcessor(fMethod) : null;
207                                 if (fChangeSignatureProcessor == null)
208                                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method);
209                                 fChangeSignatureRefactoring= new ProcessorBasedRefactoring(fChangeSignatureProcessor);
210                                 fChangeSignatureRefactoring.setValidationContext(getValidationContext());
211                                 result.merge(fChangeSignatureProcessor.checkInitialConditions(new SubProgressMonitor(pm, 1)));
212                                 if (result.hasFatalError()) {
213                                         RefactoringStatusEntry entry= result.getEntryMatchingSeverity(RefactoringStatus.FATAL);
214                                         if (entry.getCode() == RefactoringStatusCodes.OVERRIDES_ANOTHER_METHOD || entry.getCode() == RefactoringStatusCodes.METHOD_DECLARED_IN_INTERFACE) {
215                                                 // second try:
216                                                 IMethod method= (IMethod) entry.getData();
217                                                 fChangeSignatureProcessor= RefactoringAvailabilityTester.isChangeSignatureAvailable(method) ? new ChangeSignatureProcessor(method) : null;
218                                                 if (fChangeSignatureProcessor == null) {
219                                                         String msg= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_cannot_introduce, entry.getMessage());
220                                                         return RefactoringStatus.createFatalErrorStatus(msg);
221                                                 }
222                                                 fChangeSignatureRefactoring= new ProcessorBasedRefactoring(fChangeSignatureProcessor);
223                                                 fChangeSignatureRefactoring.setValidationContext(getValidationContext());
224                                                 result= fChangeSignatureProcessor.checkInitialConditions(new SubProgressMonitor(pm, 1));
225                                                 if (result.hasFatalError())
226                                                         return result;
227                                         } else {
228                                                 return result;
229                                         }
230                                 } else {
231                                         pm.worked(1);
232                                 }
233                         }
234
235                         CompilationUnitRewrite cuRewrite= fChangeSignatureProcessor.getBaseCuRewrite();
236                         if (! cuRewrite.getCu().equals(fSourceCU))
237                                 cuRewrite= new CompilationUnitRewrite(fSourceCU); // TODO: should try to avoid throwing away this AST
238
239                         initializeSelectedExpression(cuRewrite);
240                         pm.worked(1);
241
242                         result.merge(checkSelection(cuRewrite, new SubProgressMonitor(pm, 3)));
243                         if (result.hasFatalError())
244                                 return result;
245
246                         initializeExcludedParameterNames(cuRewrite);
247
248                         addParameterInfo(cuRewrite);
249
250                         fChangeSignatureProcessor.setBodyUpdater(new BodyUpdater() {
251                                 @Override
252                                 public void updateBody(MethodDeclaration methodDeclaration, CompilationUnitRewrite rewrite, RefactoringStatus updaterResult) {
253                                         replaceSelectedExpression(rewrite);
254                                 }
255                         });
256
257                         return result;
258                 } finally {
259                         pm.done();
260                         if (fChangeSignatureRefactoring != null)
261                                 fChangeSignatureRefactoring.setValidationContext(null);
262                 }
263         }
264
265         private void addParameterInfo(CompilationUnitRewrite cuRewrite) throws JavaModelException {
266                 ITypeBinding typeBinding= Bindings.normalizeForDeclarationUse(fSelectedExpression.resolveTypeBinding(), fSelectedExpression.getAST());
267                 String name= fParameterName != null ? fParameterName : guessedParameterName();
268                 Expression expression= fSelectedExpression instanceof ParenthesizedExpression ? ((ParenthesizedExpression)fSelectedExpression).getExpression() : fSelectedExpression;
269                 
270                 ImportRewrite importRewrite= cuRewrite.getImportRewrite();                      
271                 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(fSelectedExpression, importRewrite);
272                 String typeName= importRewrite.addImport(typeBinding, importRewriteContext);
273                 
274                 String defaultValue= null;
275                 if (expression instanceof ClassInstanceCreation && typeBinding.isParameterizedType()) {
276                         ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) expression;
277                         Type cicType= classInstanceCreation.getType();
278                         if (cicType instanceof ParameterizedType && ((ParameterizedType) cicType).typeArguments().size() == 0) {
279                                 // expand the diamond:
280                                 AST ast= cuRewrite.getAST();                            
281                                 Type type= importRewrite.addImport(typeBinding, ast, importRewriteContext);                             
282                                 classInstanceCreation.setType(type);    // Should not touch the original AST ...
283                                 defaultValue= ASTNodes.asFormattedString(classInstanceCreation,  0, StubUtility.getLineDelimiterUsed(cuRewrite.getCu()), cuRewrite.getCu().getJavaProject().getOptions(true));
284                                 classInstanceCreation.setType(cicType); // ... so let's restore it right away.
285                         }
286                 }
287                 
288                 if (defaultValue == null) {
289                         defaultValue= fSourceCU.getBuffer().getText(expression.getStartPosition(), expression.getLength());
290                 }
291                 fParameter= ParameterInfo.createInfoForAddedParameter(typeBinding, typeName, name, defaultValue);
292                 if (fArguments == null) {
293                         List<ParameterInfo> parameterInfos= fChangeSignatureProcessor.getParameterInfos();
294                         int parametersCount= parameterInfos.size();
295                         if (parametersCount > 0 && parameterInfos.get(parametersCount - 1).isOldVarargs())
296                                 parameterInfos.add(parametersCount - 1, fParameter);
297                         else
298                                 parameterInfos.add(fParameter);
299                 }
300         }
301
302         private void replaceSelectedExpression(CompilationUnitRewrite cuRewrite) {
303                 if (! fSourceCU.equals(cuRewrite.getCu()))
304                         return;
305                 // TODO: do for all methodDeclarations and replace matching fragments?
306
307                 // cannot use fSelectedExpression here, since it could be from another AST (if method was replaced by overridden):
308                 Expression expression= (Expression) NodeFinder.perform(cuRewrite.getRoot(), fSelectedExpression.getStartPosition(), fSelectedExpression.getLength());
309
310                 ASTNode newExpression= cuRewrite.getRoot().getAST().newSimpleName(fParameter.getNewName());
311                 String description= RefactoringCoreMessages.IntroduceParameterRefactoring_replace;
312                 cuRewrite.getASTRewrite().replace(expression.getParent() instanceof ParenthesizedExpression
313                                 ? expression.getParent() : expression, newExpression, cuRewrite.createGroupDescription(description));
314         }
315
316         private void initializeSelectedExpression(CompilationUnitRewrite cuRewrite) throws JavaModelException {
317                 IASTFragment fragment= ASTFragmentFactory.createFragmentForSourceRange(
318                                 new SourceRange(fSelectionStart, fSelectionLength), cuRewrite.getRoot(), cuRewrite.getCu());
319
320                 if (! (fragment instanceof IExpressionFragment))
321                         return;
322
323                 //TODO: doesn't handle selection of partial Expressions
324                 Expression expression= ((IExpressionFragment) fragment).getAssociatedExpression();
325                 if (fragment.getStartPosition() != expression.getStartPosition()
326                                 || fragment.getLength() != expression.getLength())
327                         return;
328
329                 if (Checks.isInsideJavadoc(expression))
330                         return;
331                 //TODO: exclude invalid selections
332                 if (Checks.isEnumCase(expression.getParent()))
333                         return;
334
335                 fSelectedExpression= expression;
336         }
337
338         private RefactoringStatus checkSelection(CompilationUnitRewrite cuRewrite, IProgressMonitor pm) {
339                 try {
340                         if (fSelectedExpression == null){
341                                 String message= RefactoringCoreMessages.IntroduceParameterRefactoring_select;
342                                 return CodeRefactoringUtil.checkMethodSyntaxErrors(fSelectionStart, fSelectionLength, cuRewrite.getRoot(), message);
343                         }
344
345                         MethodDeclaration methodDeclaration= (MethodDeclaration) ASTNodes.getParent(fSelectedExpression, MethodDeclaration.class);
346                         if (methodDeclaration == null || ASTNodes.getParent(fSelectedExpression, Annotation.class) != null)
347                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method);
348                         if (methodDeclaration.resolveBinding() == null)
349                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_no_binding);
350                         //TODO: check for rippleMethods -> find matching fragments, consider callers of all rippleMethods
351
352                         RefactoringStatus result= new RefactoringStatus();
353                         result.merge(checkExpression());
354                         if (result.hasFatalError())
355                                 return result;
356
357                         result.merge(checkExpressionBinding());
358                         if (result.hasFatalError())
359                                 return result;
360
361 //                      if (isUsedInForInitializerOrUpdater(getSelectedExpression().getAssociatedExpression()))
362 //                              return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ExtractTempRefactoring.for_initializer_updater")); //$NON-NLS-1$
363 //                      pm.worked(1);
364 //
365 //                      if (isReferringToLocalVariableFromFor(getSelectedExpression().getAssociatedExpression()))
366 //                              return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ExtractTempRefactoring.refers_to_for_variable")); //$NON-NLS-1$
367 //                      pm.worked(1);
368
369                         return result;
370                 } finally {
371                         if (pm != null)
372                                 pm.done();
373                 }
374         }
375
376         private RefactoringStatus checkExpression() {
377                 //TODO: adjust error messages (or generalize for all refactorings on expression-selections?)
378                 Expression selectedExpression= fSelectedExpression;
379
380                 if (selectedExpression instanceof Name && selectedExpression.getParent() instanceof ClassInstanceCreation)
381                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_name_in_new);
382                         //TODO: let's just take the CIC automatically (no ambiguity -> no problem -> no dialog ;-)
383
384                 if (selectedExpression instanceof NullLiteral) {
385                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_null_literals);
386                 } else if (selectedExpression instanceof ArrayInitializer) {
387                         return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_array_initializer);
388                 } else if (selectedExpression instanceof Assignment) {
389                         if (selectedExpression.getParent() instanceof Expression)
390                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_assignment);
391                         else
392                                 return null;
393
394                 } else if (selectedExpression instanceof SimpleName){
395                         if ((((SimpleName)selectedExpression)).isDeclaration())
396                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_names_in_declarations);
397                         if (selectedExpression.getParent() instanceof QualifiedName && selectedExpression.getLocationInParent() == QualifiedName.NAME_PROPERTY
398                                         || selectedExpression.getParent() instanceof FieldAccess && selectedExpression.getLocationInParent() == FieldAccess.NAME_PROPERTY)
399                                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_select_expression);
400                 }
401
402                 return null;
403         }
404
405         private RefactoringStatus checkExpressionBinding() {
406                 return checkExpressionFragmentIsRValue();
407         }
408
409         // !! +/- same as in ExtractConstantRefactoring & ExtractTempRefactoring
410         private RefactoringStatus checkExpressionFragmentIsRValue() {
411                 switch(Checks.checkExpressionIsRValue(fSelectedExpression)) {
412                         case Checks.IS_RVALUE_GUESSED:
413                         case Checks.NOT_RVALUE_MISC:
414                                 return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.IntroduceParameterRefactoring_select, null, Corext.getPluginId(), RefactoringStatusCodes.EXPRESSION_NOT_RVALUE, null);
415                         case Checks.NOT_RVALUE_VOID:
416                                 return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.IntroduceParameterRefactoring_no_void, null, Corext.getPluginId(), RefactoringStatusCodes.EXPRESSION_NOT_RVALUE_VOID, null);
417                         case Checks.IS_RVALUE:
418                                 return new RefactoringStatus();
419                         default:
420                                 Assert.isTrue(false); return null;
421                 }
422         }
423
424         public List<ParameterInfo> getParameterInfos() {
425                 return fChangeSignatureProcessor.getParameterInfos();
426         }
427
428         public ParameterInfo getAddedParameterInfo() {
429                 return fParameter;
430         }
431
432         public String getMethodSignaturePreview() throws JavaModelException {
433                 return fChangeSignatureProcessor.getNewMethodSignature();
434         }
435
436 //--- Input setting/validation
437
438         public void setParameterName(String name) {
439                 Assert.isNotNull(name);
440                 fParameter.setNewName(name);
441         }
442
443         /**
444          * must only be called <i>after</i> checkActivation()
445          * @return guessed parameter name
446          */
447         public String guessedParameterName() {
448                 String[] proposals= guessParameterNames();
449                 if (proposals.length == 0)
450                         return ""; //$NON-NLS-1$
451                 else
452                         return proposals[0];
453         }
454
455 // --- TODO: copied from ExtractTempRefactoring - should extract ------------------------------
456
457         /**
458          * Must only be called <i>after</i> checkActivation().
459          * The first proposal should be used as "best guess" (if it exists).
460          * @return proposed variable names (may be empty, but not null).
461          */
462         public String[] guessParameterNames() {
463                 LinkedHashSet<String> proposals= new LinkedHashSet<String>(); //retain ordering, but prevent duplicates
464                 if (fSelectedExpression instanceof MethodInvocation){
465                         proposals.addAll(guessTempNamesFromMethodInvocation((MethodInvocation) fSelectedExpression, fExcludedParameterNames));
466                 }
467                 proposals.addAll(guessTempNamesFromExpression(fSelectedExpression, fExcludedParameterNames));
468                 return proposals.toArray(new String[proposals.size()]);
469         }
470
471         private List<String> guessTempNamesFromMethodInvocation(MethodInvocation selectedMethodInvocation, String[] excludedVariableNames) {
472                 String methodName= selectedMethodInvocation.getName().getIdentifier();
473                 for (int i= 0; i < KNOWN_METHOD_NAME_PREFIXES.length; i++) {
474                         String prefix= KNOWN_METHOD_NAME_PREFIXES[i];
475                         if (! methodName.startsWith(prefix))
476                                 continue; //not this prefix
477                         if (methodName.length() == prefix.length())
478                                 return Collections.emptyList(); // prefix alone -> don't take method name
479                         char firstAfterPrefix= methodName.charAt(prefix.length());
480                         if (! Character.isUpperCase(firstAfterPrefix))
481                                 continue; //not uppercase after prefix
482                         //found matching prefix
483                         String proposal= Character.toLowerCase(firstAfterPrefix) + methodName.substring(prefix.length() + 1);
484                         methodName= proposal;
485                         break;
486                 }
487                 String[] proposals= StubUtility.getLocalNameSuggestions(fSourceCU.getJavaProject(), methodName, 0, excludedVariableNames);
488                 return Arrays.asList(proposals);
489         }
490
491         private List<String> guessTempNamesFromExpression(Expression selectedExpression, String[] excluded) {
492                 ITypeBinding expressionBinding= Bindings.normalizeForDeclarationUse(
493                         selectedExpression.resolveTypeBinding(),
494                         selectedExpression.getAST());
495                 String typeName= getQualifiedName(expressionBinding);
496                 if (typeName.length() == 0)
497                         typeName= expressionBinding.getName();
498                 if (typeName.length() == 0)
499                         return Collections.emptyList();
500                 int typeParamStart= typeName.indexOf("<"); //$NON-NLS-1$
501                 if (typeParamStart != -1)
502                         typeName= typeName.substring(0, typeParamStart);
503                 String[] proposals= StubUtility.getLocalNameSuggestions(fSourceCU.getJavaProject(), typeName, expressionBinding.getDimensions(), excluded);
504                 return Arrays.asList(proposals);
505         }
506
507 // ----------------------------------------------------------------------
508
509         private static String getQualifiedName(ITypeBinding typeBinding) {
510                 if (typeBinding.isAnonymous())
511                         return getQualifiedName(typeBinding.getSuperclass());
512                 if (! typeBinding.isArray())
513                         return typeBinding.getQualifiedName();
514                 else
515                         return typeBinding.getElementType().getQualifiedName();
516         }
517
518         private void initializeExcludedParameterNames(CompilationUnitRewrite cuRewrite) {
519                 IBinding[] bindings= new ScopeAnalyzer(cuRewrite.getRoot()).getDeclarationsInScope(
520                                 fSelectedExpression.getStartPosition(), ScopeAnalyzer.VARIABLES);
521                 fExcludedParameterNames= new String[bindings.length];
522                 for (int i= 0; i < fExcludedParameterNames.length; i++) {
523                         fExcludedParameterNames[i]= bindings[i].getName();
524                 }
525         }
526
527         public RefactoringStatus validateInput() {
528                 return fChangeSignatureProcessor.checkSignature();
529         }
530
531 //--- checkInput
532
533         @Override
534         public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
535                 fChangeSignatureRefactoring.setValidationContext(getValidationContext());
536                 try {
537                         return fChangeSignatureRefactoring.checkFinalConditions(pm);
538                 } finally {
539                         fChangeSignatureRefactoring.setValidationContext(null);
540                 }
541         }
542
543         @Override
544         public Change createChange(IProgressMonitor pm) throws CoreException {
545                 fChangeSignatureRefactoring.setValidationContext(getValidationContext());
546                 try {
547                         Change[] changes= fChangeSignatureProcessor.getAllChanges();
548                         return new DynamicValidationRefactoringChange(getRefactoringDescriptor(), RefactoringCoreMessages.IntroduceParameterRefactoring_name, changes);
549                 } finally {
550                         fChangeSignatureRefactoring.setValidationContext(null);
551                         pm.done();
552                 }
553         }
554
555         private IntroduceParameterDescriptor getRefactoringDescriptor() {
556                 ChangeMethodSignatureDescriptor extended= (ChangeMethodSignatureDescriptor) fChangeSignatureProcessor.createDescriptor();
557                 RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(IJavaRefactorings.CHANGE_METHOD_SIGNATURE);
558
559                 Map<String, String> argumentsMap= contribution.retrieveArgumentMap(extended);
560
561                 final Map<String, String> arguments= new HashMap<String, String>();
562                 arguments.put(ATTRIBUTE_ARGUMENT, fParameter.getNewName());
563                 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
564                 arguments.putAll(argumentsMap);
565                 String signature= fChangeSignatureProcessor.getMethodName();
566                 try {
567                         signature= fChangeSignatureProcessor.getOldMethodSignature();
568                 } catch (JavaModelException exception) {
569                         JavaPlugin.log(exception);
570                 }
571                 final String description= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(fChangeSignatureProcessor.getMethod().getElementName()));
572                 final String header= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_descriptor_description, new String[] { BasicElementLabels.getJavaElementName(fParameter.getNewName()), signature, BasicElementLabels.getJavaCodeString(ASTNodes.asString(fSelectedExpression))});
573                 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(extended.getProject(), this, header);
574                 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_original_pattern, JavaElementLabels.getTextLabel(fChangeSignatureProcessor.getMethod(),
575                                 JavaElementLabels.ALL_FULLY_QUALIFIED)));
576                 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_pattern, BasicElementLabels.getJavaCodeString(ASTNodes.asString(fSelectedExpression))));
577                 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_parameter_pattern, BasicElementLabels.getJavaElementName(getAddedParameterInfo().getNewName())));
578                 return RefactoringSignatureDescriptorFactory.createIntroduceParameterDescriptor(extended.getProject(), description, comment.asString(), arguments, extended.getFlags());
579         }
580
581         private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
582                 fArguments= arguments;
583                 final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
584                 if (selection != null) {
585                         int offset= -1;
586                         int length= -1;
587                         final StringTokenizer tokenizer= new StringTokenizer(selection);
588                         if (tokenizer.hasMoreTokens())
589                                 offset= Integer.valueOf(tokenizer.nextToken()).intValue();
590                         if (tokenizer.hasMoreTokens())
591                                 length= Integer.valueOf(tokenizer.nextToken()).intValue();
592                         if (offset >= 0 && length >= 0) {
593                                 fSelectionStart= offset;
594                                 fSelectionLength= length;
595                         } else
596                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
597                 } else
598                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
599                 final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
600                 if (handle != null) {
601                         final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
602                         if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
603                                 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INTRODUCE_PARAMETER);
604                         else
605                                 fSourceCU= (ICompilationUnit)element;
606                 } else
607                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
608                 final String name= arguments.getAttribute(ATTRIBUTE_ARGUMENT);
609                 if (name != null && !"".equals(name)) //$NON-NLS-1$
610                         fParameterName= name;
611                 else
612                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ARGUMENT));
613                 return new RefactoringStatus();
614         }
615
616         /**
617          * {@inheritDoc}
618          */
619         public String getDelegateUpdatingTitle(boolean plural) {
620                 if (plural)
621                         return RefactoringCoreMessages.DelegateCreator_keep_original_changed_plural;
622                 else
623                         return RefactoringCoreMessages.DelegateCreator_keep_original_changed_singular;
624         }
625 }