blob: 60b65d1e19ca2045f736d02284589d13ca6ef31b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.jdt.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.text.edits.TextEdit;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.ltk.core.refactoring.Change;
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.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.NamingConventions;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptor;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitDescriptorChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
import org.eclipse.jdt.internal.corext.refactoring.rename.TempDeclarationFinder;
import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.CodeGeneration;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
public class PromoteTempToFieldRefactoring extends ScriptableRefactoring {
private static final String ID_PROMOTE_TEMP= "org.eclipse.jdt.ui.promote.temp"; //$NON-NLS-1$
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;
//------ 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;
private final CodeGenerationSettings fCodeGenerationSettings;
//------ analysis ---------//
private boolean fInitializerUsesLocalTypes;
private boolean fTempTypeUsesClassTypeVariables;
//------ scripting --------//
private boolean fSelfInitializing= false;
/**
* 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
* @param settings the code generation settings, or <code>null</code> if invoked by scripting
*/
public PromoteTempToFieldRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength, CodeGenerationSettings settings){
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;
fCodeGenerationSettings= settings;
}
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 String getFieldName() {
return fFieldName;
}
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
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.class);
}
private boolean isDeclaredInAnonymousClass() {
return null != ASTNodes.getParent(fTempDeclarationNode, AnonymousClassDeclaration.class);
}
/*
* @see org.eclipse.jdt.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 ICompilationUnit[]{fCu}),
getValidationContext());
if (result.hasFatalError())
return result;
initAST(pm);
if (fTempDeclarationNode == null)
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_select_declaration);
if (! Checks.isDeclaredIn(fTempDeclarationNode, MethodDeclaration.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;
fFieldName= getInitialFieldName();
}
public String[] guessFieldNames() {
String tempName= fTempDeclarationNode.getName().getIdentifier();
String rawTempName= NamingConventions.removePrefixAndSuffixForLocalVariableName(fCu.getJavaProject(), tempName);
String[] excludedNames= getNamesOfFieldsInDeclaringType();
int dim= getTempTypeArrayDimensions();
String[] suggestedNames= StubUtility.getFieldNameSuggestions(fCu.getJavaProject(), rawTempName, dim, getModifiers(), excludedNames);
return suggestedNames;
}
private String getInitialFieldName() {
String[] suggestedNames= guessFieldNames();
if (suggestedNames.length > 0) {
String longest= suggestedNames[0];
for (int i= 1; i < suggestedNames.length; i++)
if (suggestedNames[i].length() > longest.length())
longest= suggestedNames[i];
return longest;
} 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 int getTempTypeArrayDimensions() {
int dim= 0;
Type tempType= getTempDeclarationStatement().getType();
if (tempType.isArrayType())
dim += ((ArrayType)tempType).getDimensions();
dim += fTempDeclarationNode.getExtraDimensions();
return dim;
}
private void checkTempInitializerForLocalTypeUsage() {
Expression initializer= fTempDeclarationNode.getInitializer();
if (initializer == null)
return;
ITypeBinding[] methodTypeParameters= getMethodDeclaration().resolveBinding().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.PromoteTempToFieldRefactoring_cannot_promote);
Type type= vds.getType();
ITypeBinding binding= type.resolveBinding();
if (binding == null)
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote);
ITypeBinding[] methodTypeParameters= getMethodDeclaration().resolveBinding().getTypeParameters();
LocalTypeAndVariableUsageAnalyzer analyzer= new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters);
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 MethodDeclaration);
}
private void initAST(IProgressMonitor pm){
fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm);
fTempDeclarationNode= TempDeclarationFinder.findTempDeclaration(fCompilationUnitNode, fSelectionStart, fSelectionLength);
}
public RefactoringStatus validateInput(){
return Checks.checkFieldName(fFieldName);
}
/*
* @see org.eclipse.jdt.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) {
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) {
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(), JavaElementLabels.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.jdt.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 {
ASTRewrite rewrite= ASTRewrite.create(fCompilationUnitNode.getAST());
if (fInitializeIn == INITIALIZE_IN_METHOD && tempHasInitializer())
addLocalDeclarationSplit(rewrite);
else
addLocalDeclarationRemoval(rewrite);
if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
addInitializersToConstructors(rewrite);
if (! fFieldName.equals(fTempDeclarationNode.getName().getIdentifier()))
addTempRenames(rewrite);
addFieldDeclaration(rewrite);
return createChange(rewrite);
} finally {
pm.done();
}
}
private void addTempRenames(ASTRewrite rewrite) {
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];
SimpleName newName= getAST().newSimpleName(fFieldName);
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){
addNewConstructorWithInitializing(rewrite, declaration);
} else {
for (int index= 0; index < constructors.length; index++) {
if (shouldInsertTempInitialization(constructors[index]))
addFieldInitializationToConstructor(rewrite, constructors[index]);
}
}
}
private void addNewConstructorWithInitializing(ASTRewrite rewrite, AbstractTypeDeclaration declaration) throws CoreException {
String constructorSource= CodeFormatterUtil.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, getNewConstructorSource(declaration), 0, null, StubUtility.getLineDelimiterUsed(fCu), fCu.getJavaProject());
BodyDeclaration newConstructor= (BodyDeclaration) rewrite.createStringPlaceholder(constructorSource, ASTNode.METHOD_DECLARATION);
rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newConstructor, computeInsertIndexForNewConstructor(declaration), null);
}
private String getEnclosingTypeName() {
return getEnclosingType().getName().getIdentifier();
}
private AbstractTypeDeclaration getEnclosingType() {
return (AbstractTypeDeclaration)ASTNodes.getParent(getTempDeclarationStatement(), AbstractTypeDeclaration.class);
}
private String getNewConstructorSource(AbstractTypeDeclaration declaration) throws CoreException {
String lineDelimiter= StubUtility.getLineDelimiterUsed(fCu);
return getNewConstructorComment() + JdtFlags.getVisibilityString(declaration.getModifiers()) + ' ' + getEnclosingTypeName() + "(){" + //$NON-NLS-1$
lineDelimiter + (fFieldName + '=' + getTempInitializerCode() + ';') + lineDelimiter + '}';
}
private String getNewConstructorComment() throws CoreException {
if (fCodeGenerationSettings.createComments){
String comment= CodeGeneration.getMethodComment(fCu, getEnclosingTypeName(), getEnclosingTypeName(), new String[0], new String[0], null, null, StubUtility.getLineDelimiterUsed(fCu));
if (comment == null)
return ""; //$NON-NLS-1$
return comment + StubUtility.getLineDelimiterUsed(fCu);
} else
return "";//$NON-NLS-1$
}
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 MethodDeclaration)
return i;
}
return -1;
}
private void addFieldInitializationToConstructor(ASTRewrite rewrite, MethodDeclaration constructor) throws JavaModelException {
if (constructor.getBody() == null)
constructor.setBody(getAST().newBlock());
rewrite.getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertLast(createExpressionStatementThatInitializesField(rewrite), null);
}
private static boolean shouldInsertTempInitialization(MethodDeclaration 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 MethodDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) {
if (typeDeclaration instanceof TypeDeclaration) {
MethodDeclaration[] allMethods= ((TypeDeclaration) typeDeclaration).getMethods();
List result= new ArrayList(Math.min(allMethods.length, 1));
for (int i= 0; i < allMethods.length; i++) {
MethodDeclaration declaration= allMethods[i];
if (declaration.isConstructor())
result.add(declaration);
}
return (MethodDeclaration[]) result.toArray(new MethodDeclaration[result.size()]);
}
return new MethodDeclaration[] {};
}
private Change createChange(ASTRewrite rewrite) throws CoreException {
final Map arguments= new HashMap();
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, 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 JavaRefactoringDescriptorComment comment= new JavaRefactoringDescriptorComment(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, 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 JavaRefactoringDescriptor descriptor= new JavaRefactoringDescriptor(ID_PROMOTE_TEMP, project, description, comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE);
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCu));
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_NAME, fFieldName);
arguments.put(JavaRefactoringDescriptor.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());
final CompilationUnitDescriptorChange result= new CompilationUnitDescriptorChange(descriptor, RefactoringCoreMessages.PromoteTempToFieldRefactoring_name, fCu);
ITextFileBuffer buffer= RefactoringFileBuffers.acquire(fCu);
try {
TextEdit resultingEdits= rewrite.rewriteAST(buffer.getDocument(), fCu.getJavaProject().getOptions(true));
TextChangeCompatibility.addTextEdit(result, RefactoringCoreMessages.PromoteTempToFieldRefactoring_editName, resultingEdits);
} finally {
RefactoringFileBuffers.release(fCu);
}
return result;
}
private void addLocalDeclarationSplit(ASTRewrite rewrite) throws JavaModelException {
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 JavaModelException{
Assignment assignment= getAST().newAssignment();
SimpleName fieldName= getAST().newSimpleName(fFieldName);
assignment.setLeftHandSide(fieldName);
String initializerCode= getTempInitializerCode();
Expression tempInitializerCopy= (Expression)rewrite.createStringPlaceholder(initializerCode, ASTNode.METHOD_INVOCATION);
///XXX workaround for bug 25178
///(Expression)rewrite.createCopy(getTempInitializer());
assignment.setRightHandSide(tempInitializerCopy);
ExpressionStatement assignmentStatement= getAST().newExpressionStatement(assignment);
return assignmentStatement;
}
private String getTempInitializerCode() throws JavaModelException {
final StringBuffer buffer= new StringBuffer(128);
final Expression initializer= getTempInitializer();
if (initializer instanceof ArrayInitializer) {
final ArrayInitializer extended= (ArrayInitializer) initializer;
final ITypeBinding binding= extended.resolveTypeBinding();
if (binding != null) {
buffer.append("new "); //$NON-NLS-1$
buffer.append(binding.getName());
}
}
buffer.append(fCu.getBuffer().getText(initializer.getStartPosition(), initializer.getLength()));
return buffer.toString();
}
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) {
VariableDeclarationFragment fragment= getAST().newVariableDeclarationFragment();
SimpleName variableName= getAST().newSimpleName(fFieldName);
fragment.setName(variableName);
fragment.setExtraDimensions(fTempDeclarationNode.getExtraDimensions());
if (fInitializeIn == INITIALIZE_IN_FIELD && tempHasInitializer()){
Expression initializer= (Expression)rewrite.createCopyTarget(getTempInitializer());
fragment.setInitializer(initializer);
}
FieldDeclaration fieldDeclaration= getAST().newFieldDeclaration(fragment);
VariableDeclarationStatement vds= getTempDeclarationStatement();
Type type= (Type)rewrite.createCopyTarget(vds.getType());
fieldDeclaration.setType(type);
fieldDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(getAST(), 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);
}
}
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);
}
}
public RefactoringStatus initialize(final RefactoringArguments arguments) {
fSelfInitializing= true;
if (arguments instanceof JavaRefactoringArguments) {
final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
final String selection= extended.getAttribute(JavaRefactoringDescriptor.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, JavaRefactoringDescriptor.ATTRIBUTE_SELECTION}));
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.ATTRIBUTE_SELECTION));
final String handle= extended.getAttribute(JavaRefactoringDescriptor.ATTRIBUTE_INPUT);
if (handle != null) {
final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
return createInputFatalStatus(element, ID_PROMOTE_TEMP);
else
fCu= (ICompilationUnit) element;
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.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(JavaRefactoringDescriptor.ATTRIBUTE_NAME);
if (name != null && !"".equals(name)) //$NON-NLS-1$
fFieldName= name;
else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.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();
}
}