upload tizen1.0 source
[sdk/ide/product.git] / org.eclipse.cdt.ui / src / org / eclipse / cdt / internal / ui / refactoring / togglefunction / ToggleNodeHelper.java
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  
8  * 
9  * Contributors: 
10  *              Martin Schwab & Thomas Kallenberg - initial API and implementation 
11  ******************************************************************************/
12 package org.eclipse.cdt.internal.ui.refactoring.togglefunction;
13
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.ListIterator;
17 import java.util.Stack;
18
19 import org.eclipse.core.resources.IFile;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.Path;
22
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;
58
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;
68
69 import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
70 import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
71
72 public class ToggleNodeHelper extends NodeHelper {
73         
74         private static final String EMPTY_STRING = ""; //$NON-NLS-1$
75
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);
81                         }
82                 }
83         }
84
85         private static ArrayList<ICPPASTConstructorChainInitializer> 
86                         getInitializerList(IASTFunctionDefinition definition) {
87                 ArrayList<ICPPASTConstructorChainInitializer> initalizers = 
88                         new ArrayList<ICPPASTConstructorChainInitializer>();
89         
90                 for (IASTNode node : definition.getChildren()) {
91                         if (node instanceof ICPPASTConstructorChainInitializer) {
92                                 initalizers.add(((ICPPASTConstructorChainInitializer) node).copy(CopyStyle.withLocations));
93                         }
94                 }
95                 return initalizers;
96         }
97
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;
105         }
106
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());
115                 } else {
116                         newFunc = new CPPASTFunctionDefinition(newDeclSpec, newFuncDecl, 
117                                         new CPPASTCompoundStatement());
118                 }
119                 copyInitializerList(newFunc, oldDefinition);
120                 return newFunc;
121         }
122         
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));
131                         }
132                 }
133                 return newFuncDecl;
134         }
135
136         private static void copyInitializerList(ICPPASTFunctionDefinition newFunc, 
137                         IASTFunctionDefinition oldFunc) {
138                 for (ICPPASTConstructorChainInitializer initializer : getInitializerList(oldFunc)) {
139                         initializer.setParent(newFunc);
140                         newFunc.addMemberInitializer(initializer);
141                 }
142         }
143
144         static IASTFunctionDefinition getQualifiedNameDefinition(IASTFunctionDefinition oldDefinition, 
145                         IASTTranslationUnit definitionUnit, IASTNode nameSpace) {
146                 
147                 ICPPASTDeclSpecifier newDeclSpec = 
148  (ICPPASTDeclSpecifier) oldDefinition.getDeclSpecifier().copy(
149                                 CopyStyle.withLocations);
150                 
151                 newDeclSpec.setVirtual(false);
152                 newDeclSpec.setInline(true);
153                 newDeclSpec.setStorageClass(IASTDeclSpecifier.sc_unspecified);
154                 
155                 IASTFunctionDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations);
156                 newDeclarator.setName(getQualifiedName(oldDefinition.getDeclarator(), nameSpace));
157                 removeParameterInitializations(newDeclarator);
158                 
159                 ICPPASTFunctionDefinition newFunction = 
160                         createFunctionSignatureWithEmptyBody(newDeclSpec, newDeclarator, oldDefinition);
161         
162                 return newFunction;
163         }
164
165         public static ICPPASTTemplateDeclaration getTemplateDeclaration(
166                         IASTFunctionDefinition oldFunction, IASTFunctionDefinition newFunction) {
167                 ArrayList<ICPPASTTemplateDeclaration> templateDeclarations = getAllTemplateDeclaration(oldFunction);
168                 return addTemplateDeclarationsInOrder(templateDeclarations, newFunction);
169         }
170
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);
183                                 child = parent;
184                         }
185                 }
186                 return child;
187         }
188
189         private static ArrayList<ICPPASTTemplateDeclaration> getAllTemplateDeclaration(
190                         IASTNode node) {
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));
196                         }
197                 }
198                 return templdecs;
199         }
200
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);
211                 }
212                 declSpec.setStorageClass(getStorageClass(dec));
213                 
214                 return createFunctionSignatureWithEmptyBody(declSpec, declarator, def);
215         }
216
217         static boolean isVirtual(IASTFunctionDeclarator fdec) {
218                 if (fdec.getParent() instanceof IASTSimpleDeclaration) {
219                         IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
220                         return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).isVirtual();
221                 }
222                 return false;
223         }
224         
225         static int getStorageClass(IASTFunctionDeclarator fdec) {
226                 if (fdec.getParent() instanceof IASTSimpleDeclaration) {
227                         IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
228                         return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).getStorageClass();
229                 }
230                 return -1;
231         }
232
233         static IASTNode getParentRemovePoint(IASTFunctionDefinition definition) {
234                 IASTNode toremove = definition;
235                 while (toremove.getParent() != null && 
236                                 toremove.getParent() instanceof ICPPASTTemplateDeclaration)
237                         toremove = toremove.getParent();
238                 return toremove;
239         }
240
241         /**
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
244          * @return
245          */
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));
250                 return qName;
251         }
252
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());
259                         }
260                         else if (nnode instanceof ICPPASTNamespaceDefinition) {
261                                 qName.addName(((ICPPASTNamespaceDefinition) nnode).getName());
262                         }
263                         else if (nnode instanceof ICPPASTTemplateId) {
264                                 qName.addName((ICPPASTTemplateId) nnode);
265                         }
266                 }
267                 return qName;
268         }
269
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();
279                         }
280                         else if (node instanceof ICPPASTNamespaceDefinition) {
281                                 nodes.push(((ICPPASTNamespaceDefinition) node).copy(CopyStyle.withLocations));
282                                 lastName = ((ICPPASTNamespaceDefinition) node).getName();
283                         }
284                         else if (shouldAddTemplateBrackets(node)) {
285                                 if (!nodes.isEmpty())
286                                         nodes.pop();
287                                 ICPPASTTemplateId templateID = ToggleNodeHelper.getTemplateParameter(node, lastName);
288                                 nodes.add(templateID);
289                         } 
290                 }
291                 return nodes;
292         }
293
294         private static boolean shouldAddTemplateBrackets(IASTNode node) {
295                 return node instanceof ICPPASTTemplateDeclaration
296                                 && !(((ICPPASTTemplateDeclaration) node).getDeclaration() 
297                                                 instanceof CPPASTFunctionDefinition);
298         }
299
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;
306         
307                                 CPPASTNamedTypeSpecifier namedTypeSpecifier = new CPPASTNamedTypeSpecifier();
308                                 namedTypeSpecifier.setName(tempcild.getName().copy(CopyStyle.withLocations));
309                                 
310                                 CPPASTTypeId id = new CPPASTTypeId();
311                                 id.setDeclSpecifier(namedTypeSpecifier);
312                                 templateID.addTemplateArgument(id);
313                         }
314                 }
315                 return templateID;
316         }
317
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);
322                 try {
323                         projectIndex.acquireReadLock();
324
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);
334                                         }
335                                 }
336                         } else {
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$
341                                                 }
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);
346                                 }
347                                         }
348                                 }
349                         }
350                 }catch (InterruptedException e) {
351                         e.printStackTrace();
352                 } finally {
353                         projectIndex.releaseReadLock();
354                 }
355                 return null;
356         }
357
358         public static String getFilenameWithoutExtension(String filename) {
359                 int indexP = filename.lastIndexOf('.');
360                 int indexS = filename.lastIndexOf('/');
361                 indexS++;
362                 return filename.substring(indexS, indexP);
363         }
364
365         /**
366          * Will extract the innermost ICPPASTFunctionDefinition out of a template declaration.
367          * 
368          * template<typename T>                         // <-- input this node
369          * template<typename U>
370          * void function(T t, U u) { ... }  // <-- will find this node here 
371          * 
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.
374          */
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();
381                                 continue;
382                         }
383                         if (node instanceof ICPPASTFunctionDefinition) {
384                                 return (ICPPASTFunctionDefinition) node;
385                         } else {
386                                 return null;
387                         }
388                 }
389                 return null;
390         }
391         
392         /**
393          * Gets comments inside the body of a function.
394          * @return The body as a string and all the catch handlers
395          */
396         public static String getBody(IASTFunctionDefinition oldDefinition, IASTTranslationUnit oldUnit,
397                         ModificationCollector modifications) {
398                 return getBodyOnly(oldDefinition, oldUnit, modifications)
399                                 + getCatchHandlers(oldDefinition, oldUnit, modifications);
400         }
401
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,
407                                 modifications));
408                 return leadingComments + oldDefinition.getBody().getRawSignature() + trailingComments;
409         }
410
411         private static String getCatchHandlers(IASTFunctionDefinition oldDefinition, IASTTranslationUnit oldUnit,
412                         ModificationCollector modifications) {
413                 if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) {
414                         ICPPASTCatchHandler[] oldCatches = ((ICPPASTFunctionWithTryBlock) oldDefinition)
415                                         .getCatchHandlers();
416                         String allCatchHandlers = ""; //$NON-NLS-1$
417                         for (int i = 0; i < oldCatches.length; i++) {
418                                 String lead = getCommentsAsString(getLeadingCommentsFromNode(oldCatches[i], oldUnit,
419                                                 modifications));
420                                 String trail = getCommentsAsString(getTrailingComments(oldCatches[i], oldUnit, modifications));
421                                 allCatchHandlers += lead + oldCatches[i].getRawSignature() + trail;
422                         }
423                         return allCatchHandlers;
424                 }
425                 return ""; //$NON-NLS-1$
426         }
427
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);
432         }
433
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);
438         }
439
440         
441         public static IASTNode getParentTemplateDeclaration(
442                         IASTNode def) {
443                 if (def == null)
444                         return null;
445                 IASTNode lastSeen = def;
446                 IASTNode node = def.getParent();
447                 while (node != null) {
448                         if (node instanceof ICPPASTTemplateDeclaration || 
449                                         node instanceof IASTSimpleDeclaration) {
450                                 lastSeen = node;
451                                 node = node.getParent();
452                                 continue;
453                         }
454                         return lastSeen;
455                 }
456                 return lastSeen;
457         }
458         
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$
463                 }
464                 return comments;
465         }
466
467         @SuppressWarnings("unchecked")
468         public static <T> T getAncestorOfType(IASTNode node, Class<?> T) {
469                 while(node != null) {
470                         if (T.isInstance(node)) {
471                                 return (T) node;
472                         }
473                         node = node.getParent();
474                 }
475                 return null;
476         }
477 }