/******************************************************************************* * 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; } }