1 /*******************************************************************************
2 * Copyright (c) 2011 Institute for Software, HSR Hochschule fuer Technik
3 * Rapperswil, University of applied sciences and others.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Martin Schwab & Thomas Kallenberg - initial API and implementation
11 ******************************************************************************/
12 package org.eclipse.cdt.internal.ui.refactoring.togglefunction;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.ListIterator;
17 import java.util.Stack;
19 import org.eclipse.core.resources.IFile;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.Path;
23 import org.eclipse.cdt.core.CCorePlugin;
24 import org.eclipse.cdt.core.dom.ast.IASTComment;
25 import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
26 import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
27 import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
28 import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
29 import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
30 import org.eclipse.cdt.core.dom.ast.IASTName;
31 import org.eclipse.cdt.core.dom.ast.IASTNode;
32 import org.eclipse.cdt.core.dom.ast.IASTNode.CopyStyle;
33 import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
34 import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
35 import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
36 import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
37 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
38 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
39 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
40 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
41 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
42 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
43 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
44 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
45 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
46 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
47 import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
48 import org.eclipse.cdt.core.dom.rewrite.ASTRewrite.CommentPosition;
49 import org.eclipse.cdt.core.index.IIndex;
50 import org.eclipse.cdt.core.index.IIndexFile;
51 import org.eclipse.cdt.core.index.IIndexInclude;
52 import org.eclipse.cdt.core.index.IndexLocationFactory;
53 import org.eclipse.cdt.core.model.CoreModel;
54 import org.eclipse.cdt.core.model.CoreModelUtil;
55 import org.eclipse.cdt.core.model.ICElement;
56 import org.eclipse.cdt.core.model.ICProject;
57 import org.eclipse.cdt.core.model.ITranslationUnit;
59 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompoundStatement;
60 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDefinition;
61 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionWithTryBlock;
62 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNamedTypeSpecifier;
63 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName;
64 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSimpleDeclaration;
65 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateId;
66 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTypeId;
67 import org.eclipse.cdt.internal.core.model.TranslationUnit;
69 import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
70 import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
72 public class ToggleNodeHelper extends NodeHelper {
74 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
76 private static void removeParameterInitializations(IASTFunctionDeclarator funcDecl) {
77 for (IASTNode child : funcDecl.getChildren()) {
78 if (child instanceof IASTParameterDeclaration) {
79 IASTParameterDeclaration parameter = (IASTParameterDeclaration) child;
80 parameter.getDeclarator().setInitializer(null);
85 private static ArrayList<ICPPASTConstructorChainInitializer>
86 getInitializerList(IASTFunctionDefinition definition) {
87 ArrayList<ICPPASTConstructorChainInitializer> initalizers =
88 new ArrayList<ICPPASTConstructorChainInitializer>();
90 for (IASTNode node : definition.getChildren()) {
91 if (node instanceof ICPPASTConstructorChainInitializer) {
92 initalizers.add(((ICPPASTConstructorChainInitializer) node).copy(CopyStyle.withLocations));
98 static IASTSimpleDeclaration createDeclarationFromDefinition(
99 IASTFunctionDefinition oldDefinition) {
100 IASTDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations);
101 IASTDeclSpecifier newDeclSpec = oldDefinition.getDeclSpecifier().copy(CopyStyle.withLocations);
102 IASTSimpleDeclaration newDeclaration = new CPPASTSimpleDeclaration(newDeclSpec);
103 newDeclaration.addDeclarator(newDeclarator);
104 return newDeclaration;
107 static ICPPASTFunctionDefinition createFunctionSignatureWithEmptyBody(
108 IASTDeclSpecifier newDeclSpec, IASTFunctionDeclarator newFuncDecl,
109 IASTFunctionDefinition oldDefinition) {
110 ICPPASTFunctionDefinition newFunc = null;
111 newFuncDecl = adjustParamNames(newFuncDecl, oldDefinition);
112 if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) {
113 newFunc = new CPPASTFunctionWithTryBlock(newDeclSpec, newFuncDecl,
114 new CPPASTCompoundStatement());
116 newFunc = new CPPASTFunctionDefinition(newDeclSpec, newFuncDecl,
117 new CPPASTCompoundStatement());
119 copyInitializerList(newFunc, oldDefinition);
123 private static IASTFunctionDeclarator adjustParamNames(IASTFunctionDeclarator newFuncDecl,
124 IASTFunctionDefinition oldDefinition) {
125 if (oldDefinition.getDeclarator() instanceof IASTStandardFunctionDeclarator) {
126 IASTStandardFunctionDeclarator oldStdDec = (IASTStandardFunctionDeclarator) oldDefinition.getDeclarator();
127 IASTParameterDeclaration[] definitionParams = oldStdDec.getParameters();
128 IASTParameterDeclaration[] declarationParams = ((IASTStandardFunctionDeclarator)newFuncDecl).getParameters();
129 for(int i = 0; i < declarationParams.length; ++i) {
130 declarationParams[i].getDeclarator().setName(definitionParams[i].getDeclarator().getName().copy(CopyStyle.withLocations));
136 private static void copyInitializerList(ICPPASTFunctionDefinition newFunc,
137 IASTFunctionDefinition oldFunc) {
138 for (ICPPASTConstructorChainInitializer initializer : getInitializerList(oldFunc)) {
139 initializer.setParent(newFunc);
140 newFunc.addMemberInitializer(initializer);
144 static IASTFunctionDefinition getQualifiedNameDefinition(IASTFunctionDefinition oldDefinition,
145 IASTTranslationUnit definitionUnit, IASTNode nameSpace) {
147 ICPPASTDeclSpecifier newDeclSpec =
148 (ICPPASTDeclSpecifier) oldDefinition.getDeclSpecifier().copy(
149 CopyStyle.withLocations);
151 newDeclSpec.setVirtual(false);
152 newDeclSpec.setInline(true);
153 newDeclSpec.setStorageClass(IASTDeclSpecifier.sc_unspecified);
155 IASTFunctionDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations);
156 newDeclarator.setName(getQualifiedName(oldDefinition.getDeclarator(), nameSpace));
157 removeParameterInitializations(newDeclarator);
159 ICPPASTFunctionDefinition newFunction =
160 createFunctionSignatureWithEmptyBody(newDeclSpec, newDeclarator, oldDefinition);
165 public static ICPPASTTemplateDeclaration getTemplateDeclaration(
166 IASTFunctionDefinition oldFunction, IASTFunctionDefinition newFunction) {
167 ArrayList<ICPPASTTemplateDeclaration> templateDeclarations = getAllTemplateDeclaration(oldFunction);
168 return addTemplateDeclarationsInOrder(templateDeclarations, newFunction);
171 private static ICPPASTTemplateDeclaration addTemplateDeclarationsInOrder(
172 ArrayList<ICPPASTTemplateDeclaration> templdecs, IASTFunctionDefinition newfunc) {
173 ListIterator<ICPPASTTemplateDeclaration> iter1 = templdecs.listIterator();
174 ICPPASTTemplateDeclaration child = null;
175 while(iter1.hasNext()) {
176 child = iter1.next();
177 child.setDeclaration(newfunc);
178 ListIterator<ICPPASTTemplateDeclaration> iter2 = iter1;
179 if (iter2.hasNext()) {
180 ICPPASTTemplateDeclaration parent = iter2.next();
181 child.setParent(parent);
182 parent.setDeclaration(child);
189 private static ArrayList<ICPPASTTemplateDeclaration> getAllTemplateDeclaration(
191 ArrayList<ICPPASTTemplateDeclaration> templdecs = new ArrayList<ICPPASTTemplateDeclaration>();
192 while (node.getParent() != null) {
193 node = node.getParent();
194 if (node instanceof ICPPASTTemplateDeclaration) {
195 templdecs.add((ICPPASTTemplateDeclaration) node.copy(CopyStyle.withLocations));
201 static IASTFunctionDefinition createInClassDefinition(
202 IASTFunctionDeclarator dec,
203 IASTFunctionDefinition def,
204 IASTTranslationUnit insertionunit) {
205 IASTFunctionDeclarator declarator = dec.copy(CopyStyle.withLocations);
206 ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) def.getDeclSpecifier().copy(
207 CopyStyle.withLocations);
208 declSpec.setInline(false);
209 if (ToggleNodeHelper.isVirtual(dec)) {
210 declSpec.setVirtual(true);
212 declSpec.setStorageClass(getStorageClass(dec));
214 return createFunctionSignatureWithEmptyBody(declSpec, declarator, def);
217 static boolean isVirtual(IASTFunctionDeclarator fdec) {
218 if (fdec.getParent() instanceof IASTSimpleDeclaration) {
219 IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
220 return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).isVirtual();
225 static int getStorageClass(IASTFunctionDeclarator fdec) {
226 if (fdec.getParent() instanceof IASTSimpleDeclaration) {
227 IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
228 return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).getStorageClass();
233 static IASTNode getParentRemovePoint(IASTFunctionDefinition definition) {
234 IASTNode toremove = definition;
235 while (toremove.getParent() != null &&
236 toremove.getParent() instanceof ICPPASTTemplateDeclaration)
237 toremove = toremove.getParent();
242 * @param declarator the declarator from which the full qualified namespace should be fetched
243 * @param limiter set a limiter in the class hierarchy where the lookup will stop
246 static ICPPASTQualifiedName getQualifiedName(IASTFunctionDeclarator declarator, IASTNode limiter) {
247 Stack<IASTNode> nodes = getQualifiedNames(declarator, limiter, declarator);
248 CPPASTQualifiedName qName = reAssembleQualifiedName(nodes);
249 qName.addName(declarator.getName().copy(CopyStyle.withLocations));
253 private static CPPASTQualifiedName reAssembleQualifiedName(Stack<IASTNode> nodes) {
254 CPPASTQualifiedName qName = new CPPASTQualifiedName();
255 while(!nodes.isEmpty()) {
256 IASTNode nnode = nodes.pop();
257 if (nnode instanceof IASTCompositeTypeSpecifier) {
258 qName.addName(((IASTCompositeTypeSpecifier) nnode).getName());
260 else if (nnode instanceof ICPPASTNamespaceDefinition) {
261 qName.addName(((ICPPASTNamespaceDefinition) nnode).getName());
263 else if (nnode instanceof ICPPASTTemplateId) {
264 qName.addName((ICPPASTTemplateId) nnode);
270 private static Stack<IASTNode> getQualifiedNames(
271 IASTFunctionDeclarator declarator, IASTNode limiter, IASTNode node) {
272 IASTName lastName = declarator.getName();
273 Stack<IASTNode> nodes = new Stack<IASTNode>();
274 while(node.getParent() != null && node.getParent() != limiter) {
275 node = node.getParent();
276 if (node instanceof IASTCompositeTypeSpecifier) {
277 nodes.push(((IASTCompositeTypeSpecifier) node).copy(CopyStyle.withLocations));
278 lastName = ((IASTCompositeTypeSpecifier) node).getName();
280 else if (node instanceof ICPPASTNamespaceDefinition) {
281 nodes.push(((ICPPASTNamespaceDefinition) node).copy(CopyStyle.withLocations));
282 lastName = ((ICPPASTNamespaceDefinition) node).getName();
284 else if (shouldAddTemplateBrackets(node)) {
285 if (!nodes.isEmpty())
287 ICPPASTTemplateId templateID = ToggleNodeHelper.getTemplateParameter(node, lastName);
288 nodes.add(templateID);
294 private static boolean shouldAddTemplateBrackets(IASTNode node) {
295 return node instanceof ICPPASTTemplateDeclaration
296 && !(((ICPPASTTemplateDeclaration) node).getDeclaration()
297 instanceof CPPASTFunctionDefinition);
300 private static ICPPASTTemplateId getTemplateParameter(IASTNode node, IASTName name) {
301 ICPPASTTemplateId templateID = new CPPASTTemplateId();
302 templateID.setTemplateName(name.copy(CopyStyle.withLocations));
303 for(IASTNode child : node.getChildren()) {
304 if (child instanceof ICPPASTSimpleTypeTemplateParameter) {
305 ICPPASTSimpleTypeTemplateParameter tempcild = (ICPPASTSimpleTypeTemplateParameter) child;
307 CPPASTNamedTypeSpecifier namedTypeSpecifier = new CPPASTNamedTypeSpecifier();
308 namedTypeSpecifier.setName(tempcild.getName().copy(CopyStyle.withLocations));
310 CPPASTTypeId id = new CPPASTTypeId();
311 id.setDeclSpecifier(namedTypeSpecifier);
312 templateID.addTemplateArgument(id);
318 static IASTTranslationUnit getSiblingFile(IFile file, IASTTranslationUnit asttu) throws CoreException {
319 ICProject cProject = CoreModel.getDefault().create(file).getCProject();
320 ICProject[] projects = CoreModel.getDefault().getCModel().getCProjects();
321 IIndex projectIndex = CCorePlugin.getIndexManager().getIndex(projects);
323 projectIndex.acquireReadLock();
325 IIndexFile thisFile = projectIndex.getFile(asttu.getLinkage().getLinkageID(),
326 IndexLocationFactory.getWorkspaceIFL(file));
327 String fileName = ToggleNodeHelper.getFilenameWithoutExtension(
328 file.getFullPath().toString());
329 if (asttu.isHeaderUnit()) {
330 for (IIndexInclude include : projectIndex.findIncludedBy(thisFile)) {
331 if (ToggleNodeHelper.getFilenameWithoutExtension(include.getIncludedBy().getLocation().getFullPath()).equals(fileName)) {
332 ITranslationUnit tu = CoreModelUtil.findTranslationUnitForLocation(include.getIncludedBy().getLocation().getURI(), cProject);
333 return tu.getAST(projectIndex, ITranslationUnit.AST_SKIP_ALL_HEADERS);
337 for (IIndexInclude include : projectIndex.findIncludes(thisFile)) {
338 if (ToggleNodeHelper.getFilenameWithoutExtension(include.getFullName()).equals(fileName)) {
339 if (include.getIncludesLocation() == null){
340 throw new NotSupportedException("The include file does not exist"); //$NON-NLS-1$
342 String loc = include.getIncludesLocation().getFullPath();
343 ICElement tufile = CoreModel.getDefault().create(new Path(loc));
344 if (tufile instanceof TranslationUnit) {
345 return ((TranslationUnit) tufile).getAST(null, ITranslationUnit.AST_SKIP_ALL_HEADERS);
350 }catch (InterruptedException e) {
353 projectIndex.releaseReadLock();
358 public static String getFilenameWithoutExtension(String filename) {
359 int indexP = filename.lastIndexOf('.');
360 int indexS = filename.lastIndexOf('/');
362 return filename.substring(indexS, indexP);
366 * Will extract the innermost ICPPASTFunctionDefinition out of a template declaration.
368 * template<typename T> // <-- input this node
369 * template<typename U>
370 * void function(T t, U u) { ... } // <-- will find this node here
372 * @param declaration the template declaration that should be searched for the function definition.
373 * @return null if a declaration is found instead of a definition.
375 public static ICPPASTFunctionDefinition getFunctionDefinition(IASTNode declaration) {
376 IASTNode node = declaration;
377 while (node != null) {
378 if (node instanceof ICPPASTTemplateDeclaration) {
379 ICPPASTTemplateDeclaration templdec = (ICPPASTTemplateDeclaration) node;
380 node = templdec.getDeclaration();
383 if (node instanceof ICPPASTFunctionDefinition) {
384 return (ICPPASTFunctionDefinition) node;
393 * Gets comments inside the body of a function.
394 * @return The body as a string and all the catch handlers
396 public static String getBody(IASTFunctionDefinition oldDefinition, IASTTranslationUnit oldUnit,
397 ModificationCollector modifications) {
398 return getBodyOnly(oldDefinition, oldUnit, modifications)
399 + getCatchHandlers(oldDefinition, oldUnit, modifications);
402 private static String getBodyOnly(IASTFunctionDefinition oldDefinition, IASTTranslationUnit oldUnit,
403 ModificationCollector modifications) {
404 String leadingComments = getCommentsAsString(getLeadingCommentsFromNode(oldDefinition.getBody(),
405 oldUnit, modifications));
406 String trailingComments = getCommentsAsString(getTrailingComments(oldDefinition.getBody(), oldUnit,
408 return leadingComments + oldDefinition.getBody().getRawSignature() + trailingComments;
411 private static String getCatchHandlers(IASTFunctionDefinition oldDefinition, IASTTranslationUnit oldUnit,
412 ModificationCollector modifications) {
413 if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) {
414 ICPPASTCatchHandler[] oldCatches = ((ICPPASTFunctionWithTryBlock) oldDefinition)
416 String allCatchHandlers = ""; //$NON-NLS-1$
417 for (int i = 0; i < oldCatches.length; i++) {
418 String lead = getCommentsAsString(getLeadingCommentsFromNode(oldCatches[i], oldUnit,
420 String trail = getCommentsAsString(getTrailingComments(oldCatches[i], oldUnit, modifications));
421 allCatchHandlers += lead + oldCatches[i].getRawSignature() + trail;
423 return allCatchHandlers;
425 return ""; //$NON-NLS-1$
428 private static List<IASTComment> getLeadingCommentsFromNode(IASTNode existingNode,
429 IASTTranslationUnit oldUnit, ModificationCollector modifications) {
430 ASTRewrite rw = modifications.rewriterForTranslationUnit(oldUnit);
431 return rw.getComments(existingNode, CommentPosition.leading);
434 private static List<IASTComment> getTrailingComments(IASTNode existingNode,
435 IASTTranslationUnit oldUnit, ModificationCollector modifications) {
436 ASTRewrite rw = modifications.rewriterForTranslationUnit(oldUnit);
437 return rw.getComments(existingNode, CommentPosition.trailing);
441 public static IASTNode getParentTemplateDeclaration(
445 IASTNode lastSeen = def;
446 IASTNode node = def.getParent();
447 while (node != null) {
448 if (node instanceof ICPPASTTemplateDeclaration ||
449 node instanceof IASTSimpleDeclaration) {
451 node = node.getParent();
459 private static String getCommentsAsString(List<IASTComment> commentList) {
460 String comments = EMPTY_STRING;
461 for (IASTComment c : commentList) {
462 comments += c.getRawSignature() + System.getProperty("line.separator"); //$NON-NLS-1$
467 @SuppressWarnings("unchecked")
468 public static <T> T getAncestorOfType(IASTNode node, Class<?> T) {
469 while(node != null) {
470 if (T.isInstance(node)) {
473 node = node.getParent();