/******************************************************************************* * Copyright (c) 2011, 2016 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: * Martin Schwab & Thomas Kallenberg - initial API and implementation * Marc-Andre Laperle (Ericsson) * Thomas Corbat (IFS) ******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.togglefunction; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Stack; import org.eclipse.cdt.core.dom.ast.ASTNodeFactoryFactory; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; 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.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter; 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.ICPPASTTypeId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; import org.eclipse.cdt.core.dom.rewrite.ASTRewrite; import org.eclipse.cdt.core.dom.rewrite.ASTRewrite.CommentPosition; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; import org.eclipse.cdt.internal.core.dom.rewrite.DeclarationGeneratorImpl; import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper; public class ToggleNodeHelper extends NodeHelper { private static final String EMPTY_STRING = ""; //$NON-NLS-1$ private static final ICPPNodeFactory factory = ASTNodeFactoryFactory.getDefaultCPPNodeFactory(); private static void removeParameterInitializations(IASTFunctionDeclarator funcDecl) { for (IASTNode child : funcDecl.getChildren()) { if (child instanceof IASTParameterDeclaration) { IASTParameterDeclaration parameter = (IASTParameterDeclaration) child; parameter.getDeclarator().setInitializer(null); } } } private static List<ICPPASTConstructorChainInitializer> getInitializerList(IASTFunctionDefinition definition) { ArrayList<ICPPASTConstructorChainInitializer> initalizers = new ArrayList<ICPPASTConstructorChainInitializer>(); for (IASTNode node : definition.getChildren()) { if (node instanceof ICPPASTConstructorChainInitializer) { initalizers.add(((ICPPASTConstructorChainInitializer) node).copy(CopyStyle.withLocations)); } } return initalizers; } static IASTSimpleDeclaration createDeclarationFromDefinition(IASTFunctionDefinition oldDefinition) { IASTDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations); IASTDeclSpecifier newDeclSpec = oldDefinition.getDeclSpecifier().copy(CopyStyle.withLocations); IASTSimpleDeclaration newDeclaration = factory.newSimpleDeclaration(newDeclSpec); newDeclaration.addDeclarator(newDeclarator); return newDeclaration; } static ICPPASTFunctionDefinition createFunctionSignatureWithEmptyBody( IASTDeclSpecifier newDeclSpec, IASTFunctionDeclarator newFuncDecl, IASTFunctionDefinition oldDefinition) { ICPPASTFunctionDefinition newFunc = null; newFuncDecl = adjustParamNames(newFuncDecl, oldDefinition); if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) { newFunc = factory.newFunctionTryBlock(newDeclSpec, newFuncDecl, factory.newCompoundStatement()); } else { newFunc = factory.newFunctionDefinition(newDeclSpec, newFuncDecl, factory.newCompoundStatement()); } copyInitializerList(newFunc, oldDefinition); return newFunc; } private static IASTFunctionDeclarator adjustParamNames(IASTFunctionDeclarator newFuncDecl, IASTFunctionDefinition oldDefinition) { if (oldDefinition.getDeclarator() instanceof IASTStandardFunctionDeclarator) { IASTStandardFunctionDeclarator oldStdDec = (IASTStandardFunctionDeclarator) oldDefinition.getDeclarator(); IASTParameterDeclaration[] definitionParams = oldStdDec.getParameters(); IASTParameterDeclaration[] declarationParams = ((IASTStandardFunctionDeclarator)newFuncDecl).getParameters(); for(int i = 0; i < declarationParams.length; ++i) { declarationParams[i].getDeclarator().setName(definitionParams[i].getDeclarator().getName().copy(CopyStyle.withLocations)); } } return newFuncDecl; } private static void copyInitializerList(ICPPASTFunctionDefinition newFunc, IASTFunctionDefinition oldFunc) { for (ICPPASTConstructorChainInitializer initializer : getInitializerList(oldFunc)) { initializer.setParent(newFunc); newFunc.addMemberInitializer(initializer); } } static IASTFunctionDefinition getQualifiedNameDefinition(IASTFunctionDefinition oldDefinition, IASTTranslationUnit definitionUnit, IASTNode nameSpace) { ICPPASTDeclSpecifier newDeclSpecifier = createDeclSpecifier(oldDefinition); IASTFunctionDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations); newDeclarator.setName(getQualifiedName(oldDefinition.getDeclarator(), nameSpace)); removeParameterInitializations(newDeclarator); ICPPASTFunctionDefinition newFunction = createFunctionSignatureWithEmptyBody(newDeclSpecifier, newDeclarator, oldDefinition); return newFunction; } private static ICPPASTDeclSpecifier createDeclSpecifier(IASTFunctionDefinition oldDefinition) { IASTDeclSpecifier originalDeclSpecifier = oldDefinition.getDeclSpecifier(); ICPPASTDeclSpecifier newDeclSpecifier = (ICPPASTDeclSpecifier) originalDeclSpecifier.copy(CopyStyle.withLocations); if (newDeclSpecifier instanceof ICPPASTNamedTypeSpecifier) { ICPPASTNamedTypeSpecifier newNamedTypeSpecifier = (ICPPASTNamedTypeSpecifier) newDeclSpecifier; IASTName typename = ((ICPPASTNamedTypeSpecifier) originalDeclSpecifier).getName(); IBinding typenameBinding = typename.resolveBinding(); adaptTemplateQualifiers(newNamedTypeSpecifier, typenameBinding); } newDeclSpecifier.setVirtual(false); newDeclSpecifier.setInline(true); newDeclSpecifier.setStorageClass(IASTDeclSpecifier.sc_unspecified); return newDeclSpecifier; } private static void adaptTemplateQualifiers(ICPPASTNamedTypeSpecifier newDeclSpecifier, IBinding typenameBinding) { if (typenameBinding instanceof ICPPBinding) { try { String[] nameParts = ((ICPPBinding) typenameBinding).getQualifiedName(); String qualifiedName = typenameBinding.getName(); String[] nameQualifiers = Arrays.copyOf(nameParts, nameParts.length - 1); ICPPASTQualifiedName qualifiedTypeName = factory.newQualifiedName(nameQualifiers, qualifiedName); if (typenameBinding instanceof ICPPTemplateInstance) { ICPPTemplateInstance templateInstance = (ICPPTemplateInstance) typenameBinding; ICPPTemplateArgument[] templateArguments = templateInstance.getTemplateArguments(); IASTName lastName = qualifiedTypeName.getLastName(); ICPPASTTemplateId newTemplateId = createTemplateIdForArguments(lastName, templateArguments); qualifiedTypeName.setLastName(newTemplateId); } boolean setTypename = adaptQualifiers(typenameBinding, qualifiedTypeName); newDeclSpecifier.setName(qualifiedTypeName); newDeclSpecifier.setIsTypename(setTypename); } catch (DOMException e) { CUIPlugin.log(e); } } } private static boolean adaptQualifiers(IBinding typenameBinding, ICPPASTQualifiedName qualifiedTypeName) { boolean setTypename = false; IBinding owner = typenameBinding.getOwner(); ICPPASTNameSpecifier[] qualifiers = qualifiedTypeName.getQualifier(); if (qualifiers.length > 0) { int level = qualifiers.length - 1; while (owner != null && level >= 0) { IASTName qualifierName = (IASTName) qualifiers[level]; ICPPASTTemplateId newTemplateId = createTemplateIdForQualifier(qualifierName, owner); if (newTemplateId != null) { qualifiers[level] = newTemplateId; newTemplateId.setParent(qualifiedTypeName); setTypename = true; } owner = owner.getOwner(); level--; } } return setTypename; } private static ICPPASTTemplateId createTemplateIdForQualifier(IASTName qualifierName, IBinding templateBinding) { ICPPASTTemplateId newTemplateId = null; if (templateBinding instanceof ICPPClassTemplate) { ICPPClassTemplate classTemplateBinding = (ICPPClassTemplate) templateBinding; newTemplateId = createTemplateId(qualifierName, classTemplateBinding); } else if (templateBinding instanceof ICPPTemplateInstance) { ICPPTemplateInstance instanceBinding = (ICPPTemplateInstance) templateBinding; ICPPTemplateArgument[] templateArguments = instanceBinding.getTemplateArguments(); newTemplateId = createTemplateIdForArguments(qualifierName, templateArguments); } return newTemplateId; } private static ICPPASTTemplateId createTemplateIdForArguments(IASTName qualifierName, ICPPTemplateArgument[] templateArguments) { ICPPASTTemplateId newTemplateId = factory.newTemplateId(qualifierName); for (ICPPTemplateArgument templateArgument : templateArguments) { IType type = templateArgument.getOriginalTypeValue(); DeclarationGeneratorImpl declarationGeneratorImpl = new DeclarationGeneratorImpl(factory); IASTDeclarator abstractDeclarator = declarationGeneratorImpl.createDeclaratorFromType(type, EMPTY_STRING.toCharArray()); IType ultimateType = SemanticUtil.getUltimateType(type, false); IASTName templateParameterName = factory.newName(ASTTypeUtil.getType(ultimateType, false)); ICPPASTNamedTypeSpecifier typeSpecifier = factory.newNamedTypeSpecifier(templateParameterName); ICPPASTTypeId newTypeId = factory.newTypeId(typeSpecifier, abstractDeclarator); newTemplateId.addTemplateArgument(newTypeId); } return newTemplateId; } private static ICPPASTTemplateId createTemplateId(IASTName qualifierName, ICPPClassTemplate ownerBinding) { ICPPASTTemplateId newTemplateId = factory.newTemplateId(qualifierName); for (ICPPTemplateParameter templateParameter : ownerBinding.getTemplateParameters()) { IASTName abstractDeclaratorName = factory.newName(); ICPPASTDeclarator abstractDeclarator = factory.newDeclarator(abstractDeclaratorName); ICPPASTName templateParameterName = factory.newName(templateParameter.getNameCharArray()); ICPPASTNamedTypeSpecifier typeSpecifier = factory.newNamedTypeSpecifier(templateParameterName); ICPPASTTypeId newTypeId = factory.newTypeId(typeSpecifier, abstractDeclarator); newTemplateId.addTemplateArgument(newTypeId); } return newTemplateId; } public static ICPPASTTemplateDeclaration getTemplateDeclaration( IASTFunctionDefinition oldFunction, IASTFunctionDefinition newFunction) { ArrayList<ICPPASTTemplateDeclaration> templateDeclarations = getAllTemplateDeclarations(oldFunction); return addTemplateDeclarationsInOrder(templateDeclarations, newFunction); } private static ICPPASTTemplateDeclaration addTemplateDeclarationsInOrder( ArrayList<ICPPASTTemplateDeclaration> templDecs, IASTFunctionDefinition newFunction) { ICPPASTTemplateDeclaration child = null; for (ICPPASTTemplateDeclaration templateDeclaration : templDecs) { if (templateDeclaration.getTemplateParameters().length == 0) { continue; } if (child == null) { child = templateDeclaration; child.setDeclaration(newFunction); } else { templateDeclaration.setDeclaration(child); child.setParent(templateDeclaration); child = templateDeclaration; } } return child; } private static ArrayList<ICPPASTTemplateDeclaration> getAllTemplateDeclarations(IASTNode node) { ArrayList<ICPPASTTemplateDeclaration> templdecs = new ArrayList<ICPPASTTemplateDeclaration>(); while (node.getParent() != null) { node = node.getParent(); if (node instanceof ICPPASTTemplateDeclaration) { templdecs.add((ICPPASTTemplateDeclaration) node.copy(CopyStyle.withLocations)); } } return templdecs; } static IASTFunctionDefinition createInClassDefinition(IASTFunctionDeclarator dec, IASTFunctionDefinition def, IASTTranslationUnit insertionAst) { IASTFunctionDeclarator declarator = dec.copy(CopyStyle.withLocations); IASTDeclSpecifier declSpec = def.getDeclSpecifier().copy(CopyStyle.withLocations); declSpec.setInline(false); if (declSpec instanceof ICPPASTDeclSpecifier && ToggleNodeHelper.isVirtual(dec)) { ((ICPPASTDeclSpecifier) declSpec).setVirtual(true); } declSpec.setStorageClass(getStorageClass(dec)); return createFunctionSignatureWithEmptyBody(declSpec, declarator, def); } static boolean isVirtual(IASTFunctionDeclarator fdec) { if (fdec.getParent() instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent(); return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).isVirtual(); } return false; } static int getStorageClass(IASTFunctionDeclarator fdec) { if (fdec.getParent() instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent(); return dec.getDeclSpecifier().getStorageClass(); } return -1; } static IASTNode getParentRemovePoint(IASTFunctionDefinition definition) { IASTNode toremove = definition; while (toremove.getParent() != null && toremove.getParent() instanceof ICPPASTTemplateDeclaration) { toremove = toremove.getParent(); } return toremove; } /** * @param declarator the declarator from which the full qualified namespace should be fetched * @param limiter set a limiter in the class hierarchy where the lookup will stop * @return */ static ICPPASTQualifiedName getQualifiedName(IASTFunctionDeclarator declarator, IASTNode limiter) { Stack<IASTNode> nodes = getQualifiedNames(declarator, limiter, declarator); ICPPASTQualifiedName qName = reAssembleQualifiedName(nodes); qName.addName(declarator.getName().copy(CopyStyle.withLocations)); return qName; } private static ICPPASTQualifiedName reAssembleQualifiedName(Stack<IASTNode> nodes) { ICPPASTQualifiedName qName = factory.newQualifiedName(null); while (!nodes.isEmpty()) { IASTNode nnode = nodes.pop(); if (nnode instanceof IASTCompositeTypeSpecifier) { qName.addName(((IASTCompositeTypeSpecifier) nnode).getName()); } else if (nnode instanceof ICPPASTNamespaceDefinition) { qName.addName(((ICPPASTNamespaceDefinition) nnode).getName()); } else if (nnode instanceof ICPPASTTemplateId) { qName.addName((ICPPASTTemplateId) nnode); } } return qName; } private static Stack<IASTNode> getQualifiedNames(IASTFunctionDeclarator declarator, IASTNode limiter, IASTNode node) { IASTName lastName = declarator.getName(); Stack<IASTNode> nodes = new Stack<IASTNode>(); while (node.getParent() != null && node.getParent() != limiter) { node = node.getParent(); if (node instanceof IASTCompositeTypeSpecifier) { nodes.push(((IASTCompositeTypeSpecifier) node).copy(CopyStyle.withLocations)); lastName = ((IASTCompositeTypeSpecifier) node).getName(); } else if (node instanceof ICPPASTNamespaceDefinition) { nodes.push(((ICPPASTNamespaceDefinition) node).copy(CopyStyle.withLocations)); lastName = ((ICPPASTNamespaceDefinition) node).getName(); } else if (!(lastName instanceof ICPPASTTemplateId) && shouldAddTemplateBrackets(node)) { if (!nodes.isEmpty()) nodes.pop(); ICPPASTTemplateId templateID = getTemplateParameter(node, lastName); nodes.add(templateID); } } return nodes; } private static boolean shouldAddTemplateBrackets(IASTNode node) { return node instanceof ICPPASTTemplateDeclaration && !(((ICPPASTTemplateDeclaration) node).getDeclaration() instanceof ICPPASTFunctionDefinition); } private static ICPPASTTemplateId getTemplateParameter(IASTNode node, IASTName name) { ICPPASTTemplateId templateID = factory.newTemplateId(name.copy(CopyStyle.withLocations)); for (IASTNode child : node.getChildren()) { if (child instanceof ICPPASTSimpleTypeTemplateParameter) { ICPPASTSimpleTypeTemplateParameter tempChild = (ICPPASTSimpleTypeTemplateParameter) child; IASTName argumentName = tempChild.getName().copy(CopyStyle.withLocations); ICPPASTNamedTypeSpecifier namedTypeSpecifier = factory.newNamedTypeSpecifier(argumentName); ICPPASTTypeId id = factory.newTypeId(namedTypeSpecifier, null); templateID.addTemplateArgument(id); } } return templateID; } public static String getFilenameWithoutExtension(String filename) { int indexP = filename.lastIndexOf('.'); int indexS = filename.lastIndexOf('/'); indexS++; return filename.substring(indexS, indexP); } /** * Will extract the innermost ICPPASTFunctionDefinition out of a template declaration. * * template<typename T> // <-- input this node * template<typename U> * void function(T t, U u) { ... } // <-- will find this node here * * @param declaration the template declaration that should be searched for the function definition. * @return null if a declaration is found instead of a definition. */ public static ICPPASTFunctionDefinition getFunctionDefinition(IASTNode declaration) { IASTNode node = declaration; while (node != null) { if (node instanceof ICPPASTTemplateDeclaration) { ICPPASTTemplateDeclaration templdec = (ICPPASTTemplateDeclaration) node; node = templdec.getDeclaration(); continue; } if (node instanceof ICPPASTFunctionDefinition) { return (ICPPASTFunctionDefinition) node; } else { return null; } } return null; } /** * Gets comments inside the body of a function. * @return The body as a string and all the catch handlers */ public static String getBody(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast, ModificationCollector modifications) { return getBodyOnly(oldDefinition, ast, modifications) + getCatchHandlers(oldDefinition, ast, modifications); } private static String getBodyOnly(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast, ModificationCollector modifications) { String leadingComments = getCommentsAsString(getLeadingCommentsFromNode(oldDefinition.getBody(), ast, modifications)); String trailingComments = getCommentsAsString(getTrailingComments(oldDefinition.getBody(), ast, modifications)); return leadingComments + oldDefinition.getBody().getRawSignature() + trailingComments; } private static String getCatchHandlers(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast, ModificationCollector modifications) { if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) { ICPPASTCatchHandler[] oldCatches = ((ICPPASTFunctionWithTryBlock) oldDefinition).getCatchHandlers(); String allCatchHandlers = ""; //$NON-NLS-1$ for (int i = 0; i < oldCatches.length; i++) { String lead = getCommentsAsString(getLeadingCommentsFromNode(oldCatches[i], ast, modifications)); String trail = getCommentsAsString(getTrailingComments(oldCatches[i], ast, modifications)); allCatchHandlers += lead + oldCatches[i].getRawSignature() + trail; } return allCatchHandlers; } return ""; //$NON-NLS-1$ } private static List<IASTComment> getLeadingCommentsFromNode(IASTNode existingNode, IASTTranslationUnit ast, ModificationCollector modifications) { ASTRewrite rw = modifications.rewriterForTranslationUnit(ast); return rw.getComments(existingNode, CommentPosition.leading); } private static List<IASTComment> getTrailingComments(IASTNode existingNode, IASTTranslationUnit ast, ModificationCollector modifications) { ASTRewrite rw = modifications.rewriterForTranslationUnit(ast); return rw.getComments(existingNode, CommentPosition.trailing); } public static IASTNode getParentTemplateDeclaration(IASTNode def) { if (def == null) return null; IASTNode lastSeen = def; IASTNode node = def.getParent(); while (node != null) { if (node instanceof ICPPASTTemplateDeclaration || node instanceof IASTSimpleDeclaration) { lastSeen = node; node = node.getParent(); continue; } return lastSeen; } return lastSeen; } private static String getCommentsAsString(List<IASTComment> commentList) { String comments = EMPTY_STRING; for (IASTComment c : commentList) { comments += c.getRawSignature() + System.getProperty("line.separator"); //$NON-NLS-1$ } return comments; } /** * Returns all namespace definitions surrounding <code>node</code>, ordered from outer to inner. * @param node to collect the namespaces for. * @return List of the surrounding namespaces. */ public static List<ICPPASTNamespaceDefinition> findSurroundingNamespaces(IASTNode node) { ArrayList<ICPPASTNamespaceDefinition> namespaces = new ArrayList<>(); ICPPASTNamespaceDefinition currentNamespace = ASTQueries.findAncestorWithType(node, ICPPASTNamespaceDefinition.class); while (currentNamespace != null) { namespaces.add(0, currentNamespace); currentNamespace = ASTQueries.findAncestorWithType(currentNamespace.getParent(), ICPPASTNamespaceDefinition.class); } return namespaces; } }