/******************************************************************************* * Copyright (c) 2000, 2009 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.wst.jsdt.internal.corext.refactoring.code; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.text.edits.TextEdit; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration; import org.eclipse.wst.jsdt.core.dom.ArrayCreation; import org.eclipse.wst.jsdt.core.dom.ArrayInitializer; import org.eclipse.wst.jsdt.core.dom.ArrayType; import org.eclipse.wst.jsdt.core.dom.Assignment; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.CatchClause; import org.eclipse.wst.jsdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.ExpressionStatement; import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; import org.eclipse.wst.jsdt.core.dom.IBinding; import org.eclipse.wst.jsdt.core.dom.ITypeBinding; import org.eclipse.wst.jsdt.core.dom.IVariableBinding; import org.eclipse.wst.jsdt.core.dom.JSdoc; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.core.dom.Modifier; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.Type; import org.eclipse.wst.jsdt.core.dom.TypeDeclaration; import org.eclipse.wst.jsdt.core.dom.VariableDeclaration; import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment; import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement; import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite; import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.dom.HierarchicalASTVisitor; import org.eclipse.wst.jsdt.internal.corext.dom.ModifierRewrite; import org.eclipse.wst.jsdt.internal.corext.fix.LinkedProposalModel; import org.eclipse.wst.jsdt.internal.corext.fix.LinkedProposalPositionGroup; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.TextChangeCompatibility; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.TempDeclarationFinder; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider; import org.eclipse.wst.jsdt.ui.CodeGeneration; import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; public class PromoteTempToFieldRefactoring extends ScriptableRefactoring { 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 IJavaScriptUnit 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 JavaScriptUnit fCompilationUnitNode; private VariableDeclaration fTempDeclarationNode; //------ analysis ---------// private boolean fInitializerUsesLocalTypes; private boolean fTempTypeUsesClassTypeVariables; //------ scripting --------// private boolean fSelfInitializing= false; 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 * @param selectionLength */ public PromoteTempToFieldRefactoring(IJavaScriptUnit unit, int selectionStart, int selectionLength){ 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) { Assert.isTrue(declaration != null); fTempDeclarationNode= declaration; IVariableBinding resolveBinding= declaration.resolveBinding(); Assert.isTrue(resolveBinding != null && !resolveBinding.isParameter() && !resolveBinding.isField()); ASTNode root= declaration.getRoot(); Assert.isTrue(root instanceof JavaScriptUnit); fCompilationUnitNode= (JavaScriptUnit) root; IJavaScriptElement input= fCompilationUnitNode.getJavaElement(); Assert.isTrue(input instanceof IJavaScriptUnit); fCu= (IJavaScriptUnit) 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 String getName() { return RefactoringCoreMessages.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 FunctionDeclaration getMethodDeclaration(){ return (FunctionDeclaration)ASTNodes.getParent(fTempDeclarationNode, FunctionDeclaration.class); } private boolean isDeclaredInAnonymousClass() { return null != ASTNodes.getParent(fTempDeclarationNode, AnonymousClassDeclaration.class); } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor) */ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { RefactoringStatus result= Checks.validateModifiesFiles( ResourceUtil.getFiles(new IJavaScriptUnit[]{fCu}), getValidationContext()); if (result.hasFatalError()) return result; initAST(pm); if (fTempDeclarationNode == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_select_declaration); if (! Checks.isDeclaredIn(fTempDeclarationNode, FunctionDeclaration.class)) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_only_declared_in_methods); if (isMethodParameter()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_method_parameters); if (isTempAnExceptionInCatchBlock()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.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.removePrefixAndSuffixForVariable(fCu.getJavaScriptProject(), fTempDeclarationNode.resolveBinding()); String[] excludedNames= getNamesOfFieldsInDeclaringType(); int dim= ASTNodes.getDimensions(fTempDeclarationNode); return StubUtility.getFieldNameSuggestions(fCu.getJavaScriptProject(), 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 result= new ArrayList(fields.length); for (int i= 0; i < fields.length; i++) { for (Iterator iter= fields[i].fragments().iterator(); iter.hasNext();) { VariableDeclarationFragment field= (VariableDeclarationFragment) iter.next(); result.add(field.getName().getIdentifier()); } } return (String[]) result.toArray(new String[result.size()]); } return new String[] {}; } private void checkTempInitializerForLocalTypeUsage() { Expression initializer= fTempDeclarationNode.getInitializer(); if (initializer == null) return; LocalTypeAndVariableUsageAnalyzer localTypeAnalyer= new LocalTypeAndVariableUsageAnalyzer(null); initializer.accept(localTypeAnalyer); fInitializerUsesLocalTypes= ! localTypeAnalyer.getUsageOfEnclosingNodes().isEmpty(); } private RefactoringStatus checkTempTypeForLocalTypeUsage(){ VariableDeclarationStatement vds= getTempDeclarationStatement(); if (vds == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote); Type type= vds.getType(); ITypeBinding binding= type.resolveBinding(); if (binding == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote); LocalTypeAndVariableUsageAnalyzer analyzer= new LocalTypeAndVariableUsageAnalyzer(null); type.accept(analyzer); boolean usesLocalTypes= ! analyzer.getUsageOfEnclosingNodes().isEmpty(); fTempTypeUsesClassTypeVariables= analyzer.getClassTypeVariablesUsed(); if (usesLocalTypes) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_uses_type_declared_locally); return null; } private VariableDeclarationStatement getTempDeclarationStatement() { return (VariableDeclarationStatement) ASTNodes.getParent(fTempDeclarationNode, VariableDeclarationStatement.class); } private boolean isTempAnExceptionInCatchBlock() { return (fTempDeclarationNode.getParent() instanceof CatchClause); } private boolean isMethodParameter() { return (fTempDeclarationNode.getParent() instanceof FunctionDeclaration); } private void initAST(IProgressMonitor pm){ if (fCompilationUnitNode == null) { fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm); fTempDeclarationNode= TempDeclarationFinder.findTempDeclaration(fCompilationUnitNode, fSelectionStart, fSelectionLength); } } public RefactoringStatus validateInput(){ return Checks.checkFieldName(fFieldName); } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor) */ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { try{ RefactoringStatus result= new RefactoringStatus(); result.merge(checkClashesWithExistingFields()); if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR) result.merge(checkClashesInConstructors()); return result; } finally { pm.done(); } } private RefactoringStatus checkClashesInConstructors() { Assert.isTrue(fInitializeIn == INITIALIZE_IN_CONSTRUCTOR); Assert.isTrue(!isDeclaredInAnonymousClass()); final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getMethodDeclaration().getParent(); if (declaration instanceof TypeDeclaration) { FunctionDeclaration[] methods= ((TypeDeclaration) declaration).getMethods(); for (int i= 0; i < methods.length; i++) { FunctionDeclaration method= methods[i]; if (!method.isConstructor()) continue; NameCollector nameCollector= new NameCollector(method) { protected boolean visitNode(ASTNode node) { return true; } }; method.accept(nameCollector); List names= nameCollector.getNames(); if (names.contains(fFieldName)) { String[] keys= { fFieldName, BindingLabelProvider.getBindingLabel(method.resolveBinding(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}; String msg= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_Name_conflict, keys); 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.PromoteTempToFieldRefactoring_Name_conflict_with_field, context); } } } 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 bodyDeclarations= (List) getMethodDeclaration().getParent().getStructuralProperty(descriptor); List fields= new ArrayList(1); for (Iterator iter= bodyDeclarations.iterator(); iter.hasNext();) { Object each= iter.next(); if (each instanceof FieldDeclaration) fields.add(each); } return (FieldDeclaration[]) fields.toArray(new FieldDeclaration[fields.size()]); } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) */ public Change createChange(IProgressMonitor pm) throws CoreException { pm.beginTask("", 1); //$NON-NLS-1$ try { 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); return createChange(rewrite); } finally { pm.done(); } } 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 FunctionDeclaration[] constructors= getAllConstructors(declaration); if (constructors.length == 0) { AST ast= rewrite.getAST(); FunctionDeclaration newConstructor= ast.newFunctionDeclaration(); 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.class); } private JSdoc getNewConstructorComment(ASTRewrite rewrite) throws CoreException { if (StubUtility.doAddComments(fCu.getJavaScriptProject())){ String comment= CodeGeneration.getMethodComment(fCu, getEnclosingTypeName(), getEnclosingTypeName(), new String[0], new String[0], null, null, StubUtility.getLineDelimiterUsed(fCu)); if (comment != null && comment.length() > 0) { return (JSdoc) rewrite.createStringPlaceholder(comment, ASTNode.JSDOC); } } return null; } private int computeInsertIndexForNewConstructor(AbstractTypeDeclaration declaration) { List 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 FunctionDeclaration) return i; } return -1; } private void addFieldInitializationToConstructor(ASTRewrite rewrite, FunctionDeclaration constructor) throws JavaScriptModelException { if (constructor.getBody() == null) constructor.setBody(getAST().newBlock()); ExpressionStatement newStatement= createExpressionStatementThatInitializesField(rewrite); rewrite.getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertLast(newStatement, null); } private static boolean shouldInsertTempInitialization(FunctionDeclaration constructor){ Assert.isTrue(constructor.isConstructor()); if (constructor.getBody() == null) return false; List statements= constructor.getBody().statements(); if (statements == null) return false; if (statements.size() > 0 && statements.get(0) instanceof ConstructorInvocation) return false; return true; } private static FunctionDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) { if (typeDeclaration instanceof TypeDeclaration) { FunctionDeclaration[] allMethods= ((TypeDeclaration) typeDeclaration).getMethods(); List result= new ArrayList(Math.min(allMethods.length, 1)); for (int i= 0; i < allMethods.length; i++) { FunctionDeclaration declaration= allMethods[i]; if (declaration.isConstructor()) result.add(declaration); } return (FunctionDeclaration[]) result.toArray(new FunctionDeclaration[result.size()]); } return new FunctionDeclaration[] {}; } private Change createChange(ASTRewrite rewrite) throws CoreException { final Map arguments= new HashMap(); String project= null; IJavaScriptProject javaProject= fCu.getJavaScriptProject(); if (javaProject != null) project= javaProject.getElementName(); final IVariableBinding binding= fTempDeclarationNode.resolveBinding(); final String description= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description_short, binding.getName()); final String header= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(binding.getDeclaringMethod(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}); final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.ALL_FULLY_QUALIFIED))); comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_field_pattern, 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 JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.CONVERT_LOCAL_VARIABLE, project, description, comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCu)); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fFieldName); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(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, Integer.valueOf(fVisibility).toString()); arguments.put(ATTRIBUTE_INITIALIZE, Integer.valueOf(fInitializeIn).toString()); final CompilationUnitChange result= new CompilationUnitChange(RefactoringCoreMessages.PromoteTempToFieldRefactoring_name, fCu); result.setDescriptor(new RefactoringChangeDescriptor(descriptor)); TextEdit resultingEdits= rewrite.rewriteAST(); TextChangeCompatibility.addTextEdit(result, RefactoringCoreMessages.PromoteTempToFieldRefactoring_editName, resultingEdits); return result; } private void addLocalDeclarationSplit(ASTRewrite rewrite) throws JavaScriptModelException { VariableDeclarationStatement tempDeclarationStatement= getTempDeclarationStatement(); Block block= (Block)tempDeclarationStatement.getParent();//XXX can it be anything else? int statementIndex= block.statements().indexOf(tempDeclarationStatement); Assert.isTrue(statementIndex != -1); List fragments= tempDeclarationStatement.fragments(); int fragmentIndex= fragments.indexOf(fTempDeclarationNode); Assert.isTrue(fragmentIndex != -1); for (int i1= fragmentIndex, n = fragments.size(); i1 < n; i1++) { VariableDeclarationFragment fragment= (VariableDeclarationFragment)fragments.get(i1); rewrite.remove(fragment, null); } if (fragmentIndex == 0) rewrite.remove(tempDeclarationStatement, null); Assert.isTrue(tempHasInitializer()); rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertAt(createExpressionStatementThatInitializesField(rewrite), statementIndex + 1, null); if (fragmentIndex + 1 < fragments.size()){ VariableDeclarationFragment firstFragmentAfter= (VariableDeclarationFragment)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 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= (VariableDeclarationFragment)fragments.get(i); VariableDeclarationFragment fragmentCopy= (VariableDeclarationFragment)rewrite.createCopyTarget(fragment); statement.fragments().add(fragmentCopy); } rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertAt(statement, statementIndex + 2, null); } } private ExpressionStatement createExpressionStatementThatInitializesField(ASTRewrite rewrite) throws JavaScriptModelException{ 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) throws JavaScriptModelException { 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 fragments= tempDeclarationStatement.fragments(); int fragmentIndex= fragments.indexOf(fTempDeclarationNode); Assert.isTrue(fragmentIndex != -1); VariableDeclarationFragment fragment= (VariableDeclarationFragment)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) 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 fLocalDefinitions= new ArrayList(0); // List of IBinding (Variable and Type) private final List fLocalReferencesToEnclosing= new ArrayList(0); // List of ASTNodes private final List fMethodTypeVariables; private boolean fClassTypeVariablesUsed= false; public LocalTypeAndVariableUsageAnalyzer(ITypeBinding[] methodTypeVariables) { fMethodTypeVariables= Arrays.asList(methodTypeVariables); } public List getUsageOfEnclosingNodes(){ return fLocalReferencesToEnclosing; } public boolean getClassTypeVariablesUsed() { return fClassTypeVariablesUsed; } 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); } } 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); } } public RefactoringStatus initialize(final RefactoringArguments arguments) { fSelfInitializing= true; if (arguments instanceof JavaRefactoringArguments) { final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; final String selection= extended.getAttribute(JDTRefactoringDescriptor.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, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION})); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION)); final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); if (handle != null) { final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); if (element == null || !element.exists() || element.getElementType() != IJavaScriptElement.JAVASCRIPT_UNIT) return createInputFatalStatus(element, IJavaScriptRefactorings.CONVERT_LOCAL_VARIABLE); else fCu= (IJavaScriptUnit) element; } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); final String visibility= extended.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= extended.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= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME); if (name != null && !"".equals(name)) //$NON-NLS-1$ fFieldName= name; else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME)); final String declareStatic= extended.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= extended.getAttribute(ATTRIBUTE_FINAL); if (declareFinal != null) { fDeclareFinal= Boolean.valueOf(declareFinal).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL)); } else return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); return new RefactoringStatus(); } public void setLinkedProposalModel(LinkedProposalModel model) { fLinkedProposalModel= model; } }