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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.code;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
19 import java.util.StringTokenizer;
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.text.edits.TextEdit;
27 import org.eclipse.ltk.core.refactoring.Change;
28 import org.eclipse.ltk.core.refactoring.Refactoring;
29 import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
30 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
31 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
32 import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
34 import org.eclipse.jdt.core.ICompilationUnit;
35 import org.eclipse.jdt.core.IJavaElement;
36 import org.eclipse.jdt.core.IJavaProject;
37 import org.eclipse.jdt.core.dom.AST;
38 import org.eclipse.jdt.core.dom.ASTNode;
39 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
40 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
41 import org.eclipse.jdt.core.dom.ArrayCreation;
42 import org.eclipse.jdt.core.dom.ArrayInitializer;
43 import org.eclipse.jdt.core.dom.ArrayType;
44 import org.eclipse.jdt.core.dom.Assignment;
45 import org.eclipse.jdt.core.dom.Block;
46 import org.eclipse.jdt.core.dom.BodyDeclaration;
47 import org.eclipse.jdt.core.dom.CatchClause;
48 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
49 import org.eclipse.jdt.core.dom.CompilationUnit;
50 import org.eclipse.jdt.core.dom.ConstructorInvocation;
51 import org.eclipse.jdt.core.dom.Expression;
52 import org.eclipse.jdt.core.dom.FieldDeclaration;
53 import org.eclipse.jdt.core.dom.IBinding;
54 import org.eclipse.jdt.core.dom.IExtendedModifier;
55 import org.eclipse.jdt.core.dom.IMethodBinding;
56 import org.eclipse.jdt.core.dom.ITypeBinding;
57 import org.eclipse.jdt.core.dom.IVariableBinding;
58 import org.eclipse.jdt.core.dom.Javadoc;
59 import org.eclipse.jdt.core.dom.MethodDeclaration;
60 import org.eclipse.jdt.core.dom.Modifier;
61 import org.eclipse.jdt.core.dom.SimpleName;
62 import org.eclipse.jdt.core.dom.Statement;
63 import org.eclipse.jdt.core.dom.SwitchStatement;
64 import org.eclipse.jdt.core.dom.Type;
65 import org.eclipse.jdt.core.dom.TypeDeclaration;
66 import org.eclipse.jdt.core.dom.VariableDeclaration;
67 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
68 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
69 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
70 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
71 import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
72 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
73 import org.eclipse.jdt.core.refactoring.descriptors.ConvertLocalVariableDescriptor;
75 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
76 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
77 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
78 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
79 import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
80 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
81 import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
82 import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroup;
83 import org.eclipse.jdt.internal.corext.refactoring.Checks;
84 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
85 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
86 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
87 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
88 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
89 import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
90 import org.eclipse.jdt.internal.corext.refactoring.rename.TempDeclarationFinder;
91 import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
92 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
93 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
94 import org.eclipse.jdt.internal.corext.util.JdtFlags;
95 import org.eclipse.jdt.internal.corext.util.Messages;
97 import org.eclipse.jdt.ui.CodeGeneration;
98 import org.eclipse.jdt.ui.JavaElementLabels;
100 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
101 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
103 public class PromoteTempToFieldRefactoring extends Refactoring {
105 private static final String ATTRIBUTE_STATIC= "static"; //$NON-NLS-1$
106 private static final String ATTRIBUTE_FINAL= "final"; //$NON-NLS-1$
107 private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$
108 private static final String ATTRIBUTE_INITIALIZE= "initialize"; //$NON-NLS-1$
110 private int fSelectionStart;
111 private int fSelectionLength;
112 private ICompilationUnit fCu;
114 public static final int INITIALIZE_IN_FIELD= 0;
115 public static final int INITIALIZE_IN_METHOD= 1;
116 public static final int INITIALIZE_IN_CONSTRUCTOR= 2;
118 private static final String LINKED_NAME= "name"; //$NON-NLS-1$
120 //------ settings ---------//
121 private String fFieldName;
122 private int fVisibility; /*see Modifier*/
123 private boolean fDeclareStatic;
124 private boolean fDeclareFinal;
125 private int fInitializeIn; /*see INITIALIZE_IN_* constraints */
127 //------ fields used for computations ---------//
128 private CompilationUnit fCompilationUnitNode;
129 private VariableDeclaration fTempDeclarationNode;
130 //------ analysis ---------//
131 private boolean fInitializerUsesLocalTypes;
132 private boolean fTempTypeUsesClassTypeVariables;
133 //------ scripting --------//
134 private boolean fSelfInitializing= false;
135 private LinkedProposalModel fLinkedProposalModel;
138 * Creates a new promote temp to field refactoring.
139 * @param unit the compilation unit, or <code>null</code> if invoked by scripting
140 * @param selectionStart start
141 * @param selectionLength length
143 public PromoteTempToFieldRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength){
144 Assert.isTrue(selectionStart >= 0);
145 Assert.isTrue(selectionLength >= 0);
146 fSelectionStart= selectionStart;
147 fSelectionLength= selectionLength;
150 fFieldName= ""; //$NON-NLS-1$
151 fVisibility= Modifier.PRIVATE;
152 fDeclareStatic= false;
153 fDeclareFinal= false;
154 fInitializeIn= INITIALIZE_IN_METHOD;
155 fLinkedProposalModel= null;
159 * Creates a new promote temp to field refactoring.
160 * @param declaration the variable declaration node to convert to a field
162 public PromoteTempToFieldRefactoring(VariableDeclaration declaration) {
163 Assert.isTrue(declaration != null);
164 fTempDeclarationNode= declaration;
165 IVariableBinding resolveBinding= declaration.resolveBinding();
166 Assert.isTrue(resolveBinding != null && !resolveBinding.isParameter() && !resolveBinding.isField());
168 ASTNode root= declaration.getRoot();
169 Assert.isTrue(root instanceof CompilationUnit);
170 fCompilationUnitNode= (CompilationUnit) root;
172 IJavaElement input= fCompilationUnitNode.getJavaElement();
173 Assert.isTrue(input instanceof ICompilationUnit);
174 fCu= (ICompilationUnit) input;
176 fSelectionStart= declaration.getStartPosition();
177 fSelectionLength= declaration.getLength();
179 fFieldName= ""; //$NON-NLS-1$
180 fVisibility= Modifier.PRIVATE;
181 fDeclareStatic= false;
182 fDeclareFinal= false;
183 fInitializeIn= INITIALIZE_IN_METHOD;
184 fLinkedProposalModel= null;
187 public PromoteTempToFieldRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
189 RefactoringStatus initializeStatus= initialize(arguments);
190 status.merge(initializeStatus);
194 public String getName() {
195 return RefactoringCoreMessages.PromoteTempToFieldRefactoring_name;
198 public int[] getAvailableVisibilities(){
199 return new int[]{Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE};
202 public int getVisibility() {
206 public boolean getDeclareFinal() {
207 return fDeclareFinal;
210 public boolean getDeclareStatic() {
211 return fDeclareStatic;
214 public int getInitializeIn() {
215 return fInitializeIn;
218 public void setVisibility(int accessModifier) {
219 Assert.isTrue(accessModifier == Modifier.PRIVATE ||
220 accessModifier == Modifier.NONE ||
221 accessModifier == Modifier.PROTECTED ||
222 accessModifier == Modifier.PUBLIC);
223 fVisibility= accessModifier;
226 public void setDeclareFinal(boolean declareFinal) {
227 fDeclareFinal= declareFinal;
230 public void setDeclareStatic(boolean declareStatic) {
231 fDeclareStatic= declareStatic;
234 public void setFieldName(String fieldName) {
235 Assert.isNotNull(fieldName);
236 fFieldName= fieldName;
239 public void setInitializeIn(int initializeIn) {
240 Assert.isTrue( initializeIn == INITIALIZE_IN_CONSTRUCTOR ||
241 initializeIn == INITIALIZE_IN_FIELD ||
242 initializeIn == INITIALIZE_IN_METHOD);
243 fInitializeIn= initializeIn;
246 public boolean canEnableSettingStatic(){
247 return fInitializeIn != INITIALIZE_IN_CONSTRUCTOR &&
248 ! isTempDeclaredInStaticMethod() &&
249 ! fTempTypeUsesClassTypeVariables;
252 public boolean canEnableSettingFinal(){
253 if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
254 return canEnableSettingDeclareInConstructors() && ! tempHasAssignmentsOtherThanInitialization();
255 else if (fInitializeIn == INITIALIZE_IN_FIELD)
256 return canEnableSettingDeclareInFieldDeclaration() && ! tempHasAssignmentsOtherThanInitialization();
257 else if (getMethodDeclaration().isConstructor())
258 return !tempHasAssignmentsOtherThanInitialization();
263 private boolean tempHasAssignmentsOtherThanInitialization() {
264 TempAssignmentFinder assignmentFinder= new TempAssignmentFinder(fTempDeclarationNode);
265 fCompilationUnitNode.accept(assignmentFinder);
266 return assignmentFinder.hasAssignments();
269 public boolean canEnableSettingDeclareInConstructors(){
270 return ! fDeclareStatic &&
271 ! fInitializerUsesLocalTypes &&
272 ! getMethodDeclaration().isConstructor() &&
273 ! isDeclaredInAnonymousClass() &&
274 ! isTempDeclaredInStaticMethod() &&
275 tempHasInitializer();
278 public boolean canEnableSettingDeclareInMethod(){
279 return ! fDeclareFinal &&
280 tempHasInitializer();
282 private boolean tempHasInitializer() {
283 return getTempInitializer() != null;
286 public boolean canEnableSettingDeclareInFieldDeclaration(){
287 return ! fInitializerUsesLocalTypes && tempHasInitializer();
290 private Expression getTempInitializer() {
291 return fTempDeclarationNode.getInitializer();
294 private boolean isTempDeclaredInStaticMethod() {
295 return Modifier.isStatic(getMethodDeclaration().getModifiers());
298 private MethodDeclaration getMethodDeclaration(){
299 return (MethodDeclaration)ASTNodes.getParent(fTempDeclarationNode, MethodDeclaration.class);
302 private boolean isDeclaredInAnonymousClass() {
303 return null != ASTNodes.getParent(fTempDeclarationNode, AnonymousClassDeclaration.class);
307 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
310 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
311 RefactoringStatus result= Checks.validateModifiesFiles(
312 ResourceUtil.getFiles(new ICompilationUnit[]{fCu}),
313 getValidationContext());
314 if (result.hasFatalError())
319 if (fTempDeclarationNode == null)
320 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_select_declaration);
322 if (! Checks.isDeclaredIn(fTempDeclarationNode, MethodDeclaration.class))
323 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_only_declared_in_methods);
325 if (isMethodParameter())
326 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_method_parameters);
328 if (isTempAnExceptionInCatchBlock())
329 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_exceptions);
331 result.merge(checkTempTypeForLocalTypeUsage());
332 if (result.hasFatalError())
335 checkTempInitializerForLocalTypeUsage();
337 if (!fSelfInitializing)
338 initializeDefaults();
342 private void initializeDefaults() {
343 fVisibility= Modifier.PRIVATE;
344 fDeclareStatic= Modifier.isStatic(getMethodDeclaration().getModifiers());
345 fDeclareFinal= false;
346 if (canEnableSettingDeclareInMethod())
347 fInitializeIn= INITIALIZE_IN_METHOD;
348 else if (canEnableSettingDeclareInFieldDeclaration())
349 fInitializeIn= INITIALIZE_IN_FIELD;
350 else if (canEnableSettingDeclareInConstructors())
351 fInitializeIn= INITIALIZE_IN_CONSTRUCTOR;
354 public String[] guessFieldNames() {
355 String rawTempName= StubUtility.getBaseName(fTempDeclarationNode.resolveBinding(), fCu.getJavaProject());
356 String[] excludedNames= getNamesOfFieldsInDeclaringType();
357 int dim= ASTNodes.getDimensions(fTempDeclarationNode);
358 return StubUtility.getFieldNameSuggestions(fCu.getJavaProject(), rawTempName, dim, getModifiers(), excludedNames);
361 private String getInitialFieldName() {
362 String[] suggestedNames= guessFieldNames();
363 if (suggestedNames.length > 0) {
364 if (fLinkedProposalModel != null) {
365 LinkedProposalPositionGroup nameGroup= fLinkedProposalModel.getPositionGroup(LINKED_NAME, true);
366 for (int i= 0; i < suggestedNames.length; i++) {
367 nameGroup.addProposal(suggestedNames[i], null, suggestedNames.length - i);
370 return suggestedNames[0];
372 return fTempDeclarationNode.getName().getIdentifier();
376 private String[] getNamesOfFieldsInDeclaringType() {
377 final AbstractTypeDeclaration type= getEnclosingType();
378 if (type instanceof TypeDeclaration) {
379 FieldDeclaration[] fields= ((TypeDeclaration) type).getFields();
380 List<String> result= new ArrayList<String>(fields.length);
381 for (int i= 0; i < fields.length; i++) {
382 for (Iterator<VariableDeclarationFragment> iter= fields[i].fragments().iterator(); iter.hasNext();) {
383 VariableDeclarationFragment field= iter.next();
384 result.add(field.getName().getIdentifier());
387 return result.toArray(new String[result.size()]);
389 return new String[] {};
392 private void checkTempInitializerForLocalTypeUsage() {
393 Expression initializer= fTempDeclarationNode.getInitializer();
394 if (initializer == null)
397 IMethodBinding declaringMethodBinding= getMethodDeclaration().resolveBinding();
398 ITypeBinding[] methodTypeParameters= declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters();
399 LocalTypeAndVariableUsageAnalyzer localTypeAnalyer= new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters);
400 initializer.accept(localTypeAnalyer);
401 fInitializerUsesLocalTypes= ! localTypeAnalyer.getUsageOfEnclosingNodes().isEmpty();
404 private RefactoringStatus checkTempTypeForLocalTypeUsage(){
405 VariableDeclarationStatement vds= getTempDeclarationStatement();
407 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote);
408 Type type= vds.getType();
409 ITypeBinding binding= type.resolveBinding();
411 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote);
413 IMethodBinding declaringMethodBinding= getMethodDeclaration().resolveBinding();
414 ITypeBinding[] methodTypeParameters= declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters();
415 LocalTypeAndVariableUsageAnalyzer analyzer= new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters);
416 type.accept(analyzer);
417 boolean usesLocalTypes= ! analyzer.getUsageOfEnclosingNodes().isEmpty();
418 fTempTypeUsesClassTypeVariables= analyzer.getClassTypeVariablesUsed();
420 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_uses_type_declared_locally);
424 private VariableDeclarationStatement getTempDeclarationStatement() {
425 return (VariableDeclarationStatement) ASTNodes.getParent(fTempDeclarationNode, VariableDeclarationStatement.class);
428 private boolean isTempAnExceptionInCatchBlock() {
429 return (fTempDeclarationNode.getParent() instanceof CatchClause);
432 private boolean isMethodParameter() {
433 return (fTempDeclarationNode.getParent() instanceof MethodDeclaration);
436 private void initAST(IProgressMonitor pm){
437 if (fCompilationUnitNode == null) {
438 fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm);
439 fTempDeclarationNode= TempDeclarationFinder.findTempDeclaration(fCompilationUnitNode, fSelectionStart, fSelectionLength);
443 public RefactoringStatus validateInput(){
444 return Checks.checkFieldName(fFieldName, fCu);
448 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
451 public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
453 RefactoringStatus result= new RefactoringStatus();
454 result.merge(checkClashesWithExistingFields());
455 if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
456 result.merge(checkClashesInConstructors());
463 private RefactoringStatus checkClashesInConstructors() {
464 Assert.isTrue(fInitializeIn == INITIALIZE_IN_CONSTRUCTOR);
465 Assert.isTrue(!isDeclaredInAnonymousClass());
466 final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getMethodDeclaration().getParent();
467 if (declaration instanceof TypeDeclaration) {
468 MethodDeclaration[] methods= ((TypeDeclaration) declaration).getMethods();
469 for (int i= 0; i < methods.length; i++) {
470 MethodDeclaration method= methods[i];
471 if (!method.isConstructor())
473 NameCollector nameCollector= new NameCollector(method) {
475 protected boolean visitNode(ASTNode node) {
479 method.accept(nameCollector);
480 List<String> names= nameCollector.getNames();
481 if (names.contains(fFieldName)) {
482 String[] keys= { BasicElementLabels.getJavaElementName(fFieldName), BindingLabelProvider.getBindingLabel(method.resolveBinding(), JavaElementLabels.ALL_FULLY_QUALIFIED)};
483 String msg= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_Name_conflict, keys);
484 return RefactoringStatus.createFatalErrorStatus(msg);
491 private RefactoringStatus checkClashesWithExistingFields(){
492 FieldDeclaration[] existingFields= getFieldDeclarations(getBodyDeclarationListOfDeclaringType());
493 for (int i= 0; i < existingFields.length; i++) {
494 FieldDeclaration declaration= existingFields[i];
495 VariableDeclarationFragment[] fragments= (VariableDeclarationFragment[]) declaration.fragments().toArray(new VariableDeclarationFragment[declaration.fragments().size()]);
496 for (int j= 0; j < fragments.length; j++) {
497 VariableDeclarationFragment fragment= fragments[j];
498 if (fFieldName.equals(fragment.getName().getIdentifier())){
499 //cannot conflict with more than 1 name
500 RefactoringStatusContext context= JavaStatusContext.create(fCu, fragment);
501 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_Name_conflict_with_field, context);
508 private ChildListPropertyDescriptor getBodyDeclarationListOfDeclaringType(){
509 ASTNode methodParent= getMethodDeclaration().getParent();
510 if (methodParent instanceof AbstractTypeDeclaration)
511 return ((AbstractTypeDeclaration) methodParent).getBodyDeclarationsProperty();
512 if (methodParent instanceof AnonymousClassDeclaration)
513 return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY;
514 Assert.isTrue(false);
518 private FieldDeclaration[] getFieldDeclarations(ChildListPropertyDescriptor descriptor) {
519 final List<BodyDeclaration> bodyDeclarations= (List<BodyDeclaration>) getMethodDeclaration().getParent().getStructuralProperty(descriptor);
520 List<FieldDeclaration> fields= new ArrayList<FieldDeclaration>(1);
521 for (Iterator<BodyDeclaration> iter= bodyDeclarations.iterator(); iter.hasNext();) {
522 Object each= iter.next();
523 if (each instanceof FieldDeclaration)
524 fields.add((FieldDeclaration) each);
526 return fields.toArray(new FieldDeclaration[fields.size()]);
530 * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
533 public Change createChange(IProgressMonitor pm) throws CoreException {
534 pm.beginTask("", 1); //$NON-NLS-1$
536 if (fFieldName.length() == 0) {
537 fFieldName= getInitialFieldName();
540 ASTRewrite rewrite= ASTRewrite.create(fCompilationUnitNode.getAST());
541 if (fInitializeIn == INITIALIZE_IN_METHOD && tempHasInitializer())
542 addLocalDeclarationSplit(rewrite);
544 addLocalDeclarationRemoval(rewrite);
545 if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
546 addInitializersToConstructors(rewrite);
547 addTempRenames(rewrite);
548 addFieldDeclaration(rewrite);
550 CompilationUnitChange result= new CompilationUnitChange(RefactoringCoreMessages.PromoteTempToFieldRefactoring_name, fCu);
551 result.setDescriptor(new RefactoringChangeDescriptor(getRefactoringDescriptor()));
552 TextEdit resultingEdits= rewrite.rewriteAST();
553 TextChangeCompatibility.addTextEdit(result, RefactoringCoreMessages.PromoteTempToFieldRefactoring_editName, resultingEdits);
561 private void addTempRenames(ASTRewrite rewrite) {
562 boolean noNameChange= fFieldName.equals(fTempDeclarationNode.getName().getIdentifier());
563 if (fLinkedProposalModel == null && noNameChange) {
564 return; // no changes needed
566 TempOccurrenceAnalyzer analyzer= new TempOccurrenceAnalyzer(fTempDeclarationNode, false);
568 SimpleName[] tempRefs= analyzer.getReferenceNodes(); // no javadocs (refactoring not for parameters)
571 for (int j= 0; j < tempRefs.length; j++) {
572 SimpleName occurence= tempRefs[j];
574 addLinkedName(rewrite, occurence, false);
576 SimpleName newName= getAST().newSimpleName(fFieldName);
577 addLinkedName(rewrite, newName, false);
578 rewrite.replace(occurence, newName, null);
583 private void addInitializersToConstructors(ASTRewrite rewrite) throws CoreException {
584 Assert.isTrue(! isDeclaredInAnonymousClass());
585 final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration)getMethodDeclaration().getParent();
586 final MethodDeclaration[] constructors= getAllConstructors(declaration);
587 if (constructors.length == 0) {
588 AST ast= rewrite.getAST();
589 MethodDeclaration newConstructor= ast.newMethodDeclaration();
590 newConstructor.setConstructor(true);
591 newConstructor.modifiers().addAll(ast.newModifiers(declaration.getModifiers() & ModifierRewrite.VISIBILITY_MODIFIERS));
592 newConstructor.setName(ast.newSimpleName(declaration.getName().getIdentifier()));
593 newConstructor.setJavadoc(getNewConstructorComment(rewrite));
594 newConstructor.setBody(ast.newBlock());
596 addFieldInitializationToConstructor(rewrite, newConstructor);
598 int insertionIndex= computeInsertIndexForNewConstructor(declaration);
599 rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newConstructor, insertionIndex, null);
601 for (int index= 0; index < constructors.length; index++) {
602 if (shouldInsertTempInitialization(constructors[index]))
603 addFieldInitializationToConstructor(rewrite, constructors[index]);
608 private String getEnclosingTypeName() {
609 return getEnclosingType().getName().getIdentifier();
612 private AbstractTypeDeclaration getEnclosingType() {
613 return (AbstractTypeDeclaration)ASTNodes.getParent(getTempDeclarationStatement(), AbstractTypeDeclaration.class);
616 private Javadoc getNewConstructorComment(ASTRewrite rewrite) throws CoreException {
617 if (StubUtility.doAddComments(fCu.getJavaProject())){
618 String comment= CodeGeneration.getMethodComment(fCu, getEnclosingTypeName(), getEnclosingTypeName(), new String[0], new String[0], null, null, StubUtility.getLineDelimiterUsed(fCu));
619 if (comment != null && comment.length() > 0) {
620 return (Javadoc) rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC);
626 private int computeInsertIndexForNewConstructor(AbstractTypeDeclaration declaration) {
627 List<BodyDeclaration> declarations= declaration.bodyDeclarations();
628 if (declarations.isEmpty())
630 int index= findFirstMethodIndex(declaration);
632 return declarations.size();
637 private int findFirstMethodIndex(AbstractTypeDeclaration typeDeclaration) {
638 for (int i= 0, n= typeDeclaration.bodyDeclarations().size(); i < n; i++) {
639 if (typeDeclaration.bodyDeclarations().get(i) instanceof MethodDeclaration)
645 private void addFieldInitializationToConstructor(ASTRewrite rewrite, MethodDeclaration constructor) {
646 if (constructor.getBody() == null)
647 constructor.setBody(getAST().newBlock());
648 Statement newStatement= createNewAssignmentStatement(rewrite);
649 rewrite.getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertLast(newStatement, null);
652 private static boolean shouldInsertTempInitialization(MethodDeclaration constructor){
653 Assert.isTrue(constructor.isConstructor());
654 if (constructor.getBody() == null)
656 List<Statement> statements= constructor.getBody().statements();
657 if (statements == null)
659 if (statements.size() > 0 && statements.get(0) instanceof ConstructorInvocation)
664 private static MethodDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) {
665 if (typeDeclaration instanceof TypeDeclaration) {
666 MethodDeclaration[] allMethods= ((TypeDeclaration) typeDeclaration).getMethods();
667 List<MethodDeclaration> result= new ArrayList<MethodDeclaration>(Math.min(allMethods.length, 1));
668 for (int i= 0; i < allMethods.length; i++) {
669 MethodDeclaration declaration= allMethods[i];
670 if (declaration.isConstructor())
671 result.add(declaration);
673 return result.toArray(new MethodDeclaration[result.size()]);
675 return new MethodDeclaration[] {};
679 private ConvertLocalVariableDescriptor getRefactoringDescriptor() {
680 final Map<String, String> arguments= new HashMap<String, String>();
681 String project= null;
682 IJavaProject javaProject= fCu.getJavaProject();
683 if (javaProject != null)
684 project= javaProject.getElementName();
685 final IVariableBinding binding= fTempDeclarationNode.resolveBinding();
686 final String description= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName()));
687 final String header= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(binding.getDeclaringMethod(), JavaElementLabels.ALL_FULLY_QUALIFIED)});
688 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
689 comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
690 comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_field_pattern, BasicElementLabels.getJavaElementName(fFieldName)));
691 switch (fInitializeIn) {
692 case INITIALIZE_IN_CONSTRUCTOR:
693 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_constructor);
695 case INITIALIZE_IN_FIELD:
696 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_declaration);
698 case INITIALIZE_IN_METHOD:
699 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_method);
702 String visibility= JdtFlags.getVisibilityString(fVisibility);
703 if ("".equals(visibility)) //$NON-NLS-1$
704 visibility= RefactoringCoreMessages.PromoteTempToFieldRefactoring_default_visibility;
705 comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_visibility_pattern, visibility));
706 if (fDeclareFinal && fDeclareStatic)
707 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final_static);
708 else if (fDeclareFinal)
709 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final);
710 else if (fDeclareStatic)
711 comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_static);
712 final ConvertLocalVariableDescriptor descriptor= RefactoringSignatureDescriptorFactory.createConvertLocalVariableDescriptor(project, description, comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE);
713 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCu));
714 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fFieldName);
715 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
716 arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic).toString());
717 arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal).toString());
718 arguments.put(ATTRIBUTE_VISIBILITY, new Integer(fVisibility).toString());
719 arguments.put(ATTRIBUTE_INITIALIZE, new Integer(fInitializeIn).toString());
723 private void addLocalDeclarationSplit(ASTRewrite rewrite) {
724 VariableDeclarationStatement tempDeclarationStatement= getTempDeclarationStatement();
725 ASTNode parentStatement= tempDeclarationStatement.getParent();
727 ListRewrite listRewrite;
728 if (parentStatement instanceof SwitchStatement) {
729 listRewrite= rewrite.getListRewrite(parentStatement, SwitchStatement.STATEMENTS_PROPERTY);
730 } else if (parentStatement instanceof Block) {
731 listRewrite= rewrite.getListRewrite(parentStatement, Block.STATEMENTS_PROPERTY);
733 // should not happen. VariableDeclaration's can not be in a control statement body
735 Assert.isTrue(false);
737 int statementIndex= listRewrite.getOriginalList().indexOf(tempDeclarationStatement);
738 Assert.isTrue(statementIndex != -1);
740 Statement newStatement= createNewAssignmentStatement(rewrite);
742 List<VariableDeclarationFragment> fragments= tempDeclarationStatement.fragments();
744 int fragmentIndex= fragments.indexOf(fTempDeclarationNode);
745 Assert.isTrue(fragmentIndex != -1);
747 if (fragments.size() == 1) {
748 rewrite.replace(tempDeclarationStatement, newStatement, null);
752 for (int i1= fragmentIndex, n = fragments.size(); i1 < n; i1++) {
753 VariableDeclarationFragment fragment= fragments.get(i1);
754 rewrite.remove(fragment, null);
756 if (fragmentIndex == 0)
757 rewrite.remove(tempDeclarationStatement, null);
759 Assert.isTrue(tempHasInitializer());
761 listRewrite.insertAt(newStatement, statementIndex + 1, null);
763 if (fragmentIndex + 1 < fragments.size()){
764 VariableDeclarationFragment firstFragmentAfter= fragments.get(fragmentIndex + 1);
765 VariableDeclarationFragment copyfirstFragmentAfter= (VariableDeclarationFragment)rewrite.createCopyTarget(firstFragmentAfter);
766 VariableDeclarationStatement statement= getAST().newVariableDeclarationStatement(copyfirstFragmentAfter);
767 Type type= (Type)rewrite.createCopyTarget(tempDeclarationStatement.getType());
768 statement.setType(type);
769 List<IExtendedModifier> modifiers= tempDeclarationStatement.modifiers();
770 if (modifiers.size() > 0) {
771 ListRewrite modifiersRewrite= rewrite.getListRewrite(tempDeclarationStatement, VariableDeclarationStatement.MODIFIERS2_PROPERTY);
772 ASTNode firstModifier= (ASTNode) modifiers.get(0);
773 ASTNode lastModifier= (ASTNode) modifiers.get(modifiers.size() - 1);
774 ASTNode modifiersCopy= modifiersRewrite.createCopyTarget(firstModifier, lastModifier);
775 statement.modifiers().add(modifiersCopy);
777 for (int i= fragmentIndex + 2; i < fragments.size(); i++) {
778 VariableDeclarationFragment fragment= fragments.get(i);
779 VariableDeclarationFragment fragmentCopy= (VariableDeclarationFragment)rewrite.createCopyTarget(fragment);
780 statement.fragments().add(fragmentCopy);
782 listRewrite.insertAt(statement, statementIndex + 2, null);
786 private Statement createNewAssignmentStatement(ASTRewrite rewrite) {
788 Assignment assignment= ast.newAssignment();
789 SimpleName fieldName= ast.newSimpleName(fFieldName);
790 addLinkedName(rewrite, fieldName, true);
791 assignment.setLeftHandSide(fieldName);
792 assignment.setRightHandSide(getTempInitializerCopy(rewrite));
793 return ast.newExpressionStatement(assignment);
796 private void addLinkedName(ASTRewrite rewrite, SimpleName fieldName, boolean isFirst) {
797 if (fLinkedProposalModel != null) {
798 fLinkedProposalModel.getPositionGroup(LINKED_NAME, true).addPosition(rewrite.track(fieldName), isFirst);
802 private Expression getTempInitializerCopy(ASTRewrite rewrite) {
803 final Expression initializer= (Expression) rewrite.createCopyTarget(getTempInitializer());
804 if (initializer instanceof ArrayInitializer && ASTNodes.getDimensions(fTempDeclarationNode) > 0) {
805 ArrayCreation arrayCreation= rewrite.getAST().newArrayCreation();
806 arrayCreation.setType((ArrayType) ASTNodeFactory.newType(rewrite.getAST(), fTempDeclarationNode));
807 arrayCreation.setInitializer((ArrayInitializer) initializer);
808 return arrayCreation;
813 private void addLocalDeclarationRemoval(ASTRewrite rewrite) {
814 VariableDeclarationStatement tempDeclarationStatement= getTempDeclarationStatement();
815 List<VariableDeclarationFragment> fragments= tempDeclarationStatement.fragments();
817 int fragmentIndex= fragments.indexOf(fTempDeclarationNode);
818 Assert.isTrue(fragmentIndex != -1);
819 VariableDeclarationFragment fragment= fragments.get(fragmentIndex);
820 rewrite.remove(fragment, null);
821 if (fragments.size() == 1)
822 rewrite.remove(tempDeclarationStatement, null);
825 private void addFieldDeclaration(ASTRewrite rewrite) {
826 final ChildListPropertyDescriptor descriptor= getBodyDeclarationListOfDeclaringType();
827 FieldDeclaration[] fields= getFieldDeclarations(getBodyDeclarationListOfDeclaringType());
828 final ASTNode parent= getMethodDeclaration().getParent();
830 if (fields.length == 0)
833 insertIndex= ((List<? extends ASTNode>) parent.getStructuralProperty(descriptor)).indexOf(fields[fields.length - 1]) + 1;
835 final FieldDeclaration declaration= createNewFieldDeclaration(rewrite);
836 rewrite.getListRewrite(parent, descriptor).insertAt(declaration, insertIndex, null);
839 private FieldDeclaration createNewFieldDeclaration(ASTRewrite rewrite) {
841 VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
842 SimpleName variableName= ast.newSimpleName(fFieldName);
843 fragment.setName(variableName);
844 addLinkedName(rewrite, variableName, false);
845 fragment.setExtraDimensions(fTempDeclarationNode.getExtraDimensions());
846 if (fInitializeIn == INITIALIZE_IN_FIELD && tempHasInitializer()){
847 Expression initializer= (Expression)rewrite.createCopyTarget(getTempInitializer());
848 fragment.setInitializer(initializer);
850 FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(fragment);
852 VariableDeclarationStatement vds= getTempDeclarationStatement();
853 Type type= (Type)rewrite.createCopyTarget(vds.getType());
854 fieldDeclaration.setType(type);
855 fieldDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getModifiers()));
856 return fieldDeclaration;
859 private int getModifiers() {
860 int flags= fVisibility;
862 flags |= Modifier.FINAL;
864 flags |= Modifier.STATIC;
868 private AST getAST(){
869 return fTempDeclarationNode.getAST();
872 private static class LocalTypeAndVariableUsageAnalyzer extends HierarchicalASTVisitor{
873 private final List<IBinding> fLocalDefinitions= new ArrayList<IBinding>(0); // List of IBinding (Variable and Type)
874 private final List<SimpleName> fLocalReferencesToEnclosing= new ArrayList<SimpleName>(0); // List of ASTNodes
875 private final List<ITypeBinding> fMethodTypeVariables;
876 private boolean fClassTypeVariablesUsed= false;
877 public LocalTypeAndVariableUsageAnalyzer(ITypeBinding[] methodTypeVariables) {
878 fMethodTypeVariables= Arrays.asList(methodTypeVariables);
880 public List<SimpleName> getUsageOfEnclosingNodes(){
881 return fLocalReferencesToEnclosing;
883 public boolean getClassTypeVariablesUsed() {
884 return fClassTypeVariablesUsed;
887 public boolean visit(SimpleName node) {
888 ITypeBinding typeBinding= node.resolveTypeBinding();
889 if (typeBinding != null && typeBinding.isLocal()) {
890 if (node.isDeclaration()) {
891 fLocalDefinitions.add(typeBinding);
892 } else if (! fLocalDefinitions.contains(typeBinding)) {
893 fLocalReferencesToEnclosing.add(node);
896 if (typeBinding != null && typeBinding.isTypeVariable()) {
897 if (node.isDeclaration()) {
898 fLocalDefinitions.add(typeBinding);
899 } else if (! fLocalDefinitions.contains(typeBinding)) {
900 if (fMethodTypeVariables.contains(typeBinding)) {
901 fLocalReferencesToEnclosing.add(node);
903 fClassTypeVariablesUsed= true;
907 IBinding binding= node.resolveBinding();
908 if (binding != null && binding.getKind() == IBinding.VARIABLE && ! ((IVariableBinding)binding).isField()) {
909 if (node.isDeclaration()) {
910 fLocalDefinitions.add(binding);
911 } else if (! fLocalDefinitions.contains(binding)) {
912 fLocalReferencesToEnclosing.add(node);
915 return super.visit(node);
919 private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
920 fSelfInitializing= true;
921 final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
922 if (selection != null) {
925 final StringTokenizer tokenizer= new StringTokenizer(selection);
926 if (tokenizer.hasMoreTokens())
927 offset= Integer.valueOf(tokenizer.nextToken()).intValue();
928 if (tokenizer.hasMoreTokens())
929 length= Integer.valueOf(tokenizer.nextToken()).intValue();
930 if (offset >= 0 && length >= 0) {
931 fSelectionStart= offset;
932 fSelectionLength= length;
934 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
936 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
937 final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
938 if (handle != null) {
939 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
940 if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
941 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.CONVERT_LOCAL_VARIABLE);
943 fCu= (ICompilationUnit) element;
945 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
946 final String visibility= arguments.getAttribute(ATTRIBUTE_VISIBILITY);
947 if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$
950 flag= Integer.parseInt(visibility);
951 } catch (NumberFormatException exception) {
952 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY));
956 final String initialize= arguments.getAttribute(ATTRIBUTE_INITIALIZE);
957 if (initialize != null && !"".equals(initialize)) {//$NON-NLS-1$
960 value= Integer.parseInt(initialize);
961 } catch (NumberFormatException exception) {
962 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INITIALIZE));
964 fInitializeIn= value;
966 final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
967 if (name != null && !"".equals(name)) //$NON-NLS-1$
970 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
971 final String declareStatic= arguments.getAttribute(ATTRIBUTE_STATIC);
972 if (declareStatic != null) {
973 fDeclareStatic= Boolean.valueOf(declareStatic).booleanValue();
975 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STATIC));
976 final String declareFinal= arguments.getAttribute(ATTRIBUTE_FINAL);
977 if (declareFinal != null) {
978 fDeclareFinal= Boolean.valueOf(declareFinal).booleanValue();
980 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL));
981 return new RefactoringStatus();
985 public void setLinkedProposalModel(LinkedProposalModel model) {
986 fLinkedProposalModel= model;