/*******************************************************************************
* Copyright (c) 2008, 2013 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
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.extractfunction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
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.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.internal.ui.refactoring.NameInformation;
import org.eclipse.cdt.internal.ui.refactoring.NameInformation.Indirection;
/**
* @author Mirko Stocker
*/
public abstract class FunctionExtractor {
public static FunctionExtractor createFor(List<IASTNode> list) {
if (list.get(0) instanceof IASTExpression) {
return new ExpressionExtractor();
}
return new StatementExtractor();
}
public abstract boolean canChooseReturnValue();
public abstract void constructMethodBody(IASTCompoundStatement compound, List<IASTNode> nodes,
List<NameInformation> parameters, ASTRewrite rewrite, TextEditGroup group);
/**
* Returns the declarator specifier and the pointer operators for the return type of
* the extracted function.
*
* @param extractedNode the first extracted node, used for expression extraction
* @param returnVariable the return variable or {@code null} if the there is no return variable
* @param pointerOperators output parameter - pointer operators for the function declarator
* @return the declarator specifier of the function
*/
public abstract IASTDeclSpecifier determineReturnType(IASTNode extractedNode,
NameInformation returnVariable, List<IASTPointerOperator> pointerOperators);
public abstract IASTNode createReturnAssignment(IASTNode node, IASTExpressionStatement stmt,
IASTExpression callExpression);
IASTStandardFunctionDeclarator createFunctionDeclarator(IASTName name,
IASTStandardFunctionDeclarator functionDeclarator, NameInformation returnVariable,
List<IASTNode> nodesToWrite, Collection<NameInformation> allUsedNames,
INodeFactory nodeFactory) {
IASTStandardFunctionDeclarator declarator = nodeFactory.newFunctionDeclarator(name);
if (functionDeclarator instanceof ICPPASTFunctionDeclarator &&
declarator instanceof ICPPASTFunctionDeclarator) {
if (((ICPPASTFunctionDeclarator) functionDeclarator).isConst()) {
((ICPPASTFunctionDeclarator) declarator).setConst(true);
}
}
for (IASTParameterDeclaration param : getParameterDeclarations(allUsedNames, nodeFactory)) {
declarator.addParameterDeclaration(param);
}
return declarator;
}
public List<IASTParameterDeclaration> getParameterDeclarations(
Collection<NameInformation> parameterNames, INodeFactory nodeFactory) {
List<IASTParameterDeclaration> result = new ArrayList<>(parameterNames.size());
for (NameInformation param : parameterNames) {
result.add(param.getParameterDeclaration(nodeFactory));
}
return result;
}
/**
* Adjusts parameter references under the given node to account for renamed parameters and
* parameters passed by pointer.
*
* @param node the root node of the AST subtree to be adjusted
* @param changedParameters the map from references to changed parameters to parameters
* themselves
* @param rewrite the rewrite for the node
* @param group the edit group to add the changes to
*/
protected static void adjustParameterReferences(IASTNode node,
final Map<IASTName, NameInformation> changedParameters, final INodeFactory nodeFactory,
final ASTRewrite rewrite, final TextEditGroup group) {
if (changedParameters.isEmpty())
return;
node.accept(new ASTVisitor() {
{
shouldVisitNames = true;
}
@Override
public int visit(IASTName name) {
NameInformation param = changedParameters.get(name.getOriginalNode());
if (param != null) {
IASTName newName = null;
if (param.isRenamed()) {
newName = nodeFactory.newName(param.getNewName().toCharArray());
}
if (param.getIndirection() == Indirection.POINTER &&
name.getPropertyInParent() == IASTIdExpression.ID_NAME) {
IASTIdExpression idExp = (IASTIdExpression) name.getParent();
if (idExp.getPropertyInParent() == IASTFieldReference.FIELD_OWNER &&
!((IASTFieldReference) idExp.getParent()).isPointerDereference()) {
IASTFieldReference dotRef = (IASTFieldReference) idExp.getParent();
IASTFieldReference arrowRef = dotRef.copy(CopyStyle.withLocations);
arrowRef.setIsPointerDereference(true);
if (newName != null) {
idExp = (IASTIdExpression) arrowRef.getFieldOwner();
idExp.setName(newName);
}
rewrite.replace(dotRef, arrowRef, group);
} else {
IASTIdExpression newIdExp = idExp.copy(CopyStyle.withLocations);
IASTUnaryExpression starExp =
nodeFactory.newUnaryExpression(IASTUnaryExpression.op_star, newIdExp);
if (newName != null) {
newIdExp.setName(newName);
}
rewrite.replace(idExp, starExp, group);
}
} else if (newName != null) {
rewrite.replace(name, newName, group);
}
}
return super.visit(name);
}
});
}
/**
* Returns a map from references to parameters inside the body of the extracted function to
* the parameters themselves. The map includes only the parameters that are either renamed,
* or passed by pointer.
*
* @param parameters the function parameters.
* @return a map from references to parameters to parameters themselves.
*/
protected static Map<IASTName, NameInformation> getChangedParameterReferences(List<NameInformation> parameters) {
final Map<IASTName, NameInformation> referenceLookupMap = new HashMap<>();
for (NameInformation param : parameters) {
if (param.isRenamed() || param.getIndirection() == Indirection.POINTER) {
for (IASTName name : param.getReferencesInSelection()) {
referenceLookupMap.put(name, param);
}
}
}
return referenceLookupMap;
}
}