/*******************************************************************************
* Copyright (c) 2008, 2010 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences 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:
* Institute for Software - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.extractfunction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNode.CopyStyle;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTOperatorName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.core.dom.parser.c.CASTBinaryExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTBinaryExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompoundStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarationStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTEqualsInitializer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTExpressionStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionCallExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDefinition;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTReturnStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSimpleDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPNodeFactory;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVariableReadWriteFlags;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.ui.refactoring.AddDeclarationNodeToClassChange;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoringDescription;
import org.eclipse.cdt.internal.ui.refactoring.Container;
import org.eclipse.cdt.internal.ui.refactoring.MethodContext;
import org.eclipse.cdt.internal.ui.refactoring.MethodContext.ContextType;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer.NameInformation;
import org.eclipse.cdt.internal.ui.refactoring.utils.ASTHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.CPPASTAllVisitor;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper;
public class ExtractFunctionRefactoring extends CRefactoring {
public static final String ID = "org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractFunctionRefactoring"; //$NON-NLS-1$
static final Integer NULL_INTEGER = Integer.valueOf(0);
static final char[] ZERO= "0".toCharArray(); //$NON-NLS-1$
NodeContainer container;
final ExtractFunctionInformation info;
final Map<String, Integer> names;
final Container<Integer> namesCounter;
final Container<Integer> trailPos;
private final Container<Integer> returnNumber;
protected boolean hasNameResolvingForSimilarError = false;
HashMap<String, Integer> nameTrail;
private ExtractedFunctionConstructionHelper extractedFunctionConstructionHelper;
private final INodeFactory factory = CPPNodeFactory.getDefault();
public ExtractFunctionRefactoring(IFile file, ISelection selection,
ExtractFunctionInformation info, ICProject project) {
super(file, selection, null, project);
this.info = info;
name = Messages.ExtractFunctionRefactoring_ExtractFunction;
names = new HashMap<String, Integer>();
namesCounter = new Container<Integer>(NULL_INTEGER);
trailPos = new Container<Integer>(NULL_INTEGER);
returnNumber = new Container<Integer>(NULL_INTEGER);
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
SubMonitor sm = SubMonitor.convert(pm, 10);
try {
lockIndex();
try {
RefactoringStatus status = super.checkInitialConditions(sm.newChild(6));
if (status.hasError()) {
return status;
}
container = findExtractableNodes();
sm.worked(1);
if (isProgressMonitorCanceld(sm, initStatus))
return initStatus;
checkForNonExtractableStatements(container, initStatus);
sm.worked(1);
if (isProgressMonitorCanceld(sm, initStatus))
return initStatus;
container.findAllNames();
markWriteAccess();
sm.worked(1);
if (isProgressMonitorCanceld(sm, initStatus))
return initStatus;
container.getAllAfterUsedNames();
info.setAllUsedNames(container.getUsedNamesUnique());
if (container.size() < 1) {
initStatus.addFatalError(Messages.ExtractFunctionRefactoring_NoStmtSelected);
sm.done();
return initStatus;
}
if (container.getAllDeclaredInScope().size() > 1) {
initStatus.addFatalError(Messages.ExtractFunctionRefactoring_TooManySelected);
} else if (container.getAllDeclaredInScope().size() == 1) {
info.setInScopeDeclaredVariable(container.getAllDeclaredInScope().get(0));
}
extractedFunctionConstructionHelper =
ExtractedFunctionConstructionHelper.createFor(container.getNodesToWrite());
boolean isExtractExpression = container.getNodesToWrite().get(0) instanceof IASTExpression;
info.setExtractExpression(isExtractExpression);
info.setDeclarator(getDeclaration(container.getNodesToWrite().get(0)));
MethodContext context = NodeHelper.findMethodContext(container.getNodesToWrite().get(0), getIndex());
info.setMethodContext(context);
if (info.getInScopeDeclaredVariable() != null) {
info.getInScopeDeclaredVariable().setUserSetIsReturnValue(true);
}
for (int i = 0; i < info.getAllUsedNames().size(); i++) {
if (!info.getAllUsedNames().get(i).isDeclarationInScope()) {
NameInformation name = info.getAllUsedNames().get(i);
if (!name.isReturnValue()) {
name.setUserSetIsReference(name.isReference());
}
}
}
sm.done();
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return initStatus;
}
private void markWriteAccess() throws CoreException {
List<NameInformation> paras = container.getNames();
for (NameInformation name : paras) {
int flag = CPPVariableReadWriteFlags.getReadWriteFlags(name.getName());
if ((flag & PDOMName.WRITE_ACCESS) != 0) {
name.setWriteAccess(true);
}
}
}
private void checkForNonExtractableStatements(NodeContainer cont, RefactoringStatus status) {
NonExtractableStmtFinder vis = new NonExtractableStmtFinder();
for (IASTNode node : cont.getNodesToWrite()) {
node.accept(vis);
if (vis.containsContinue()) {
initStatus.addFatalError(Messages.ExtractFunctionRefactoring_Error_Continue);
break;
} else if (vis.containsBreak()) {
initStatus.addFatalError(Messages.ExtractFunctionRefactoring_Error_Break);
break;
}
}
ReturnStatementFinder rFinder = new ReturnStatementFinder();
for (IASTNode node : cont.getNodesToWrite()) {
node.accept(rFinder);
if (rFinder.containsReturn()) {
initStatus.addFatalError(Messages.ExtractFunctionRefactoring_Error_Return);
break;
}
}
}
private ICPPASTFunctionDeclarator getDeclaration(IASTNode node) {
while (node != null && !(node instanceof IASTFunctionDefinition)) {
node = node.getParent();
}
if (node != null) {
IASTFunctionDeclarator declarator = ((IASTFunctionDefinition) node).getDeclarator();
if (declarator instanceof ICPPASTFunctionDeclarator) {
return (ICPPASTFunctionDeclarator) declarator;
}
}
return null;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
RefactoringStatus finalConditions = null;
try {
lockIndex();
try {
finalConditions = super.checkFinalConditions(pm);
final IASTName astMethodName = new CPPASTName(info.getMethodName().toCharArray());
MethodContext context = NodeHelper.findMethodContext(container.getNodesToWrite().get(0), getIndex());
if (context.getType() == ContextType.METHOD && !context.isInline()) {
ICPPASTCompositeTypeSpecifier classDeclaration =
(ICPPASTCompositeTypeSpecifier) context.getMethodDeclaration().getParent();
IASTSimpleDeclaration methodDeclaration = getDeclaration(astMethodName);
if (isMethodAllreadyDefined(methodDeclaration, classDeclaration, getIndex())) {
finalConditions.addError(Messages.ExtractFunctionRefactoring_NameInUse);
return finalConditions;
}
}
for (NameInformation name : info.getAllUsedNames()) {
if (name.isUserSetIsReturnValue()) {
info.setReturnVariable(name);
}
}
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return finalConditions;
}
@Override
protected void collectModifications(IProgressMonitor pm,
ModificationCollector collector) throws CoreException,
OperationCanceledException {
try {
lockIndex();
try {
final IASTName astMethodName = new CPPASTName(info.getMethodName().toCharArray());
MethodContext context = NodeHelper.findMethodContext(container.getNodesToWrite().get(0), getIndex());
// Create Declaration in Class
if (context.getType() == ContextType.METHOD && !context.isInline()) {
createMethodDeclaration(astMethodName, context, collector);
}
// Create Method Definition
IASTNode firstNode = container.getNodesToWrite().get(0);
IPath implPath = new Path(firstNode.getContainingFilename());
final IFile implementationFile =
ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(implPath);
createMethodDefinition(astMethodName, context, firstNode, implementationFile,
collector);
createMethodCalls(astMethodName, implementationFile, context, collector);
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void createMethodCalls(final IASTName astMethodName,
final IFile implementationFile, MethodContext context,
ModificationCollector collector) throws CoreException {
String title;
if (context.getType() == MethodContext.ContextType.METHOD) {
title = Messages.ExtractFunctionRefactoring_CreateMethodCall;
} else {
title = Messages.ExtractFunctionRefactoring_CreateFunctionCall;
}
IASTNode methodCall = getMethodCall(astMethodName);
IASTNode firstNodeToWrite = container.getNodesToWrite().get(0);
ASTRewrite rewriter = collector.rewriterForTranslationUnit(
firstNodeToWrite.getTranslationUnit());
TextEditGroup editGroup = new TextEditGroup(title);
if (methodCall instanceof IASTDeclaration){
CPPASTDeclarationStatement declarationStatement = new CPPASTDeclarationStatement((IASTDeclaration) methodCall);
methodCall = declarationStatement;
}
insertCallintoTree(methodCall, container.getNodesToWrite(), rewriter, editGroup);
if (info.isReplaceDuplicates()) {
replaceSimilar(collector, astMethodName, implementationFile, context.getType());
}
for (IASTNode node : container.getNodesToWrite()) {
if (node != firstNodeToWrite) {
rewriter.remove(node, editGroup);
}
}
}
private void insertCallintoTree(IASTNode methodCall, List<IASTNode> list,
ASTRewrite rewriter, TextEditGroup editGroup) {
IASTNode firstNode = list.get(0);
if (list.size() > 1 && firstNode.getParent() instanceof IASTBinaryExpression &&
firstNode.getParent().getParent() instanceof IASTBinaryExpression) {
IASTBinaryExpression parent = (IASTBinaryExpression) firstNode.getParent();
IASTExpression leftSubTree = parent.getOperand1();
int op = parent.getOperator();
IASTBinaryExpression newParentNode = new CPPASTBinaryExpression();
IASTBinaryExpression rootBinExp = getRootBinExp(parent, list);
newParentNode.setParent(rootBinExp.getParent());
newParentNode.setOperand1(leftSubTree.copy(CopyStyle.withLocations));
newParentNode.setOperator(op);
newParentNode.setOperand2((IASTExpression) methodCall);
rewriter.replace(rootBinExp, newParentNode, editGroup);
} else {
rewriter.replace(firstNode, methodCall, editGroup);
}
}
private IASTBinaryExpression getRootBinExp(IASTBinaryExpression binExp, List<IASTNode> nodeList) {
while(binExp.getParent() instanceof IASTBinaryExpression && nodeList.contains(((IASTBinaryExpression) binExp.getParent()).getOperand2())) {
binExp = (IASTBinaryExpression) binExp.getParent();
}
return binExp;
}
private void createMethodDefinition(final IASTName astMethodName,
MethodContext context, IASTNode firstNode,
final IFile implementationFile, ModificationCollector collector) {
IASTFunctionDefinition node = NodeHelper.findFunctionDefinitionInAncestors(firstNode);
if (node != null) {
String title;
if (context.getType() == MethodContext.ContextType.METHOD) {
title = Messages.ExtractFunctionRefactoring_CreateMethodDef;
} else {
title = Messages.ExtractFunctionRefactoring_CreateFunctionDef;
}
ASTRewrite rewriter = collector.rewriterForTranslationUnit(node.getTranslationUnit());
addMethod(astMethodName, context, rewriter, node, new TextEditGroup(title));
}
}
private void createMethodDeclaration(final IASTName astMethodName,
MethodContext context, ModificationCollector collector) {
ICPPASTCompositeTypeSpecifier classDeclaration = (ICPPASTCompositeTypeSpecifier) context
.getMethodDeclaration().getParent();
IASTSimpleDeclaration methodDeclaration = getDeclaration(collector, astMethodName);
AddDeclarationNodeToClassChange.createChange(classDeclaration, info.getVisibility(),
methodDeclaration, false, collector);
}
private void replaceSimilar(ModificationCollector collector, final IASTName astMethodName,
final IFile implementationFile, final ContextType contextType) {
// Find similar code
final List<IASTNode> nodesToRewriteWithoutComments = new LinkedList<IASTNode>();
for (IASTNode node : container.getNodesToWrite()) {
if (!(node instanceof IASTComment)) {
nodesToRewriteWithoutComments.add(node);
}
}
final Vector<IASTNode> initTrail = getTrail(nodesToRewriteWithoutComments);
final String title;
if (contextType == MethodContext.ContextType.METHOD) {
title = Messages.ExtractFunctionRefactoring_CreateMethodCall;
} else {
title = Messages.ExtractFunctionRefactoring_CreateFunctionCall;
}
if (!hasNameResolvingForSimilarError) {
ast.accept(new SimilarFinderVisitor(this, collector, initTrail, implementationFile,
astMethodName, nodesToRewriteWithoutComments, title));
}
}
protected Vector<IASTNode> getTrail(List<IASTNode> stmts) {
final Vector<IASTNode> trail = new Vector<IASTNode>();
nameTrail = new HashMap<String, Integer>();
final Container<Integer> trailCounter = new Container<Integer>(NULL_INTEGER);
for (IASTNode node : stmts) {
node.accept(new CPPASTAllVisitor() {
@Override
public int visitAll(IASTNode node) {
if (node instanceof IASTComment) {
// Visit Comment, but don't add them to the trail
return super.visitAll(node);
} else if (node instanceof IASTNamedTypeSpecifier) {
// Skip if somewhere is a named Type Specifier
trail.add(node);
return PROCESS_SKIP;
} else if (node instanceof IASTName) {
if (node instanceof ICPPASTConversionName && node instanceof ICPPASTOperatorName
&& node instanceof ICPPASTTemplateId) {
trail.add(node);
return super.visitAll(node);
} else {
// Save Name Sequence Number
IASTName name = (IASTName) node;
TrailName trailName = new TrailName(name);
int actCount = trailCounter.getObject().intValue();
if (nameTrail.containsKey(name.getRawSignature())) {
Integer value = nameTrail.get(name.getRawSignature());
actCount = value.intValue();
} else {
trailCounter.setObject(Integer.valueOf(++actCount));
nameTrail.put(name.getRawSignature(), trailCounter.getObject());
}
trailName.setNameNumber(actCount);
if (info.getReturnVariable() != null
&& info.getReturnVariable().getName().getRawSignature().equals(
name.getRawSignature())) {
returnNumber.setObject(Integer.valueOf(actCount));
}
trail.add(trailName);
return PROCESS_SKIP;
}
} else {
trail.add(node);
return super.visitAll(node);
}
}
});
}
return trail;
}
protected boolean isStatementInTrail(IASTStatement stmt, final Vector<IASTNode> trail, IIndex index) {
final Container<Boolean> same = new Container<Boolean>(Boolean.TRUE);
final TrailNodeEqualityChecker equalityChecker = new TrailNodeEqualityChecker(names, namesCounter, index);
stmt.accept(new CPPASTAllVisitor() {
@Override
public int visitAll(IASTNode node) {
int pos = trailPos.getObject().intValue();
if (trail.size() <= 0 || pos >= trail.size()) {
same.setObject(Boolean.FALSE);
return PROCESS_ABORT;
}
if (node instanceof IASTComment) {
// Visit Comment, but they are not in the trail
return super.visitAll(node);
}
IASTNode trailNode = trail.get(pos);
trailPos.setObject(Integer.valueOf(pos + 1));
if (equalityChecker.isEquals(trailNode, node)) {
if (node instanceof ICPPASTQualifiedName || node instanceof IASTNamedTypeSpecifier) {
return PROCESS_SKIP;
} else {
return super.visitAll(node);
}
} else {
same.setObject(new Boolean(false));
return PROCESS_ABORT;
}
}
});
return same.getObject().booleanValue();
}
private boolean isMethodAllreadyDefined(IASTSimpleDeclaration methodDeclaration,
ICPPASTCompositeTypeSpecifier classDeclaration, IIndex index) {
TrailNodeEqualityChecker equalityChecker = new TrailNodeEqualityChecker(
names, namesCounter, index);
IBinding bind = classDeclaration.getName().resolveBinding();
IASTStandardFunctionDeclarator declarator =
(IASTStandardFunctionDeclarator) methodDeclaration.getDeclarators()[0];
String name = new String(declarator.getName().toCharArray());
if (bind instanceof ICPPClassType) {
ICPPClassType classBind = (ICPPClassType) bind;
IField[] fields = classBind.getFields();
for (IField field : fields) {
if (field.getName().equals(name)) {
return true;
}
}
ICPPMethod[] methods = classBind.getAllDeclaredMethods();
for (ICPPMethod method : methods) {
if (!method.takesVarArgs() && name.equals(method.getName())) {
IParameter[] parameters = method.getParameters();
if (parameters.length == declarator.getParameters().length) {
for (int i = 0; i < parameters.length; i++) {
IASTName[] origParameterName = ast.getDeclarationsInAST(parameters[i]);
IASTParameterDeclaration origParameter =
(IASTParameterDeclaration) origParameterName[0].getParent().getParent();
IASTParameterDeclaration newParameter = declarator.getParameters()[i];
// if not the same break;
if (!(equalityChecker.isEquals(origParameter.getDeclSpecifier(),
newParameter.getDeclSpecifier()) &&
ASTHelper.samePointers(origParameter.getDeclarator().getPointerOperators(),
newParameter.getDeclarator().getPointerOperators(),
equalityChecker))) {
break;
}
if (!(i < (parameters.length - 1))) {
return true;
}
}
}
}
}
return false;
}
return true;
}
private void addMethod(IASTName astMethodName, MethodContext context,
ASTRewrite rewriter, IASTNode insertpoint, TextEditGroup group) {
ICPPASTQualifiedName qname = new CPPASTQualifiedName();
if (context.getType() == ContextType.METHOD) {
if (context.getMethodQName() != null) {
for (int i = 0; i < (context.getMethodQName().getNames().length - 1); i++) {
qname.addName(new CPPASTName(context.getMethodQName().getNames()[i].toCharArray()));
}
}
}
qname.addName(astMethodName);
IASTFunctionDefinition func = new CPPASTFunctionDefinition();
func.setParent(ast);
IASTDeclSpecifier returnType = getReturnType();
func.setDeclSpecifier(returnType);
IASTStandardFunctionDeclarator createdFunctionDeclarator = extractedFunctionConstructionHelper
.createFunctionDeclarator(qname, info.getDeclarator(), info
.getReturnVariable(), container.getNodesToWrite(), info
.getAllUsedNames(), ast.getASTNodeFactory());
func.setDeclarator(createdFunctionDeclarator);
IASTCompoundStatement compound = new CPPASTCompoundStatement();
func.setBody(compound);
ASTRewrite subRW;
if (insertpoint.getParent() instanceof ICPPASTTemplateDeclaration) {
CPPASTTemplateDeclaration templateDeclaration = new CPPASTTemplateDeclaration();
templateDeclaration.setParent(ast);
for (ICPPASTTemplateParameter templateParameter : ((ICPPASTTemplateDeclaration) insertpoint.getParent()).getTemplateParameters()) {
templateDeclaration.addTemplateParameter(templateParameter.copy(CopyStyle.withLocations));
}
templateDeclaration.setDeclaration(func);
subRW = rewriter.insertBefore(insertpoint.getParent().getParent(), insertpoint.getParent(),
templateDeclaration, group);
} else {
subRW = rewriter.insertBefore(insertpoint.getParent(), insertpoint, func, group);
}
extractedFunctionConstructionHelper.constructMethodBody(compound,
container.getNodesToWrite(), subRW, group);
// Set return value
NameInformation returnVariable = info.getReturnVariable();
if (returnVariable != null) {
IASTReturnStatement returnStmt = new CPPASTReturnStatement();
if (returnVariable.getDeclaration().getParent() instanceof IASTExpression) {
IASTExpression returnValue = (IASTExpression) returnVariable.getDeclaration().getParent();
returnStmt.setReturnValue(returnValue);
} else {
IASTIdExpression expr = new CPPASTIdExpression();
if (returnVariable.getUserSetName() == null) {
expr.setName(newName(returnVariable.getName()));
} else {
expr.setName(new CPPASTName(returnVariable.getUserSetName().toCharArray()));
}
returnStmt.setReturnValue(expr);
}
subRW.insertBefore(compound, null, returnStmt, group);
}
}
private IASTName newName(IASTName declaration) {
return new CPPASTName(declaration.toCharArray());
}
private IASTDeclSpecifier getReturnType() {
IASTNode firstNodeToWrite = container.getNodesToWrite().get(0);
NameInformation returnVariable = info.getReturnVariable();
return extractedFunctionConstructionHelper.determineReturnType(firstNodeToWrite, returnVariable);
}
protected IASTNode getMethodCall(IASTName astMethodName,
Map<String, Integer> trailNameTable,
Map<String, Integer> similarNameTable, NodeContainer myContainer,
NodeContainer mySimilarContainer) {
IASTExpressionStatement stmt = new CPPASTExpressionStatement();
IASTFunctionCallExpression callExpression = new CPPASTFunctionCallExpression();
IASTIdExpression idExpression = new CPPASTIdExpression();
idExpression.setName(astMethodName);
List<IASTInitializerClause> args = new ArrayList<IASTInitializerClause>();
Vector<IASTName> declarations = new Vector<IASTName>();
IASTName retName = null;
boolean theRetName = false;
for (NameInformation nameInfo : myContainer.getNames()) {
Integer trailSeqNumber = trailNameTable.get(nameInfo.getDeclaration().getRawSignature());
String orgName = null;
for (Entry<String, Integer> entry : similarNameTable.entrySet()) {
if (entry.getValue().equals(trailSeqNumber)) {
orgName = entry.getKey();
if (info.getReturnVariable() != null
&& trailSeqNumber.equals(returnNumber.getObject())) {
theRetName = true;
}
}
}
if (orgName != null) {
boolean found = false;
for (NameInformation simNameInfo : mySimilarContainer.getNames()) {
if (orgName.equals(simNameInfo.getDeclaration()
.getRawSignature())) {
addAParameterIfPossible(args, declarations,
simNameInfo);
found = true;
if (theRetName) {
theRetName = false;
retName = new CPPASTName(
simNameInfo.getDeclaration().getRawSignature().toCharArray());
}
}
}
if (!found) {
// should be a field, use the old name
IASTIdExpression expression = new CPPASTIdExpression();
CPPASTName fieldName = new CPPASTName(orgName.toCharArray());
expression.setName(fieldName);
args.add(expression);
if (theRetName) {
theRetName = false;
retName = fieldName;
}
}
}
}
callExpression.setArguments(args.toArray(new IASTInitializerClause[args.size()]));
callExpression.setFunctionNameExpression(idExpression);
if (info.getReturnVariable() == null) {
return getReturnAssignment(stmt, callExpression);
}
return getReturnAssignment(stmt, callExpression, retName);
}
private IASTNode getMethodCall(IASTName astMethodName) {
IASTExpressionStatement stmt = new CPPASTExpressionStatement();
IASTFunctionCallExpression callExpression = new CPPASTFunctionCallExpression();
IASTIdExpression idExpression = new CPPASTIdExpression();
idExpression.setName(new CPPASTName(astMethodName.toCharArray()));
List<IASTInitializerClause> args = getCallParameters();
callExpression.setArguments(args.toArray(new IASTInitializerClause[args.size()]));
callExpression.setFunctionNameExpression(idExpression);
if (info.getReturnVariable() == null) {
return getReturnAssignment(stmt, callExpression);
}
IASTName retname = newName(info.getReturnVariable().getName());
return getReturnAssignment(stmt, callExpression, retname);
}
private IASTNode getReturnAssignment(IASTExpressionStatement stmt,
IASTFunctionCallExpression callExpression, IASTName retname) {
if (info.getReturnVariable().equals(info.getInScopeDeclaredVariable())) {
IASTSimpleDeclaration orgDecl = NodeHelper.findSimpleDeclarationInParents(info
.getReturnVariable().getDeclaration());
IASTSimpleDeclaration decl = new CPPASTSimpleDeclaration();
decl.setDeclSpecifier(orgDecl.getDeclSpecifier().copy(CopyStyle.withLocations));
IASTDeclarator declarator = new CPPASTDeclarator();
declarator.setName(retname);
for (IASTPointerOperator pointer : orgDecl.getDeclarators()[0].getPointerOperators()) {
declarator.addPointerOperator(pointer.copy(CopyStyle.withLocations));
}
IASTEqualsInitializer initializer = new CPPASTEqualsInitializer();
initializer.setInitializerClause(callExpression);
declarator.setInitializer(initializer);
decl.addDeclarator(declarator);
return decl;
}
IASTBinaryExpression binaryExpression = new CASTBinaryExpression();
binaryExpression.setOperator(IASTBinaryExpression.op_assign);
IASTIdExpression nameExpression = new CPPASTIdExpression();
nameExpression.setName(retname);
binaryExpression.setOperand1(nameExpression);
binaryExpression.setOperand2(callExpression);
return getReturnAssignment(stmt, binaryExpression);
}
private IASTNode getReturnAssignment(IASTExpressionStatement stmt,
IASTExpression callExpression) {
IASTNode node = container.getNodesToWrite().get(0);
return extractedFunctionConstructionHelper.createReturnAssignment(node,
stmt, callExpression);
}
private IASTSimpleDeclaration getDeclaration(IASTName name) {
IASTSimpleDeclaration simpleDecl = new CPPASTSimpleDeclaration();
IASTStandardFunctionDeclarator declarator =
extractedFunctionConstructionHelper.createFunctionDeclarator(name,
info.getDeclarator(), info.getReturnVariable(),
container.getNodesToWrite(), info.getAllUsedNames(), ast.getASTNodeFactory());
simpleDecl.addDeclarator(declarator);
return simpleDecl;
}
private IASTSimpleDeclaration getDeclaration(ModificationCollector collector,IASTName name) {
IASTDeclSpecifier declSpec = getReturnType();
IASTSimpleDeclaration simpleDecl = factory.newSimpleDeclaration(declSpec);
if (info.isVirtual() && declSpec instanceof ICPPASTDeclSpecifier) {
((ICPPASTDeclSpecifier)declSpec).setVirtual(true);
}
simpleDecl.setParent(ast);
IASTStandardFunctionDeclarator declarator = extractedFunctionConstructionHelper
.createFunctionDeclarator(name, info.getDeclarator(), info
.getReturnVariable(), container.getNodesToWrite(), info
.getAllUsedNames(), ast.getASTNodeFactory());
simpleDecl.addDeclarator(declarator);
return simpleDecl;
}
private NodeContainer findExtractableNodes() {
final NodeContainer container = new NodeContainer();
ast.accept(new ASTVisitor() {
{
shouldVisitStatements = true;
shouldVisitExpressions = true;
}
@Override
public int visit(IASTStatement stmt) {
if ( SelectionHelper.isSelectedFile(region, stmt, file)) {
container.add(stmt);
return PROCESS_SKIP;
}
return super.visit(stmt);
}
@Override
public int visit(IASTExpression expression) {
if (SelectionHelper.isSelectedFile(region, expression, file)) {
container.add(expression);
return PROCESS_SKIP;
}
return super.visit(expression);
}
});
return container;
}
public List<IASTInitializerClause> getCallParameters() {
List<IASTInitializerClause> args = new ArrayList<IASTInitializerClause>();
Vector<IASTName> declarations = new Vector<IASTName>();
for (NameInformation nameInf : container.getNames()) {
addAParameterIfPossible(args, declarations, nameInf);
}
return args;
}
private void addAParameterIfPossible(List<IASTInitializerClause> args,
Vector<IASTName> declarations, NameInformation nameInf) {
if (!nameInf.isDeclarationInScope()) {
IASTName declaration = nameInf.getDeclaration();
if (!declarations.contains(declaration)) {
declarations.add(declaration);
IASTIdExpression expression = new CPPASTIdExpression();
expression.setName(newName(declaration));
args.add(expression);
}
}
}
@Override
protected RefactoringDescriptor getRefactoringDescriptor() {
Map<String, String> arguments = getArgumentMap();
RefactoringDescriptor desc = new ExtractFunctionRefactoringDescription( project.getProject().getName(), "Extract Method Refactoring", "Create method " + info.getMethodName(), arguments); //$NON-NLS-1$//$NON-NLS-2$
return desc;
}
private Map<String, String> getArgumentMap() {
Map<String, String> arguments = new HashMap<String, String>();
arguments.put(CRefactoringDescription.FILE_NAME, file.getLocationURI().toString());
arguments.put(CRefactoringDescription.SELECTION, region.getOffset() + "," + region.getLength()); //$NON-NLS-1$
arguments.put(ExtractFunctionRefactoringDescription.NAME, info.getMethodName());
arguments.put(ExtractFunctionRefactoringDescription.VISIBILITY, info.getVisibility().toString());
arguments.put(ExtractFunctionRefactoringDescription.REPLACE_DUBLICATES, Boolean.toString(info.isReplaceDuplicates()));
return arguments;
}
}