/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.refactoring.core.extract;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.StringTokenizer;
import groovyjarjarasm.asm.Opcodes;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.classgen.VariableScopeVisitor;
import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region;
import org.codehaus.groovy.eclipse.core.GroovyCore;
import org.codehaus.groovy.eclipse.core.compiler.GroovySnippetParser;
import org.codehaus.groovy.eclipse.refactoring.Activator;
import org.codehaus.groovy.eclipse.refactoring.core.rewriter.ASTWriter;
import org.codehaus.groovy.eclipse.refactoring.core.utils.ASTTools;
import org.codehaus.groovy.eclipse.refactoring.formatter.DefaultGroovyFormatter;
import org.codehaus.groovy.eclipse.refactoring.formatter.FormatterPreferences;
import org.codehaus.groovy.eclipse.refactoring.ui.extract.GroovyRefactoringMessages;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorFactory;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorWithRequestor;
import org.eclipse.jdt.groovy.search.VariableScope;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.FileStatusContext;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextEditChangeGroup;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEditGroup;
/**
* @author Andrew Eisenberg
* @author Michael Klenk mklenk@hsr.ch
*/
public class ExtractGroovyMethodRefactoring extends Refactoring {
private class GroovyRefactoringObservable extends Observable {
@Override
protected synchronized void setChanged() {
super.setChanged();
}
@Override
public void notifyObservers(Object arg) {
super.notifyObservers(arg);
clearChanged();
}
}
private GroovyRefactoringObservable observable = new GroovyRefactoringObservable();
private String newMethodName = "";
private MethodNode newMethod;
private BlockStatement block;
private int newMethodModifier = Flags.AccDefault;
/**
* Text that will be replaced by the refactoring
*/
private Region replaceScope;
/**
* Text that is currently selected
*/
private Region selectedText;
private StatementFinder methodCodeFinder;
private boolean returnMustBeDeclared = false;
/**
* Two collections since the variables in the methodCall
* and in the signature of the method can be different
*/
private List<Variable> actualParameters;
private List<ClassNode> inferredTypeOfActualParameters;
private List<Variable> originalParametersBeforeRename;
/**
* Although we can determine if there are multiple return parameters
* we only support on return parameter
*/
private Set<Variable> returnParameters;
private List<ClassNode> inferredReturnTypes;
private Map<String, String> variablesToRename;
protected IPreferenceStore refactoringPreferences;
private GroovyCompilationUnit unit;
private CompilationUnitChange change;
public ExtractGroovyMethodRefactoring(GroovyCompilationUnit unit, int offset, int length, RefactoringStatus status) {
this.unit = unit;
this.selectedText = new Region(offset, length);
this.refactoringPreferences = Activator.getDefault().getPreferenceStore();
initializeExtractedStatements(status);
}
public ExtractGroovyMethodRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
status.merge(initialize(arguments));
initializeExtractedStatements(status);
}
private void initializeExtractedStatements(RefactoringStatus status) {
try {
methodCodeFinder = new StatementFinder(selectedText, unit.getModuleNode());
createBlockStatement();
updateMethod();
saveOriginalParameters();
} catch (Exception e) {
e.printStackTrace();
status.addFatalError(e.getMessage(), createErrorContext());
}
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
RefactoringStatus status = new RefactoringStatus();
pm.beginTask("Checking initial conditions for extract method", 100);
updateMethod();
if (pm.isCanceled()) {
throw new OperationCanceledException();
}
status.merge(checkNrOfReturnValues(pm));
if (pm.isCanceled()) {
throw new OperationCanceledException();
}
status.merge(checkStatementSelection(pm));
if (pm.isCanceled()) {
throw new OperationCanceledException();
}
status.merge(checkExtractFromConstructor(pm));
if (pm.isCanceled()) {
throw new OperationCanceledException();
}
pm.done();
return status;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
RefactoringStatus stat = new RefactoringStatus();
stat.merge(checkDuplicateMethod(pm));
change = new CompilationUnitChange(GroovyRefactoringMessages.ExtractMethodRefactoring, unit);
change.setEdit(new MultiTextEdit());
if (newMethod != null) {
TextEditGroup group = new TextEditGroup("Replace existing code with call to new method", createMethodCallEdit());
change.addChangeGroup(new TextEditChangeGroup(change, group));
change.addEdit(group.getTextEdits()[0]);
group = new TextEditGroup("Declaration of extracted method", createMethodDeclarationEdit(stat));
change.addChangeGroup(new TextEditChangeGroup(change, group));
change.addEdit(group.getTextEdits()[0]);
}
return stat;
}
@Override
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
return change;
}
@Override
public String getName() {
return "Extract Groovy Method";
}
/**
* For testing, override actual preferences with test-specific ones
*/
public void setPreferences(IPreferenceStore preferences) {
this.refactoringPreferences = preferences;
}
private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
final String selection = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
if (selection == null) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(
RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
}
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)
return RefactoringStatus.createFatalErrorStatus(Messages.format(
RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection,
JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION }));
selectedText = new Region(offset, length);
final String handle = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
if (handle == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(
RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
IJavaElement element = JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT
|| !(element instanceof GroovyCompilationUnit))
return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.EXTRACT_METHOD);
unit = (GroovyCompilationUnit) element;
final String name = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
if (name == null || name.length() == 0)
return RefactoringStatus.createFatalErrorStatus(Messages.format(
RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
newMethodName = name;
return new RefactoringStatus();
}
private void saveOriginalParameters() {
originalParametersBeforeRename = new ArrayList<Variable>();
if (newMethod != null && newMethod.getParameters() != null) {
for (Parameter p : newMethod.getParameters()) {
originalParametersBeforeRename.add(p);
}
}
}
public void addObserver(Observer observer) {
observable.addObserver(observer);
}
public void setNewMethodname(String newMethodname) {
this.newMethodName = newMethodname;
updateMethod();
observable.setChanged();
observable.notifyObservers();
}
public String getNewMethodName() {
return newMethodName;
}
public void setModifier(int modifier) {
this.newMethodModifier = modifier;
updateMethod();
observable.setChanged();
observable.notifyObservers();
}
public int getModifier() {
return newMethodModifier;
}
private void setCallAndMethHeadParameters(List<Variable> params) {
actualParameters = params;
inferredTypeOfActualParameters.clear();
for (Variable variable : params) {
inferredTypeOfActualParameters.add(variable.getType());
}
updateMethod();
}
public Parameter[] getCallAndMethHeadParameters() {
Parameter[] params = new Parameter[actualParameters.size()];
for (int i = 0, n = params.length; i < n; i += 1) {
Variable v = actualParameters.get(i);
ClassNode t = inferredTypeOfActualParameters.get(i);
Parameter tmpParam = new Parameter(t, v.getName());
params[i] = tmpParam;
}
return params;
}
/**
* MethodCallExpression to call the newly generated method
*
* @return String containing the call
*/
public String getMethodCall() {
Expression objExp = new VariableExpression("this");
ArgumentListExpression arguments = new ArgumentListExpression();
for (Variable param : originalParametersBeforeRename) {
arguments.addExpression(new VariableExpression(param.getName(),
param.getOriginType() == null ? ClassHelper.DYNAMIC_TYPE : param.getOriginType()));
}
MethodCallExpression newMethodCall = new MethodCallExpression(objExp, newMethodName, arguments);
ASTWriter writer = new ASTWriter(unit.getModuleNode(), replaceScope.getOffset(), null);
if (!returnParameters.isEmpty()) {
visitExpressionsForReturnStmt(newMethodCall, writer);
} else {
writer.visitMethodCallExpression(newMethodCall);
}
return writer.getGroovyCode();
}
private void visitExpressionsForReturnStmt(MethodCallExpression newMethodCall, ASTWriter astw) {
Assert.isTrue(!returnParameters.isEmpty());
Variable retVar = returnParameters.iterator().next();
if (returnMustBeDeclared) {
VariableExpression varExp = new VariableExpression(retVar);
DeclarationExpression declarationExpression = new DeclarationExpression(varExp, Token.newSymbol(Types.ASSIGN, -1, -1), newMethodCall);
astw.visitDeclarationExpression(declarationExpression);
} else {
BinaryExpression binaryExpression = new BinaryExpression(new VariableExpression(retVar), Token.newSymbol(Types.ASSIGN, -1, -1), newMethodCall);
astw.visitBinaryExpression(binaryExpression);
}
}
/**
* Return a method head for preview
*
* @return
*/
public String getMethodHead() {
updateMethod();
ASTWriter astw = new ASTWriter(unit.getModuleNode(), null);
astw.visitMethod(newMethod);
String head = astw.getGroovyCode();
int headEndPos = head.indexOf(")");
return head.substring(0, headEndPos + 1).trim();
}
/**
* create the method node with all given parameters
*/
private void updateMethod() {
// rearrange parameters if necessary
if (!block.getStatements().isEmpty()) {
Parameter[] params = getCallAndMethHeadParameters();
ClassNode returnType = ClassHelper.DYNAMIC_TYPE;
if (!returnParameters.isEmpty()) {
returnType = inferredReturnTypes.get(0);
if (returnType.equals(VariableScope.OBJECT_CLASS_NODE)) {
returnType = ClassHelper.DYNAMIC_TYPE;
}
}
newMethod = new MethodNode(newMethodName, 0, returnType, params, null, block);
checkStaticModifier();
}
}
private void checkStaticModifier() {
if (methodCodeFinder.isStatic()) {
newMethod.setModifiers(newMethodModifier | Opcodes.ACC_STATIC);
} else {
newMethod.setModifiers(newMethodModifier);
}
}
public boolean isStatic() {
return methodCodeFinder.isStatic();
}
/**
* Determines the statements in the new method and the parameters and return type.
*/
private void createBlockStatement() {
block = new BlockStatement();
block.addStatements(methodCodeFinder.getInSelection());
replaceScope = ASTTools.getPositionOfBlockStatements(block);
Assert.isLegal(replaceScope.getOffset() >= 0, "Replace scope has bad offset: " + replaceScope.getOffset());
Assert.isLegal(replaceScope.getLength() >= 0, "Replace scope has bad length: " + replaceScope.getLength());
defineActualAndReturnParameters();
}
/**
* Determines parameters, return parameters, and inferred types.
*/
private void defineActualAndReturnParameters() {
// Read used Variables
ASTVariableScanner scanner = new ASTVariableScanner(methodCodeFinder.isInLoopOrClosure());
scanner.visitNode(block);
ASTVariableScanner postSelectionScanner = new ASTVariableScanner(methodCodeFinder.isInLoopOrClosure());
BlockStatement postBlock = new BlockStatement();
postBlock.addStatements(methodCodeFinder.getPostSelection());
postSelectionScanner.visitNode(postBlock);
Set<Variable> postUsedVar = postSelectionScanner.getUsedVariables();
Set<Variable> selReturnVar = scanner.getAssignedVariables();
Set<Variable> innerLoopAssigned = scanner.getInnerLoopAssignedVariables();
actualParameters = new ArrayList<Variable>(scanner.getUsedVariables());
inferredTypeOfActualParameters = new ArrayList<ClassNode>(actualParameters.size());
returnParameters = new HashSet<Variable>();
inferredReturnTypes = new ArrayList<ClassNode>();
// Variables that are assigned in the block AND used after it are the
// ones that should be added as return parameters.
Set<Variable> assignedInBlockAndUsedAfterBlock = new HashSet<Variable>(postUsedVar);
assignedInBlockAndUsedAfterBlock.retainAll(selReturnVar);
returnParameters.addAll(assignedInBlockAndUsedAfterBlock);
// add variables used in the loop
returnParameters.addAll(innerLoopAssigned);
// check to see if we need to declare the return
for (Variable variable : returnParameters) {
if (postUsedVar.contains(variable) && scanner.getDeclaratedVariables().contains(variable)) {
returnMustBeDeclared = true;
break;
}
}
// now try to infer the variable types
InferParameterAndReturnTypesRequestor inferRequestor = new InferParameterAndReturnTypesRequestor(actualParameters,
returnParameters, selectedText);
TypeInferencingVisitorWithRequestor visitor = new TypeInferencingVisitorFactory().createVisitor(unit);
visitor.visitCompilationUnit(inferRequestor);
Map<Variable, ClassNode> inferredTypes = inferRequestor.getInferredTypes();
for (Variable variable : actualParameters) {
if (inferredTypes.containsKey(variable)) {
ClassNode type = inferredTypes.get(variable);
if (type == null || VariableScope.isVoidOrObject(type)) {
inferredTypeOfActualParameters.add(ClassHelper.DYNAMIC_TYPE);
} else {
// force using a cached type so that getUnwrapper will work
inferredTypeOfActualParameters.add(maybeConvertToPrimitiveType(type));
}
} else {
inferredTypeOfActualParameters.add(ClassHelper.DYNAMIC_TYPE);
}
}
for (Variable variable : returnParameters) {
if (inferredTypes.containsKey(variable)) {
// force using a cached type so that getUnwrapper will work
inferredReturnTypes.add(maybeConvertToPrimitiveType(inferredTypes.get(variable)));
} else {
inferredReturnTypes.add(variable.getOriginType());
}
}
}
private ClassNode maybeConvertToPrimitiveType(ClassNode type) {
return ClassHelper.getUnwrapper(type).getPlainNodeReference();
}
private RefactoringStatus checkDuplicateMethod(IProgressMonitor pm) {
SubProgressMonitor sub = new SubProgressMonitor(pm, 25);
sub.beginTask("Checking for duplicate methods", 25);
RefactoringStatus stat = new RefactoringStatus();
if (getMethodNames().contains(newMethodName)) {
Object[] message = { newMethodName, getClassName() };
String messageString = MessageFormat.format(GroovyRefactoringMessages.ExtractMethodWizard_MethodNameAlreadyExists,
message);
stat.addError(messageString);
}
sub.done();
return stat;
}
private RefactoringStatus checkExtractFromConstructor(IProgressMonitor pm) {
SubProgressMonitor sub = new SubProgressMonitor(pm, 25);
sub.beginTask("Checking for constructor calls", 25);
RefactoringStatus stat = new RefactoringStatus();
if (methodCodeFinder.isInConstructor()) {
if (new ExtractConstructorTest().containsConstructorCall(newMethod)) {
stat.addFatalError(GroovyRefactoringMessages.ExtractMethodInfo_NoExtractionOfConstructorCallinConstructor,
createErrorContext());
}
}
sub.done();
return stat;
}
private RefactoringStatus checkStatementSelection(IProgressMonitor pm) {
SubProgressMonitor sub = new SubProgressMonitor(pm, 25);
sub.beginTask("Checking statement selection", 25);
RefactoringStatus stat = new RefactoringStatus();
int selectionLength = selectedText.getLength();
if (block.isEmpty() && selectionLength >= 0) {
stat.addFatalError(GroovyRefactoringMessages.ExtractMethodInfo_NoStatementSelected, createErrorContext());
}
sub.done();
return stat;
}
private RefactoringStatus checkNrOfReturnValues(IProgressMonitor pm) {
SubProgressMonitor sub = new SubProgressMonitor(pm, 25);
sub.beginTask("Checking number of return values", 25);
RefactoringStatus stat = new RefactoringStatus();
if (returnParameters != null && returnParameters.size() > 1) {
StringBuilder retValues = new StringBuilder();
for (Variable var : returnParameters) {
retValues.append(var.getType().getNameWithoutPackage() + " " + var.getName() + "\n");
}
String errorMsg = GroovyRefactoringMessages.ExtractMethodInfo_ToMuchReturnValues + retValues.toString();
stat.addFatalError(errorMsg, createErrorContext());
}
sub.done();
return stat;
}
/**
* Returns the Code of the new Method as a formated IDocument.
*/
private String createCopiedMethodCode(RefactoringStatus status) {
IDocument unitDocument = new Document(String.valueOf(unit.getContents()));
String lineDelimiter = TextUtilities.getDefaultLineDelimiter(unitDocument);
StringBuilder sb = new StringBuilder();
try {
final FormatterPreferences formmatterPrefs = new FormatterPreferences(unit);
int indentLevel = calculateIndentation();
String indentation = CodeFormatterUtil.createIndentString(indentLevel, unit.getJavaProject());
sb.append(lineDelimiter + lineDelimiter + indentation);
sb.append(getMethodHead()).append(" {").append(lineDelimiter);
// copy the source code
String copyOfSourceCode = unitDocument.get(replaceScope.getOffset(), replaceScope.getLength());
sb.append(copyOfSourceCode);
sb.append(lineDelimiter);
ASTWriter astw = writeReturnStatements(unitDocument);
if (astw.getGroovyCode().length() > 0) {
sb.append(astw.getGroovyCode());
}
sb.append("}");
MethodNode newMethod = createNewMethodForValidation(sb.toString(), status);
IDocument newMethodDocument = new Document(sb.toString());
if (newMethod != null && variablesToRename != null) {
MultiTextEdit edits = renameVariableInExtractedMethod(newMethod);
edits.apply(newMethodDocument);
}
DefaultGroovyFormatter formatter = new DefaultGroovyFormatter(newMethodDocument, formmatterPrefs, indentLevel);
formatter.format().apply(newMethodDocument);
return newMethodDocument.get();
} catch (BadLocationException e) {
status.addFatalError("Problem when creating the body of the extracted method.\n" + e.getMessage(), createErrorContext());
GroovyCore.logException("Problem when creating the body of the extracted method.", e);
}
return sb.toString();
}
/**
* @return may return null if there is a parse problem
*/
private MethodNode createNewMethodForValidation(String methodText, RefactoringStatus status) {
try {
GroovySnippetParser parser = new GroovySnippetParser();
ModuleNode module = parser.parse(methodText);
if (module.getMethods() == null || module.getMethods().size() != 1) {
status.addError("Problem parsing extracted method", createErrorContext());
if (module.getMethods() == null) {
return null;
}
}
MethodNode method = module.getMethods().get(0);
new VariableScopeVisitor(null).visitClass(method.getDeclaringClass());
return method;
} catch (Exception e) {
// probably bad syntax
status.addError("Problem parsing extracted method.\n" + e.getMessage(), createErrorContext());
}
return null;
}
private FileStatusContext createErrorContext() {
return new FileStatusContext((IFile) unit.getResource(), new org.eclipse.jface.text.Region(selectedText.getOffset(), selectedText.getLength()));
}
/**
* @return indentation level, given in 'indentation units'.
*/
private int calculateIndentation() {
int defaultIndentation;
if (methodCodeFinder.getClassNode().isScript()) {
defaultIndentation = 0;
} else {
// must handle inner classes
int innerClassCount = 0;
ClassNode current = methodCodeFinder.getClassNode();
while (current != null) {
innerClassCount += 1;
if (current.getEnclosingMethod() != null) {
innerClassCount += 1;
current = current.getEnclosingMethod().getDeclaringClass();
continue;
}
current = current.getDeclaringClass();
}
defaultIndentation = innerClassCount;
}
return defaultIndentation;
}
private MultiTextEdit renameVariableInExtractedMethod(MethodNode method) {
VariableRenamer renamer = new VariableRenamer();
return renamer.rename(method, variablesToRename);
}
private ASTWriter writeReturnStatements(IDocument document) {
ASTWriter astw = new ASTWriter(unit.getModuleNode(), document);
for (Variable var : returnParameters) {
ReturnStatement ret = new ReturnStatement(new VariableExpression(var));
astw.visitReturnStatement(ret);
astw.insertLineFeed();
}
return astw;
}
private InsertEdit createMethodDeclarationEdit(RefactoringStatus status) {
return new InsertEdit(methodCodeFinder.getSelectedDeclaration().getEnd(), createCopiedMethodCode(status));
}
private ReplaceEdit createMethodCallEdit() {
return new ReplaceEdit(replaceScope.getOffset(), replaceScope.getLength(), getMethodCall());
}
public List<String> getMethodNames() {
return methodCodeFinder.getMethodNames();
}
public String getClassName() {
return methodCodeFinder.getClassName();
}
/**
* @param variName
* @param upEvent true if the move is upwards
* @param numberOfMoves mostly 1, can be more for tests
* @return the index of the selected variable in the collection
*/
public int setMoveParameter(String variName, boolean upEvent, int numberOfMoves) {
Parameter[] originalParams = getCallAndMethHeadParameters();
List<Variable> newParamList = new ArrayList<Variable>();
int indexOfSelectedParam = -1;
for (Parameter param : originalParams) {
newParamList.add(param);
if (param.getName().equals(variName)) {
indexOfSelectedParam = newParamList.indexOf(param);
}
}
indexOfSelectedParam = reorderParameters(upEvent, numberOfMoves, newParamList, indexOfSelectedParam);
setCallAndMethHeadParameters(newParamList);
return indexOfSelectedParam;
}
private int reorderParameters(boolean upEvent, int numberOfMoves, List<Variable> newParamList, int index) {
int indexOfSelectedParam = index;
// also reorder in originals!
Variable variToMove = newParamList.remove(indexOfSelectedParam);
Variable originalToMove = originalParametersBeforeRename.remove(indexOfSelectedParam);
indexOfSelectedParam = calculateNewIndexAfterMove(upEvent, numberOfMoves, newParamList, indexOfSelectedParam);
newParamList.add(indexOfSelectedParam, variToMove);
originalParametersBeforeRename.add(indexOfSelectedParam, originalToMove);
return indexOfSelectedParam;
}
private int calculateNewIndexAfterMove(boolean upEvent, int numberOfMoves, List<Variable> newParamList, int index) {
int indexOfSelectedParam = index;
if (upEvent) {
if (indexOfSelectedParam < 1)
indexOfSelectedParam = 0;
else
indexOfSelectedParam -= numberOfMoves;
} else {
if (indexOfSelectedParam > newParamList.size() - 1)
indexOfSelectedParam = newParamList.size() - 1;
else
indexOfSelectedParam += numberOfMoves;
}
return indexOfSelectedParam;
}
public void setParameterRename(Map<String, String> variablesToRename) {
this.variablesToRename = variablesToRename;
List<Variable> newParamList = new ArrayList<Variable>();
for (Variable param : originalParametersBeforeRename) {
if (variablesToRename.containsKey(param.getName())) {
// there's an entry for this variable in the map, therefore rename
newParamList.add(new Parameter(param.getOriginType(), variablesToRename.get(param.getName())));
} else {
newParamList.add(param);
}
}
setCallAndMethHeadParameters(newParamList);
observable.setChanged();
observable.notifyObservers();
}
public String getOriginalParameterName(int selectionIndex) {
return originalParametersBeforeRename.get(selectionIndex).getName();
}
}