/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.code; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayCreation; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayInitializer; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayType; import org.eclipse.che.ide.ext.java.jdt.core.dom.Assignment; import org.eclipse.che.ide.ext.java.jdt.core.dom.Block; import org.eclipse.che.ide.ext.java.jdt.core.dom.BodyDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.CatchClause; import org.eclipse.che.ide.ext.java.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.ConstructorInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression; import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IExtendedModifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.Javadoc; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Modifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import org.eclipse.che.ide.ext.java.jdt.core.dom.Statement; import org.eclipse.che.ide.ext.java.jdt.core.dom.SwitchStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.HierarchicalASTVisitor; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ModifierRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.util.TextChangeCompatibility; import org.eclipse.che.ide.ext.java.jdt.internal.ui.BindingLabelProvider; import org.eclipse.che.ide.ext.java.jdt.internal.ui.JavaElementLabels; import org.eclipse.che.ide.ext.java.jdt.refactoring.Change; import org.eclipse.che.ide.ext.java.jdt.refactoring.Refactoring; import org.eclipse.che.ide.ext.java.jdt.refactoring.RefactoringStatus; import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEdit; import org.eclipse.che.ide.runtime.Assert; import org.eclipse.che.ide.runtime.CoreException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; public class PromoteTempToFieldRefactoring extends Refactoring { private static final String ATTRIBUTE_STATIC = "static"; //$NON-NLS-1$ private static final String ATTRIBUTE_FINAL = "final"; //$NON-NLS-1$ private static final String ATTRIBUTE_VISIBILITY = "visibility"; //$NON-NLS-1$ private static final String ATTRIBUTE_INITIALIZE = "initialize"; //$NON-NLS-1$ private int fSelectionStart; private int fSelectionLength; // private ICompilationUnit fCu; public static final int INITIALIZE_IN_FIELD = 0; public static final int INITIALIZE_IN_METHOD = 1; public static final int INITIALIZE_IN_CONSTRUCTOR = 2; private static final String LINKED_NAME = "name"; //$NON-NLS-1$ //------ settings ---------// private String fFieldName; private int fVisibility; /*see Modifier*/ private boolean fDeclareStatic; private boolean fDeclareFinal; private int fInitializeIn; /*see INITIALIZE_IN_* constraints */ //------ fields used for computations ---------// private CompilationUnit fCompilationUnitNode; private VariableDeclaration fTempDeclarationNode; //------ analysis ---------// private boolean fInitializerUsesLocalTypes; private boolean fTempTypeUsesClassTypeVariables; //------ scripting --------// private boolean fSelfInitializing = false; private final Document document; // private LinkedProposalModel fLinkedProposalModel; /** * Creates a new promote temp to field refactoring. * * @param unit * the compilation unit, or <code>null</code> if invoked by scripting * @param selectionStart * start * @param selectionLength * length */ public PromoteTempToFieldRefactoring(Document document, CompilationUnit cu, int selectionStart, int selectionLength) { this.document = document; fCompilationUnitNode = cu; Assert.isTrue(selectionStart >= 0); Assert.isTrue(selectionLength >= 0); fSelectionStart = selectionStart; fSelectionLength = selectionLength; // fCu = unit; fFieldName = ""; //$NON-NLS-1$ fVisibility = Modifier.PRIVATE; fDeclareStatic = false; fDeclareFinal = false; fInitializeIn = INITIALIZE_IN_METHOD; // fLinkedProposalModel= null; } /** * Creates a new promote temp to field refactoring. * * @param declaration * the variable declaration node to convert to a field */ public PromoteTempToFieldRefactoring(VariableDeclaration declaration, Document document) { Assert.isTrue(declaration != null); this.document = document; fTempDeclarationNode = declaration; IVariableBinding resolveBinding = declaration.resolveBinding(); Assert.isTrue(resolveBinding != null && !resolveBinding.isParameter() && !resolveBinding.isField()); ASTNode root = declaration.getRoot(); Assert.isTrue(root instanceof CompilationUnit); fCompilationUnitNode = (CompilationUnit)root; // IJavaElement input = fCompilationUnitNode.getJavaElement(); // Assert.isTrue(input instanceof ICompilationUnit); // fCu = (ICompilationUnit)input; // fSelectionStart = declaration.getStartPosition(); fSelectionLength = declaration.getLength(); fFieldName = ""; //$NON-NLS-1$ fVisibility = Modifier.PRIVATE; fDeclareStatic = false; fDeclareFinal = false; fInitializeIn = INITIALIZE_IN_METHOD; // fLinkedProposalModel= null; } // public PromoteTempToFieldRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) // { // this(null); // RefactoringStatus initializeStatus = initialize(arguments); // status.merge(initializeStatus); // } @Override public String getName() { return RefactoringCoreMessages.INSTANCE.PromoteTempToFieldRefactoring_name(); } public int[] getAvailableVisibilities() { return new int[]{Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE}; } public int getVisibility() { return fVisibility; } public boolean getDeclareFinal() { return fDeclareFinal; } public boolean getDeclareStatic() { return fDeclareStatic; } public int getInitializeIn() { return fInitializeIn; } public void setVisibility(int accessModifier) { Assert.isTrue(accessModifier == Modifier.PRIVATE || accessModifier == Modifier.NONE || accessModifier == Modifier.PROTECTED || accessModifier == Modifier.PUBLIC); fVisibility = accessModifier; } public void setDeclareFinal(boolean declareFinal) { fDeclareFinal = declareFinal; } public void setDeclareStatic(boolean declareStatic) { fDeclareStatic = declareStatic; } public void setFieldName(String fieldName) { Assert.isNotNull(fieldName); fFieldName = fieldName; } public void setInitializeIn(int initializeIn) { Assert.isTrue(initializeIn == INITIALIZE_IN_CONSTRUCTOR || initializeIn == INITIALIZE_IN_FIELD || initializeIn == INITIALIZE_IN_METHOD); fInitializeIn = initializeIn; } public boolean canEnableSettingStatic() { return fInitializeIn != INITIALIZE_IN_CONSTRUCTOR && !isTempDeclaredInStaticMethod() && !fTempTypeUsesClassTypeVariables; } public boolean canEnableSettingFinal() { if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR) return canEnableSettingDeclareInConstructors() && !tempHasAssignmentsOtherThanInitialization(); else if (fInitializeIn == INITIALIZE_IN_FIELD) return canEnableSettingDeclareInFieldDeclaration() && !tempHasAssignmentsOtherThanInitialization(); else if (getMethodDeclaration().isConstructor()) return !tempHasAssignmentsOtherThanInitialization(); else return false; } private boolean tempHasAssignmentsOtherThanInitialization() { TempAssignmentFinder assignmentFinder = new TempAssignmentFinder(fTempDeclarationNode); fCompilationUnitNode.accept(assignmentFinder); return assignmentFinder.hasAssignments(); } public boolean canEnableSettingDeclareInConstructors() { return !fDeclareStatic && !fInitializerUsesLocalTypes && !getMethodDeclaration().isConstructor() && !isDeclaredInAnonymousClass() && !isTempDeclaredInStaticMethod() && tempHasInitializer(); } public boolean canEnableSettingDeclareInMethod() { return !fDeclareFinal && tempHasInitializer(); } private boolean tempHasInitializer() { return getTempInitializer() != null; } public boolean canEnableSettingDeclareInFieldDeclaration() { return !fInitializerUsesLocalTypes && tempHasInitializer(); } private Expression getTempInitializer() { return fTempDeclarationNode.getInitializer(); } private boolean isTempDeclaredInStaticMethod() { return Modifier.isStatic(getMethodDeclaration().getModifiers()); } private MethodDeclaration getMethodDeclaration() { return (MethodDeclaration)ASTNodes.getParent(fTempDeclarationNode, MethodDeclaration.METHOD_DECLARATION); } private boolean isDeclaredInAnonymousClass() { return null != ASTNodes.getParent(fTempDeclarationNode, AnonymousClassDeclaration.ANONYMOUS_CLASS_DECLARATION); } @Override public RefactoringStatus checkInitialConditions() throws CoreException { RefactoringStatus result = new RefactoringStatus(); // Checks.validateModifiesFiles(ResourceUtil.getFiles(new ICompilationUnit[]{fCu}), getValidationContext()); // if (result.hasFatalError()) // return result; // initAST(pm); if (fTempDeclarationNode == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_select_declaration()); //TODO // if (!Checks.isDeclaredIn(fTempDeclarationNode, MethodDeclaration.class)) // return RefactoringStatus // .createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_only_declared_in_methods); if (isMethodParameter()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_method_parameters()); if (isTempAnExceptionInCatchBlock()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_exceptions()); result.merge(checkTempTypeForLocalTypeUsage()); if (result.hasFatalError()) return result; checkTempInitializerForLocalTypeUsage(); if (!fSelfInitializing) initializeDefaults(); return result; } private void initializeDefaults() { fVisibility = Modifier.PRIVATE; fDeclareStatic = Modifier.isStatic(getMethodDeclaration().getModifiers()); fDeclareFinal = false; if (canEnableSettingDeclareInMethod()) fInitializeIn = INITIALIZE_IN_METHOD; else if (canEnableSettingDeclareInFieldDeclaration()) fInitializeIn = INITIALIZE_IN_FIELD; else if (canEnableSettingDeclareInConstructors()) fInitializeIn = INITIALIZE_IN_CONSTRUCTOR; } public String[] guessFieldNames() { String rawTempName = StubUtility.getBaseName(fTempDeclarationNode.resolveBinding()); String[] excludedNames = getNamesOfFieldsInDeclaringType(); int dim = ASTNodes.getDimensions(fTempDeclarationNode); return StubUtility.getFieldNameSuggestions(rawTempName, dim, getModifiers(), excludedNames); } private String getInitialFieldName() { String[] suggestedNames = guessFieldNames(); if (suggestedNames.length > 0) { // if (fLinkedProposalModel != null) { // LinkedProposalPositionGroup nameGroup= fLinkedProposalModel.getPositionGroup(LINKED_NAME, true); // for (int i= 0; i < suggestedNames.length; i++) { // nameGroup.addProposal(suggestedNames[i], null, suggestedNames.length - i); // } // } return suggestedNames[0]; } else { return fTempDeclarationNode.getName().getIdentifier(); } } private String[] getNamesOfFieldsInDeclaringType() { final AbstractTypeDeclaration type = getEnclosingType(); if (type instanceof TypeDeclaration) { FieldDeclaration[] fields = ((TypeDeclaration)type).getFields(); List<String> result = new ArrayList<String>(fields.length); for (int i = 0; i < fields.length; i++) { for (Iterator<VariableDeclarationFragment> iter = fields[i].fragments().iterator(); iter.hasNext(); ) { VariableDeclarationFragment field = iter.next(); result.add(field.getName().getIdentifier()); } } return result.toArray(new String[result.size()]); } return new String[]{}; } private void checkTempInitializerForLocalTypeUsage() { Expression initializer = fTempDeclarationNode.getInitializer(); if (initializer == null) return; IMethodBinding declaringMethodBinding = getMethodDeclaration().resolveBinding(); ITypeBinding[] methodTypeParameters = declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters(); LocalTypeAndVariableUsageAnalyzer localTypeAnalyer = new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters); initializer.accept(localTypeAnalyer); fInitializerUsesLocalTypes = !localTypeAnalyer.getUsageOfEnclosingNodes().isEmpty(); } private RefactoringStatus checkTempTypeForLocalTypeUsage() { VariableDeclarationStatement vds = getTempDeclarationStatement(); if (vds == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_cannot_promote()); Type type = vds.getType(); ITypeBinding binding = type.resolveBinding(); if (binding == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_cannot_promote()); IMethodBinding declaringMethodBinding = getMethodDeclaration().resolveBinding(); ITypeBinding[] methodTypeParameters = declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters(); LocalTypeAndVariableUsageAnalyzer analyzer = new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters); type.accept(analyzer); boolean usesLocalTypes = !analyzer.getUsageOfEnclosingNodes().isEmpty(); fTempTypeUsesClassTypeVariables = analyzer.getClassTypeVariablesUsed(); if (usesLocalTypes) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.INSTANCE .PromoteTempToFieldRefactoring_uses_type_declared_locally()); return null; } private VariableDeclarationStatement getTempDeclarationStatement() { return (VariableDeclarationStatement)ASTNodes.getParent(fTempDeclarationNode, VariableDeclarationStatement.VARIABLE_DECLARATION_STATEMENT); } private boolean isTempAnExceptionInCatchBlock() { return (fTempDeclarationNode.getParent() instanceof CatchClause); } private boolean isMethodParameter() { return (fTempDeclarationNode.getParent() instanceof MethodDeclaration); } // private void initAST(IProgressMonitor pm) // { // if (fCompilationUnitNode == null) // { // fCompilationUnitNode = RefactoringASTParser.parseWithASTProvider(fCu, true, pm); // fTempDeclarationNode = // TempDeclarationFinder.findTempDeclaration(fCompilationUnitNode, fSelectionStart, fSelectionLength); // } // } public RefactoringStatus validateInput() { //TODO return new RefactoringStatus();//Checks.checkFieldName(fFieldName, fCu); } /* * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor) */ @Override public RefactoringStatus checkFinalConditions() throws CoreException { RefactoringStatus result = new RefactoringStatus(); result.merge(checkClashesWithExistingFields()); if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR) result.merge(checkClashesInConstructors()); return result; } private RefactoringStatus checkClashesInConstructors() { Assert.isTrue(fInitializeIn == INITIALIZE_IN_CONSTRUCTOR); Assert.isTrue(!isDeclaredInAnonymousClass()); final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration)getMethodDeclaration().getParent(); if (declaration instanceof TypeDeclaration) { MethodDeclaration[] methods = ((TypeDeclaration)declaration).getMethods(); for (int i = 0; i < methods.length; i++) { MethodDeclaration method = methods[i]; if (!method.isConstructor()) continue; NameCollector nameCollector = new NameCollector(method) { @Override protected boolean visitNode(ASTNode node) { return true; } }; method.accept(nameCollector); List<String> names = nameCollector.getNames(); if (names.contains(fFieldName)) { String msg = RefactoringCoreMessages.INSTANCE.PromoteTempToFieldRefactoring_Name_conflict(fFieldName, BindingLabelProvider .getBindingLabel( method.resolveBinding(), JavaElementLabels.ALL_FULLY_QUALIFIED)); return RefactoringStatus.createFatalErrorStatus(msg); } } } return null; } private RefactoringStatus checkClashesWithExistingFields() { FieldDeclaration[] existingFields = getFieldDeclarations(getBodyDeclarationListOfDeclaringType()); for (int i = 0; i < existingFields.length; i++) { FieldDeclaration declaration = existingFields[i]; VariableDeclarationFragment[] fragments = (VariableDeclarationFragment[])declaration.fragments().toArray( new VariableDeclarationFragment[declaration.fragments().size()]); for (int j = 0; j < fragments.length; j++) { VariableDeclarationFragment fragment = fragments[j]; if (fFieldName.equals(fragment.getName().getIdentifier())) { //cannot conflict with more than 1 name // RefactoringStatusContext context = JavaStatusContext.create(fCu, fragment); return RefactoringStatus.createFatalErrorStatus( RefactoringCoreMessages.INSTANCE.PromoteTempToFieldRefactoring_Name_conflict_with_field(), null); } } } return null; } private ChildListPropertyDescriptor getBodyDeclarationListOfDeclaringType() { ASTNode methodParent = getMethodDeclaration().getParent(); if (methodParent instanceof AbstractTypeDeclaration) return ((AbstractTypeDeclaration)methodParent).getBodyDeclarationsProperty(); if (methodParent instanceof AnonymousClassDeclaration) return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY; Assert.isTrue(false); return null; } private FieldDeclaration[] getFieldDeclarations(ChildListPropertyDescriptor descriptor) { final List<BodyDeclaration> bodyDeclarations = (List<BodyDeclaration>)getMethodDeclaration().getParent().getStructuralProperty(descriptor); List<FieldDeclaration> fields = new ArrayList<FieldDeclaration>(1); for (Iterator<BodyDeclaration> iter = bodyDeclarations.iterator(); iter.hasNext(); ) { Object each = iter.next(); if (each instanceof FieldDeclaration) fields.add((FieldDeclaration)each); } return fields.toArray(new FieldDeclaration[fields.size()]); } /* * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) */ @Override public Change createChange() throws CoreException { if (fFieldName.length() == 0) { fFieldName = getInitialFieldName(); } ASTRewrite rewrite = ASTRewrite.create(fCompilationUnitNode.getAST()); if (fInitializeIn == INITIALIZE_IN_METHOD && tempHasInitializer()) addLocalDeclarationSplit(rewrite); else addLocalDeclarationRemoval(rewrite); if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR) addInitializersToConstructors(rewrite); addTempRenames(rewrite); addFieldDeclaration(rewrite); CompilationUnitChange result = new CompilationUnitChange(RefactoringCoreMessages.INSTANCE.PromoteTempToFieldRefactoring_name(), document); // result.setDescriptor(new RefactoringChangeDescriptor(getRefactoringDescriptor())); TextEdit resultingEdits = rewrite.rewriteAST(document, WorkerMessageHandler.get().getOptions()); TextChangeCompatibility.addTextEdit(result, RefactoringCoreMessages.INSTANCE.PromoteTempToFieldRefactoring_editName(), resultingEdits); return result; } private void addTempRenames(ASTRewrite rewrite) { boolean noNameChange = fFieldName.equals(fTempDeclarationNode.getName().getIdentifier()); if (/*fLinkedProposalModel == null && */noNameChange) { return; // no changes needed } TempOccurrenceAnalyzer analyzer = new TempOccurrenceAnalyzer(fTempDeclarationNode, false); analyzer.perform(); SimpleName[] tempRefs = analyzer.getReferenceNodes(); // no javadocs (refactoring not for parameters) for (int j = 0; j < tempRefs.length; j++) { SimpleName occurence = tempRefs[j]; if (noNameChange) { // addLinkedName(rewrite, occurence, false); } else { SimpleName newName = getAST().newSimpleName(fFieldName); // addLinkedName(rewrite, newName, false); rewrite.replace(occurence, newName, null); } } } private void addInitializersToConstructors(ASTRewrite rewrite) throws CoreException { Assert.isTrue(!isDeclaredInAnonymousClass()); final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration)getMethodDeclaration().getParent(); final MethodDeclaration[] constructors = getAllConstructors(declaration); if (constructors.length == 0) { AST ast = rewrite.getAST(); MethodDeclaration newConstructor = ast.newMethodDeclaration(); newConstructor.setConstructor(true); newConstructor.modifiers().addAll( ast.newModifiers(declaration.getModifiers() & ModifierRewrite.VISIBILITY_MODIFIERS)); newConstructor.setName(ast.newSimpleName(declaration.getName().getIdentifier())); newConstructor.setJavadoc(getNewConstructorComment(rewrite)); newConstructor.setBody(ast.newBlock()); addFieldInitializationToConstructor(rewrite, newConstructor); int insertionIndex = computeInsertIndexForNewConstructor(declaration); rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newConstructor, insertionIndex, null); } else { for (int index = 0; index < constructors.length; index++) { if (shouldInsertTempInitialization(constructors[index])) addFieldInitializationToConstructor(rewrite, constructors[index]); } } } private String getEnclosingTypeName() { return getEnclosingType().getName().getIdentifier(); } private AbstractTypeDeclaration getEnclosingType() { return (AbstractTypeDeclaration)ASTNodes.getParent(getTempDeclarationStatement(), AbstractTypeDeclaration.TYPE_DECLARATION); } private Javadoc getNewConstructorComment(ASTRewrite rewrite) throws CoreException { if (StubUtility.doAddComments()) { String comment = StubUtility.getMethodComment(getEnclosingTypeName(), getEnclosingTypeName(), new String[0], new String[0], null, null, false, StubUtility.getLineDelimiterUsed()); // CodeGeneration.getMethodComment(fCu, getEnclosingTypeName(), getEnclosingTypeName(), new String[0], // new String[0], null, null, StubUtility.getLineDelimiterUsed()); // if (comment != null && comment.length() > 0) { return (Javadoc)rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC); } } return null; } private int computeInsertIndexForNewConstructor(AbstractTypeDeclaration declaration) { List<BodyDeclaration> declarations = declaration.bodyDeclarations(); if (declarations.isEmpty()) return 0; int index = findFirstMethodIndex(declaration); if (index == -1) return declarations.size(); else return index; } private int findFirstMethodIndex(AbstractTypeDeclaration typeDeclaration) { for (int i = 0, n = typeDeclaration.bodyDeclarations().size(); i < n; i++) { if (typeDeclaration.bodyDeclarations().get(i) instanceof MethodDeclaration) return i; } return -1; } private void addFieldInitializationToConstructor(ASTRewrite rewrite, MethodDeclaration constructor) { if (constructor.getBody() == null) constructor.setBody(getAST().newBlock()); Statement newStatement = createNewAssignmentStatement(rewrite); rewrite.getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertLast(newStatement, null); } private static boolean shouldInsertTempInitialization(MethodDeclaration constructor) { Assert.isTrue(constructor.isConstructor()); if (constructor.getBody() == null) return false; List<Statement> statements = constructor.getBody().statements(); if (statements == null) return false; if (statements.size() > 0 && statements.get(0) instanceof ConstructorInvocation) return false; return true; } private static MethodDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) { if (typeDeclaration instanceof TypeDeclaration) { MethodDeclaration[] allMethods = ((TypeDeclaration)typeDeclaration).getMethods(); List<MethodDeclaration> result = new ArrayList<MethodDeclaration>(Math.min(allMethods.length, 1)); for (int i = 0; i < allMethods.length; i++) { MethodDeclaration declaration = allMethods[i]; if (declaration.isConstructor()) result.add(declaration); } return result.toArray(new MethodDeclaration[result.size()]); } return new MethodDeclaration[]{}; } // private ConvertLocalVariableDescriptor getRefactoringDescriptor() // { // final Map<String, String> arguments = new HashMap<String, String>(); // String project = null; // IJavaProject javaProject = fCu.getJavaProject(); // if (javaProject != null) // project = javaProject.getElementName(); // final IVariableBinding binding = fTempDeclarationNode.resolveBinding(); // final String description = // Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description_short, // BasicElementLabels.getJavaElementName(binding.getName())); // 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)}); // final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(project, this, header); // comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_original_pattern, // BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED))); // comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_field_pattern, // BasicElementLabels.getJavaElementName(fFieldName))); // switch (fInitializeIn) // { // case INITIALIZE_IN_CONSTRUCTOR : // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_constructor); // break; // case INITIALIZE_IN_FIELD : // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_declaration); // break; // case INITIALIZE_IN_METHOD : // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_method); // break; // } // String visibility = JdtFlags.getVisibilityString(fVisibility); // if ("".equals(visibility)) //$NON-NLS-1$ // visibility = RefactoringCoreMessages.PromoteTempToFieldRefactoring_default_visibility; // comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_visibility_pattern, // visibility)); // if (fDeclareFinal && fDeclareStatic) // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final_static); // else if (fDeclareFinal) // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final); // else if (fDeclareStatic) // comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_static); // final ConvertLocalVariableDescriptor descriptor = // RefactoringSignatureDescriptorFactory.createConvertLocalVariableDescriptor(project, description, // comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE); // arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, // JavaRefactoringDescriptorUtil.elementToHandle(project, fCu)); // arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fFieldName); // arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() // + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$ // arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic).toString()); // arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal).toString()); // arguments.put(ATTRIBUTE_VISIBILITY, new Integer(fVisibility).toString()); // arguments.put(ATTRIBUTE_INITIALIZE, new Integer(fInitializeIn).toString()); // return descriptor; // } private void addLocalDeclarationSplit(ASTRewrite rewrite) { VariableDeclarationStatement tempDeclarationStatement = getTempDeclarationStatement(); ASTNode parentStatement = tempDeclarationStatement.getParent(); ListRewrite listRewrite; if (parentStatement instanceof SwitchStatement) { listRewrite = rewrite.getListRewrite(parentStatement, SwitchStatement.STATEMENTS_PROPERTY); } else if (parentStatement instanceof Block) { listRewrite = rewrite.getListRewrite(parentStatement, Block.STATEMENTS_PROPERTY); } else { // should not happen. VariableDeclaration's can not be in a control statement body listRewrite = null; Assert.isTrue(false); } int statementIndex = listRewrite.getOriginalList().indexOf(tempDeclarationStatement); Assert.isTrue(statementIndex != -1); Statement newStatement = createNewAssignmentStatement(rewrite); List<VariableDeclarationFragment> fragments = tempDeclarationStatement.fragments(); int fragmentIndex = fragments.indexOf(fTempDeclarationNode); Assert.isTrue(fragmentIndex != -1); if (fragments.size() == 1) { rewrite.replace(tempDeclarationStatement, newStatement, null); return; } for (int i1 = fragmentIndex, n = fragments.size(); i1 < n; i1++) { VariableDeclarationFragment fragment = fragments.get(i1); rewrite.remove(fragment, null); } if (fragmentIndex == 0) rewrite.remove(tempDeclarationStatement, null); Assert.isTrue(tempHasInitializer()); listRewrite.insertAt(newStatement, statementIndex + 1, null); if (fragmentIndex + 1 < fragments.size()) { VariableDeclarationFragment firstFragmentAfter = fragments.get(fragmentIndex + 1); VariableDeclarationFragment copyfirstFragmentAfter = (VariableDeclarationFragment)rewrite.createCopyTarget(firstFragmentAfter); VariableDeclarationStatement statement = getAST().newVariableDeclarationStatement(copyfirstFragmentAfter); Type type = (Type)rewrite.createCopyTarget(tempDeclarationStatement.getType()); statement.setType(type); List<IExtendedModifier> modifiers = tempDeclarationStatement.modifiers(); if (modifiers.size() > 0) { ListRewrite modifiersRewrite = rewrite.getListRewrite(tempDeclarationStatement, VariableDeclarationStatement.MODIFIERS2_PROPERTY); ASTNode firstModifier = (ASTNode)modifiers.get(0); ASTNode lastModifier = (ASTNode)modifiers.get(modifiers.size() - 1); ASTNode modifiersCopy = modifiersRewrite.createCopyTarget(firstModifier, lastModifier); statement.modifiers().add(modifiersCopy); } for (int i = fragmentIndex + 2; i < fragments.size(); i++) { VariableDeclarationFragment fragment = fragments.get(i); VariableDeclarationFragment fragmentCopy = (VariableDeclarationFragment)rewrite.createCopyTarget(fragment); statement.fragments().add(fragmentCopy); } listRewrite.insertAt(statement, statementIndex + 2, null); } } private Statement createNewAssignmentStatement(ASTRewrite rewrite) { AST ast = getAST(); Assignment assignment = ast.newAssignment(); SimpleName fieldName = ast.newSimpleName(fFieldName); // addLinkedName(rewrite, fieldName, true); assignment.setLeftHandSide(fieldName); assignment.setRightHandSide(getTempInitializerCopy(rewrite)); return ast.newExpressionStatement(assignment); } // // private void addLinkedName(ASTRewrite rewrite, SimpleName fieldName, boolean isFirst) { // if (fLinkedProposalModel != null) { // fLinkedProposalModel.getPositionGroup(LINKED_NAME, true).addPosition(rewrite.track(fieldName), isFirst); // } // } private Expression getTempInitializerCopy(ASTRewrite rewrite) { final Expression initializer = (Expression)rewrite.createCopyTarget(getTempInitializer()); if (initializer instanceof ArrayInitializer && ASTNodes.getDimensions(fTempDeclarationNode) > 0) { ArrayCreation arrayCreation = rewrite.getAST().newArrayCreation(); arrayCreation.setType((ArrayType)ASTNodeFactory.newType(rewrite.getAST(), fTempDeclarationNode)); arrayCreation.setInitializer((ArrayInitializer)initializer); return arrayCreation; } return initializer; } private void addLocalDeclarationRemoval(ASTRewrite rewrite) { VariableDeclarationStatement tempDeclarationStatement = getTempDeclarationStatement(); List<VariableDeclarationFragment> fragments = tempDeclarationStatement.fragments(); int fragmentIndex = fragments.indexOf(fTempDeclarationNode); Assert.isTrue(fragmentIndex != -1); VariableDeclarationFragment fragment = fragments.get(fragmentIndex); rewrite.remove(fragment, null); if (fragments.size() == 1) rewrite.remove(tempDeclarationStatement, null); } private void addFieldDeclaration(ASTRewrite rewrite) { final ChildListPropertyDescriptor descriptor = getBodyDeclarationListOfDeclaringType(); FieldDeclaration[] fields = getFieldDeclarations(getBodyDeclarationListOfDeclaringType()); final ASTNode parent = getMethodDeclaration().getParent(); int insertIndex; if (fields.length == 0) insertIndex = 0; else insertIndex = ((List<? extends ASTNode>)parent.getStructuralProperty(descriptor)).indexOf(fields[fields.length - 1]) + 1; final FieldDeclaration declaration = createNewFieldDeclaration(rewrite); rewrite.getListRewrite(parent, descriptor).insertAt(declaration, insertIndex, null); } private FieldDeclaration createNewFieldDeclaration(ASTRewrite rewrite) { AST ast = getAST(); VariableDeclarationFragment fragment = ast.newVariableDeclarationFragment(); SimpleName variableName = ast.newSimpleName(fFieldName); fragment.setName(variableName); // addLinkedName(rewrite, variableName, false); fragment.setExtraDimensions(fTempDeclarationNode.getExtraDimensions()); if (fInitializeIn == INITIALIZE_IN_FIELD && tempHasInitializer()) { Expression initializer = (Expression)rewrite.createCopyTarget(getTempInitializer()); fragment.setInitializer(initializer); } FieldDeclaration fieldDeclaration = ast.newFieldDeclaration(fragment); VariableDeclarationStatement vds = getTempDeclarationStatement(); Type type = (Type)rewrite.createCopyTarget(vds.getType()); fieldDeclaration.setType(type); fieldDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getModifiers())); return fieldDeclaration; } private int getModifiers() { int flags = fVisibility; if (fDeclareFinal) flags |= Modifier.FINAL; if (fDeclareStatic) flags |= Modifier.STATIC; return flags; } private AST getAST() { return fTempDeclarationNode.getAST(); } private static class LocalTypeAndVariableUsageAnalyzer extends HierarchicalASTVisitor { private final List<IBinding> fLocalDefinitions = new ArrayList<IBinding>(0); // List of IBinding (Variable and Type) private final List<SimpleName> fLocalReferencesToEnclosing = new ArrayList<SimpleName>(0); // List of ASTNodes private final List<ITypeBinding> fMethodTypeVariables; private boolean fClassTypeVariablesUsed = false; public LocalTypeAndVariableUsageAnalyzer(ITypeBinding[] methodTypeVariables) { fMethodTypeVariables = Arrays.asList(methodTypeVariables); } public List<SimpleName> getUsageOfEnclosingNodes() { return fLocalReferencesToEnclosing; } public boolean getClassTypeVariablesUsed() { return fClassTypeVariablesUsed; } @Override public boolean visit(SimpleName node) { ITypeBinding typeBinding = node.resolveTypeBinding(); if (typeBinding != null && typeBinding.isLocal()) { if (node.isDeclaration()) { fLocalDefinitions.add(typeBinding); } else if (!fLocalDefinitions.contains(typeBinding)) { fLocalReferencesToEnclosing.add(node); } } if (typeBinding != null && typeBinding.isTypeVariable()) { if (node.isDeclaration()) { fLocalDefinitions.add(typeBinding); } else if (!fLocalDefinitions.contains(typeBinding)) { if (fMethodTypeVariables.contains(typeBinding)) { fLocalReferencesToEnclosing.add(node); } else { fClassTypeVariablesUsed = true; } } } IBinding binding = node.resolveBinding(); if (binding != null && binding.getKind() == IBinding.VARIABLE && !((IVariableBinding)binding).isField()) { if (node.isDeclaration()) { fLocalDefinitions.add(binding); } else if (!fLocalDefinitions.contains(binding)) { fLocalReferencesToEnclosing.add(node); } } return super.visit(node); } } // private RefactoringStatus initialize(JavaRefactoringArguments arguments) // { // fSelfInitializing = true; // final String selection = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION); // if (selection != null) // { // int offset = -1; // int length = -1; // final StringTokenizer tokenizer = new StringTokenizer(selection); // if (tokenizer.hasMoreTokens()) // offset = Integer.valueOf(tokenizer.nextToken()).intValue(); // if (tokenizer.hasMoreTokens()) // length = Integer.valueOf(tokenizer.nextToken()).intValue(); // if (offset >= 0 && length >= 0) // { // fSelectionStart = offset; // fSelectionLength = length; // } // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[]{selection, // JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION})); // } // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, // JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION)); // final String handle = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); // if (handle != null) // { // final IJavaElement element = // JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false); // if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT) // return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), // IJavaRefactorings.CONVERT_LOCAL_VARIABLE); // else // fCu = (ICompilationUnit)element; // } // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, // JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); // final String visibility = arguments.getAttribute(ATTRIBUTE_VISIBILITY); // if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$ // int flag = 0; // try // { // flag = Integer.parseInt(visibility); // } // catch (NumberFormatException exception) // { // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY)); // } // fVisibility = flag; // } // final String initialize = arguments.getAttribute(ATTRIBUTE_INITIALIZE); // if (initialize != null && !"".equals(initialize)) {//$NON-NLS-1$ // int value = 0; // try // { // value = Integer.parseInt(initialize); // } // catch (NumberFormatException exception) // { // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INITIALIZE)); // } // fInitializeIn = value; // } // final String name = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME); // if (name != null && !"".equals(name)) //$NON-NLS-1$ // fFieldName = name; // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, // JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME)); // final String declareStatic = arguments.getAttribute(ATTRIBUTE_STATIC); // if (declareStatic != null) // { // fDeclareStatic = Boolean.valueOf(declareStatic).booleanValue(); // } // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STATIC)); // final String declareFinal = arguments.getAttribute(ATTRIBUTE_FINAL); // if (declareFinal != null) // { // fDeclareFinal = Boolean.valueOf(declareFinal).booleanValue(); // } // else // return RefactoringStatus.createFatalErrorStatus(Messages.format( // RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL)); // return new RefactoringStatus(); // } // public void setLinkedProposalModel(LinkedProposalModel model) { // fLinkedProposalModel= model; // } }