]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / dom / ASTNodes.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
11  *       bug "inline method - doesn't handle implicit cast" (see
12  *       https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
13  *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
14  *       bug Encapsulate field can fail when two variables in one variable declaration (see
15  *       https://bugs.eclipse.org/bugs/show_bug.cgi?id=51540).
16  *******************************************************************************/
17 package org.eclipse.jdt.internal.corext.dom;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.eclipse.core.runtime.Assert;
24
25 import org.eclipse.text.edits.TextEdit;
26
27 import org.eclipse.jface.text.BadLocationException;
28 import org.eclipse.jface.text.Document;
29
30 import org.eclipse.jdt.core.Flags;
31 import org.eclipse.jdt.core.IBuffer;
32 import org.eclipse.jdt.core.IField;
33 import org.eclipse.jdt.core.IJavaElement;
34 import org.eclipse.jdt.core.IJavaProject;
35 import org.eclipse.jdt.core.IMember;
36 import org.eclipse.jdt.core.ISourceReference;
37 import org.eclipse.jdt.core.IType;
38 import org.eclipse.jdt.core.ITypeRoot;
39 import org.eclipse.jdt.core.JavaModelException;
40 import org.eclipse.jdt.core.compiler.IProblem;
41 import org.eclipse.jdt.core.dom.AST;
42 import org.eclipse.jdt.core.dom.ASTNode;
43 import org.eclipse.jdt.core.dom.ASTVisitor;
44 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
45 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
46 import org.eclipse.jdt.core.dom.ArrayType;
47 import org.eclipse.jdt.core.dom.Assignment;
48 import org.eclipse.jdt.core.dom.BodyDeclaration;
49 import org.eclipse.jdt.core.dom.CharacterLiteral;
50 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
51 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
52 import org.eclipse.jdt.core.dom.CompilationUnit;
53 import org.eclipse.jdt.core.dom.DoStatement;
54 import org.eclipse.jdt.core.dom.EnhancedForStatement;
55 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
56 import org.eclipse.jdt.core.dom.Expression;
57 import org.eclipse.jdt.core.dom.FieldAccess;
58 import org.eclipse.jdt.core.dom.FieldDeclaration;
59 import org.eclipse.jdt.core.dom.ForStatement;
60 import org.eclipse.jdt.core.dom.IBinding;
61 import org.eclipse.jdt.core.dom.IExtendedModifier;
62 import org.eclipse.jdt.core.dom.IMethodBinding;
63 import org.eclipse.jdt.core.dom.ITypeBinding;
64 import org.eclipse.jdt.core.dom.IVariableBinding;
65 import org.eclipse.jdt.core.dom.IfStatement;
66 import org.eclipse.jdt.core.dom.InfixExpression;
67 import org.eclipse.jdt.core.dom.Message;
68 import org.eclipse.jdt.core.dom.MethodDeclaration;
69 import org.eclipse.jdt.core.dom.MethodInvocation;
70 import org.eclipse.jdt.core.dom.Modifier;
71 import org.eclipse.jdt.core.dom.Name;
72 import org.eclipse.jdt.core.dom.NodeFinder;
73 import org.eclipse.jdt.core.dom.ParameterizedType;
74 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
75 import org.eclipse.jdt.core.dom.PrimitiveType;
76 import org.eclipse.jdt.core.dom.QualifiedName;
77 import org.eclipse.jdt.core.dom.QualifiedType;
78 import org.eclipse.jdt.core.dom.SimpleName;
79 import org.eclipse.jdt.core.dom.SimpleType;
80 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
81 import org.eclipse.jdt.core.dom.StringLiteral;
82 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
83 import org.eclipse.jdt.core.dom.Type;
84 import org.eclipse.jdt.core.dom.VariableDeclaration;
85 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
86 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
87 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
88 import org.eclipse.jdt.core.dom.WhileStatement;
89
90 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
91 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
92 import org.eclipse.jdt.internal.corext.util.Strings;
93
94 import org.eclipse.jdt.internal.ui.JavaPlugin;
95 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
96 import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
97 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
98
99 public class ASTNodes {
100
101         public static final int NODE_ONLY=                              0;
102         public static final int INCLUDE_FIRST_PARENT=   1;
103         public static final int INCLUDE_ALL_PARENTS=    2;
104
105         public static final int WARNING=                                1 << 0;
106         public static final int ERROR=                                  1 << 1;
107         public static final int PROBLEMS=                               WARNING | ERROR;
108
109         private static final Message[] EMPTY_MESSAGES= new Message[0];
110         private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0];
111
112         private static final int CLEAR_VISIBILITY= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
113
114
115         private ASTNodes() {
116                 // no instance;
117         }
118
119         public static String asString(ASTNode node) {
120                 ASTFlattener flattener= new ASTFlattener();
121                 node.accept(flattener);
122                 return flattener.getResult();
123         }
124
125         public static String asFormattedString(ASTNode node, int indent, String lineDelim, Map<String, String> options) {
126                 String unformatted= asString(node);
127                 TextEdit edit= CodeFormatterUtil.format2(node, unformatted, indent, lineDelim, options);
128                 if (edit != null) {
129                         Document document= new Document(unformatted);
130                         try {
131                                 edit.apply(document, TextEdit.NONE);
132                         } catch (BadLocationException e) {
133                                 JavaPlugin.log(e);
134                         }
135                         return document.get();
136                 }
137                 return unformatted; // unknown node
138         }
139
140
141         /**
142          * Returns the source of the given node from the location where it was parsed.
143          * @param node the node to get the source from
144          * @param extendedRange if set, the extended ranges of the nodes should ne used
145          * @param removeIndent if set, the indentation is removed.
146          * @return return the source for the given node or null if accessing the source failed.
147          */
148         public static String getNodeSource(ASTNode node, boolean extendedRange, boolean removeIndent) {
149                 ASTNode root= node.getRoot();
150                 if (root instanceof CompilationUnit) {
151                         CompilationUnit astRoot= (CompilationUnit) root;
152                         ITypeRoot typeRoot= astRoot.getTypeRoot();
153                         try {
154                                 if (typeRoot != null && typeRoot.getBuffer() != null) {
155                                         IBuffer buffer= typeRoot.getBuffer();
156                                         int offset= extendedRange ? astRoot.getExtendedStartPosition(node) : node.getStartPosition();
157                                         int length= extendedRange ? astRoot.getExtendedLength(node) : node.getLength();
158                                         String str= buffer.getText(offset, length);
159                                         if (removeIndent) {
160                                                 IJavaProject project= typeRoot.getJavaProject();
161                                                 int indent= StubUtility.getIndentUsed(buffer, node.getStartPosition(), project);
162                                                 str= Strings.changeIndent(str, indent, project, new String(), typeRoot.findRecommendedLineSeparator());
163                                         }
164                                         return str;
165                                 }
166                         } catch (JavaModelException e) {
167                                 // ignore
168                         }
169                 }
170                 return null;
171         }
172
173     /**
174      * Returns the list that contains the given ASTNode. If the node
175      * isn't part of any list, <code>null</code> is returned.
176      *
177      * @param node the node in question
178      * @return the list that contains the node or <code>null</code>
179      */
180     public static List<? extends ASTNode> getContainingList(ASTNode node) {
181         StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
182         if (locationInParent != null && locationInParent.isChildListProperty()) {
183                 return (List<? extends ASTNode>) node.getParent().getStructuralProperty(locationInParent);
184         }
185         return null;
186     }
187
188         /**
189          * Returns a list of the direct children of a node. The siblings are ordered by start offset.
190          * @param node the node to get the children for
191          * @return the children
192          */
193         public static List<ASTNode> getChildren(ASTNode node) {
194                 ChildrenCollector visitor= new ChildrenCollector();
195                 node.accept(visitor);
196                 return visitor.result;
197         }
198
199         private static class ChildrenCollector extends GenericVisitor {
200                 public List<ASTNode> result;
201
202                 public ChildrenCollector() {
203                         super(true);
204                         result= null;
205                 }
206                 @Override
207                 protected boolean visitNode(ASTNode node) {
208                         if (result == null) { // first visitNode: on the node's parent: do nothing, return true
209                                 result= new ArrayList<ASTNode>();
210                                 return true;
211                         }
212                         result.add(node);
213                         return false;
214                 }
215         }
216
217         /**
218          * Returns true if this is an existing node, i.e. it was created as part of
219          * a parsing process of a source code file. Returns false if this is a newly
220          * created node which has not yet been given a source position.
221          *
222          * @param node the node to be tested.
223          * @return true if this is an existing node, false if not.
224          */
225         public static boolean isExistingNode(ASTNode node) {
226                 return node.getStartPosition() != -1;
227         }
228
229         /**
230          * Returns the element type. This is a convenience method that returns its
231          * argument if it is a simple type and the element type if the parameter is an array type.
232          * @param type The type to get the element type from.
233          * @return The element type of the type or the type itself.
234          */
235         public static Type getElementType(Type type) {
236                 if (! type.isArrayType())
237                         return type;
238                 return ((ArrayType)type).getElementType();
239         }
240
241         public static ASTNode findDeclaration(IBinding binding, ASTNode root) {
242                 root= root.getRoot();
243                 if (root instanceof CompilationUnit) {
244                         return ((CompilationUnit)root).findDeclaringNode(binding);
245                 }
246                 return null;
247         }
248
249         public static VariableDeclaration findVariableDeclaration(IVariableBinding binding, ASTNode root) {
250                 if (binding.isField())
251                         return null;
252                 ASTNode result= findDeclaration(binding, root);
253                 if (result instanceof VariableDeclaration)
254                                 return (VariableDeclaration)result;
255
256                 return null;
257         }
258
259         /**
260          * Returns the type node for the given declaration.
261          * @param declaration the declaration
262          * @return the type node
263          */
264         public static Type getType(VariableDeclaration declaration) {
265                 if (declaration instanceof SingleVariableDeclaration) {
266                         return ((SingleVariableDeclaration)declaration).getType();
267                 } else if (declaration instanceof VariableDeclarationFragment) {
268                         ASTNode parent= ((VariableDeclarationFragment)declaration).getParent();
269                         if (parent instanceof VariableDeclarationExpression)
270                                 return ((VariableDeclarationExpression)parent).getType();
271                         else if (parent instanceof VariableDeclarationStatement)
272                                 return ((VariableDeclarationStatement)parent).getType();
273                         else if (parent instanceof FieldDeclaration)
274                                 return ((FieldDeclaration)parent).getType();
275                 }
276                 Assert.isTrue(false, "Unknown VariableDeclaration"); //$NON-NLS-1$
277                 return null;
278         }
279
280         public static int getDimensions(VariableDeclaration declaration) {
281                 int dim= declaration.getExtraDimensions();
282                 Type type= getType(declaration);
283                 if (type instanceof ArrayType) {
284                         dim += ((ArrayType) type).getDimensions();
285                 }
286                 return dim;
287         }
288
289         public static List<IExtendedModifier> getModifiers(VariableDeclaration declaration) {
290                 Assert.isNotNull(declaration);
291                 if (declaration instanceof SingleVariableDeclaration) {
292                         return ((SingleVariableDeclaration)declaration).modifiers();
293                 } else if (declaration instanceof VariableDeclarationFragment) {
294                         ASTNode parent= declaration.getParent();
295                         if (parent instanceof VariableDeclarationExpression)
296                                 return ((VariableDeclarationExpression)parent).modifiers();
297                         else if (parent instanceof VariableDeclarationStatement)
298                                 return ((VariableDeclarationStatement)parent).modifiers();
299                 }
300                 return new ArrayList<IExtendedModifier>(0);
301         }
302
303         public static boolean isSingleDeclaration(VariableDeclaration declaration) {
304                 Assert.isNotNull(declaration);
305                 if (declaration instanceof SingleVariableDeclaration) {
306                         return true;
307                 } else if (declaration instanceof VariableDeclarationFragment) {
308                         ASTNode parent= declaration.getParent();
309                         if (parent instanceof VariableDeclarationExpression)
310                                 return ((VariableDeclarationExpression)parent).fragments().size() == 1;
311                         else if (parent instanceof VariableDeclarationStatement)
312                                 return ((VariableDeclarationStatement)parent).fragments().size() == 1;
313                 }
314                 return false;
315         }
316
317         public static boolean isLiteral(Expression expression) {
318                 int type= expression.getNodeType();
319                 return type == ASTNode.BOOLEAN_LITERAL || type == ASTNode.CHARACTER_LITERAL || type == ASTNode.NULL_LITERAL ||
320                         type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL;
321         }
322
323         public static boolean isLabel(SimpleName name) {
324                 int parentType= name.getParent().getNodeType();
325                 return parentType == ASTNode.LABELED_STATEMENT ||
326                         parentType == ASTNode.BREAK_STATEMENT || parentType != ASTNode.CONTINUE_STATEMENT;
327         }
328
329         public static boolean isStatic(BodyDeclaration declaration) {
330                 return Modifier.isStatic(declaration.getModifiers());
331         }
332
333         public static List<BodyDeclaration> getBodyDeclarations(ASTNode node) {
334                 if (node instanceof AbstractTypeDeclaration) {
335                         return ((AbstractTypeDeclaration)node).bodyDeclarations();
336                 } else if (node instanceof AnonymousClassDeclaration) {
337                         return ((AnonymousClassDeclaration)node).bodyDeclarations();
338                 }
339                 // should not happen.
340                 Assert.isTrue(false);
341                 return null;
342         }
343
344         /**
345          * Returns the structural property descriptor for the "bodyDeclarations" property
346          * of this node (element type: {@link BodyDeclaration}).
347          * 
348          * @param node the node, either an {@link AbstractTypeDeclaration} or an {@link AnonymousClassDeclaration}
349          * @return the property descriptor
350          */
351         public static ChildListPropertyDescriptor getBodyDeclarationsProperty(ASTNode node) {
352                 if (node instanceof AbstractTypeDeclaration) {
353                         return ((AbstractTypeDeclaration)node).getBodyDeclarationsProperty();
354                 } else if (node instanceof AnonymousClassDeclaration) {
355                         return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY;
356                 }
357                 // should not happen.
358                 Assert.isTrue(false);
359                 return null;
360         }
361
362         public static String getTypeName(Type type) {
363                 final StringBuffer buffer= new StringBuffer();
364                 ASTVisitor visitor= new ASTVisitor() {
365                         @Override
366                         public boolean visit(PrimitiveType node) {
367                                 buffer.append(node.getPrimitiveTypeCode().toString());
368                                 return false;
369                         }
370                         @Override
371                         public boolean visit(SimpleName node) {
372                                 buffer.append(node.getIdentifier());
373                                 return false;
374                         }
375                         @Override
376                         public boolean visit(QualifiedName node) {
377                                 buffer.append(node.getName().getIdentifier());
378                                 return false;
379                         }
380                         @Override
381                         public void endVisit(ArrayType node) {
382                                 buffer.append("[]"); //$NON-NLS-1$
383                         }
384                 };
385                 type.accept(visitor);
386                 return buffer.toString();
387         }
388
389         public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) {
390                 if (operator.equals(Assignment.Operator.PLUS_ASSIGN))
391                         return InfixExpression.Operator.PLUS;
392
393                 if (operator.equals(Assignment.Operator.MINUS_ASSIGN))
394                         return InfixExpression.Operator.MINUS;
395
396                 if (operator.equals(Assignment.Operator.TIMES_ASSIGN))
397                         return InfixExpression.Operator.TIMES;
398
399                 if (operator.equals(Assignment.Operator.DIVIDE_ASSIGN))
400                         return InfixExpression.Operator.DIVIDE;
401
402                 if (operator.equals(Assignment.Operator.BIT_AND_ASSIGN))
403                         return InfixExpression.Operator.AND;
404
405                 if (operator.equals(Assignment.Operator.BIT_OR_ASSIGN))
406                         return InfixExpression.Operator.OR;
407
408                 if (operator.equals(Assignment.Operator.BIT_XOR_ASSIGN))
409                         return InfixExpression.Operator.XOR;
410
411                 if (operator.equals(Assignment.Operator.REMAINDER_ASSIGN))
412                         return InfixExpression.Operator.REMAINDER;
413
414                 if (operator.equals(Assignment.Operator.LEFT_SHIFT_ASSIGN))
415                         return InfixExpression.Operator.LEFT_SHIFT;
416
417                 if (operator.equals(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN))
418                         return InfixExpression.Operator.RIGHT_SHIFT_SIGNED;
419
420                 if (operator.equals(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN))
421                         return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED;
422
423                 Assert.isTrue(false, "Cannot convert assignment operator"); //$NON-NLS-1$
424                 return null;
425         }
426
427         /**
428          * Returns true if a node at a given location is a body of a control statement. Such body nodes are
429          * interesting as when replacing them, it has to be evaluates if a Block is needed instead.
430          * E.g. <code> if (x) do(); -> if (x) { do1(); do2() } </code>
431          *
432          * @param locationInParent Location of the body node
433          * @return Returns true if the location is a body node location of a control statement.
434          */
435         public static boolean isControlStatementBody(StructuralPropertyDescriptor locationInParent) {
436                 return locationInParent == IfStatement.THEN_STATEMENT_PROPERTY
437                         || locationInParent == IfStatement.ELSE_STATEMENT_PROPERTY
438                         || locationInParent == ForStatement.BODY_PROPERTY
439                         || locationInParent == EnhancedForStatement.BODY_PROPERTY
440                         || locationInParent == WhileStatement.BODY_PROPERTY
441                         || locationInParent == DoStatement.BODY_PROPERTY;
442         }
443
444         /**
445          * Returns the type to which an inlined variable initializer should be cast, or
446          * <code>null</code> if no cast is necessary.
447          * 
448          * @param initializer the initializer expression of the variable to inline
449          * @param reference the reference to the variable (which is to be inlined)
450          * @return a type binding to which the initializer should be cast, or <code>null</code> iff no cast is necessary
451          * @since 3.6
452          */
453         public static ITypeBinding getExplicitCast(Expression initializer, Expression reference) {
454                 ITypeBinding initializerType= initializer.resolveTypeBinding();
455                 ITypeBinding referenceType= reference.resolveTypeBinding();
456                 if (initializerType == null || referenceType == null)
457                         return null;
458                 
459                 if (initializerType.isPrimitive() && referenceType.isPrimitive() && ! referenceType.isEqualTo(initializerType)) {
460                         return referenceType;
461                         
462                 } else if (initializerType.isPrimitive() && ! referenceType.isPrimitive()) { // initializer is autoboxed
463                         ITypeBinding unboxedReferenceType= Bindings.getUnboxedTypeBinding(referenceType, reference.getAST());
464                         if (!unboxedReferenceType.isEqualTo(initializerType))
465                                 return unboxedReferenceType;
466                         else if (needsExplicitBoxing(reference))
467                                 return referenceType;
468                         
469                 } else if (! initializerType.isPrimitive() && referenceType.isPrimitive()) { // initializer is autounboxed
470                         ITypeBinding unboxedInitializerType= Bindings.getUnboxedTypeBinding(initializerType, reference.getAST());
471                         if (!unboxedInitializerType.isEqualTo(referenceType))
472                                 return referenceType;
473                         
474                 } else if (initializerType.isRawType() && referenceType.isParameterizedType()) {
475                         return referenceType; // don't lose the unchecked conversion
476                         
477                 } else if (! TypeRules.canAssign(initializerType, referenceType)) {
478                         if (!Bindings.containsTypeVariables(referenceType))
479                                 return referenceType;
480                 }
481                 
482                 return null;
483         }
484
485         /**
486          * Returns whether an expression at the given location needs explicit boxing.
487          * 
488          * @param expression the expression
489          * @return <code>true</code> iff an expression at the given location needs explicit boxing
490          * @since 3.6
491          */
492         private static boolean needsExplicitBoxing(Expression expression) {
493                 StructuralPropertyDescriptor locationInParent= expression.getLocationInParent();
494                 if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY)
495                         return needsExplicitBoxing((ParenthesizedExpression) expression.getParent());
496                 
497                 if (locationInParent == ClassInstanceCreation.EXPRESSION_PROPERTY
498                                 || locationInParent == FieldAccess.EXPRESSION_PROPERTY
499                                 || locationInParent == MethodInvocation.EXPRESSION_PROPERTY)
500                         return true;
501                 
502                 return false;
503         }
504
505         /**
506          * Returns the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none.
507          * <p>
508          * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc.
509          * The resulting node may be in a totally different scope than the given node.
510          * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead.
511          * </p>
512          * @param node the node
513          * @param parentClass the class of the sought ancestor node
514          * @return the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none
515          */
516         public static ASTNode getParent(ASTNode node, Class<? extends ASTNode> parentClass) {
517                 do {
518                         node= node.getParent();
519                 } while (node != null && !parentClass.isInstance(node));
520                 return node;
521         }
522
523         /**
524          * Returns the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none.
525          * <p>
526          * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc.
527          * The resulting node may be in a totally different scope than the given node.
528          * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead.
529          * </p>
530          * @param node the node
531          * @param nodeType the node type constant from {@link ASTNode}
532          * @return the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none
533          */
534         public static ASTNode getParent(ASTNode node, int nodeType) {
535                 do {
536                         node= node.getParent();
537                 } while (node != null && node.getNodeType() != nodeType);
538                 return node;
539         }
540
541         public static ASTNode findParent(ASTNode node, StructuralPropertyDescriptor[][] pathes) {
542                 for (int p= 0; p < pathes.length; p++) {
543                         StructuralPropertyDescriptor[] path= pathes[p];
544                         ASTNode current= node;
545                         int d= path.length - 1;
546                         for (; d >= 0 && current != null; d--) {
547                                 StructuralPropertyDescriptor descriptor= path[d];
548                                 if (!descriptor.equals(current.getLocationInParent()))
549                                         break;
550                                 current= current.getParent();
551                         }
552                         if (d < 0)
553                                 return current;
554                 }
555                 return null;
556         }
557
558         public static ASTNode getNormalizedNode(ASTNode node) {
559                 ASTNode current= node;
560                 // normalize name
561                 if (QualifiedName.NAME_PROPERTY.equals(current.getLocationInParent())) {
562                         current= current.getParent();
563                 }
564                 // normalize type
565                 if (QualifiedType.NAME_PROPERTY.equals(current.getLocationInParent()) ||
566                         SimpleType.NAME_PROPERTY.equals(current.getLocationInParent())) {
567                         current= current.getParent();
568                 }
569                 // normalize parameterized types
570                 if (ParameterizedType.TYPE_PROPERTY.equals(current.getLocationInParent())) {
571                         current= current.getParent();
572                 }
573                 return current;
574         }
575
576         /**
577          * Returns <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
578          * (i.e. returns <code>false</code> if <code>parent == node</code>).
579          * 
580          * @param node node to test
581          * @param parent assumed parent
582          * @return <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
583          */
584         public static boolean isParent(ASTNode node, ASTNode parent) {
585                 Assert.isNotNull(parent);
586                 do {
587                         node= node.getParent();
588                         if (node == parent)
589                                 return true;
590                 } while (node != null);
591                 return false;
592         }
593
594         public static int getExclusiveEnd(ASTNode node){
595                 return node.getStartPosition() + node.getLength();
596         }
597
598         public static int getInclusiveEnd(ASTNode node){
599                 return node.getStartPosition() + node.getLength() - 1;
600         }
601
602         public static IMethodBinding getMethodBinding(Name node) {
603                 IBinding binding= node.resolveBinding();
604                 if (binding instanceof IMethodBinding)
605                         return (IMethodBinding)binding;
606                 return null;
607         }
608
609         public static IVariableBinding getVariableBinding(Name node) {
610                 IBinding binding= node.resolveBinding();
611                 if (binding instanceof IVariableBinding)
612                         return (IVariableBinding)binding;
613                 return null;
614         }
615
616         public static IVariableBinding getLocalVariableBinding(Name node) {
617                 IVariableBinding result= getVariableBinding(node);
618                 if (result == null || result.isField())
619                         return null;
620
621                 return result;
622         }
623
624         public static IVariableBinding getFieldBinding(Name node) {
625                 IVariableBinding result= getVariableBinding(node);
626                 if (result == null || !result.isField())
627                         return null;
628
629                 return result;
630         }
631
632         public static ITypeBinding getTypeBinding(Name node) {
633                 IBinding binding= node.resolveBinding();
634                 if (binding instanceof ITypeBinding)
635                         return (ITypeBinding)binding;
636                 return null;
637         }
638
639         /**
640          * Returns the receiver's type binding of the given method invocation.
641          *
642          * @param invocation method invocation to resolve type of
643          * @return the type binding of the receiver
644          */
645         public static ITypeBinding getReceiverTypeBinding(MethodInvocation invocation) {
646                 ITypeBinding result= null;
647                 Expression exp= invocation.getExpression();
648                 if(exp != null) {
649                         return exp.resolveTypeBinding();
650                 }
651                 else {
652                         AbstractTypeDeclaration type= (AbstractTypeDeclaration)getParent(invocation, AbstractTypeDeclaration.class);
653                         if (type != null)
654                                 return type.resolveBinding();
655                 }
656                 return result;
657         }
658
659         public static ITypeBinding getEnclosingType(ASTNode node) {
660                 while(node != null) {
661                         if (node instanceof AbstractTypeDeclaration) {
662                                 return ((AbstractTypeDeclaration)node).resolveBinding();
663                         } else if (node instanceof AnonymousClassDeclaration) {
664                                 return ((AnonymousClassDeclaration)node).resolveBinding();
665                         }
666                         node= node.getParent();
667                 }
668                 return null;
669         }
670
671         public static IProblem[] getProblems(ASTNode node, int scope, int severity) {
672                 ASTNode root= node.getRoot();
673                 if (!(root instanceof CompilationUnit))
674                         return EMPTY_PROBLEMS;
675                 IProblem[] problems= ((CompilationUnit)root).getProblems();
676                 if (root == node)
677                         return problems;
678                 final int iterations= computeIterations(scope);
679                 List<IProblem> result= new ArrayList<IProblem>(5);
680                 for (int i= 0; i < problems.length; i++) {
681                         IProblem problem= problems[i];
682                         boolean consider= false;
683                         if ((severity & PROBLEMS) == PROBLEMS)
684                                 consider= true;
685                         else if ((severity & WARNING) != 0)
686                                 consider= problem.isWarning();
687                         else if ((severity & ERROR) != 0)
688                                 consider= problem.isError();
689                         if (consider) {
690                                 ASTNode temp= node;
691                                 int count= iterations;
692                                 do {
693                                         int nodeOffset= temp.getStartPosition();
694                                         int problemOffset= problem.getSourceStart();
695                                         if (nodeOffset <= problemOffset && problemOffset < nodeOffset + temp.getLength()) {
696                                                 result.add(problem);
697                                                 count= 0;
698                                         } else {
699                                                 count--;
700                                         }
701                                 } while ((temp= temp.getParent()) != null && count > 0);
702                         }
703                 }
704                 return result.toArray(new IProblem[result.size()]);
705         }
706
707         public static Message[] getMessages(ASTNode node, int flags) {
708                 ASTNode root= node.getRoot();
709                 if (!(root instanceof CompilationUnit))
710                         return EMPTY_MESSAGES;
711                 Message[] messages= ((CompilationUnit)root).getMessages();
712                 if (root == node)
713                         return messages;
714                 final int iterations= computeIterations(flags);
715                 List<Message> result= new ArrayList<Message>(5);
716                 for (int i= 0; i < messages.length; i++) {
717                         Message message= messages[i];
718                         ASTNode temp= node;
719                         int count= iterations;
720                         do {
721                                 int nodeOffset= temp.getStartPosition();
722                                 int messageOffset= message.getStartPosition();
723                                 if (nodeOffset <= messageOffset && messageOffset < nodeOffset + temp.getLength()) {
724                                         result.add(message);
725                                         count= 0;
726                                 } else {
727                                         count--;
728                                 }
729                         } while ((temp= temp.getParent()) != null && count > 0);
730                 }
731                 return result.toArray(new Message[result.size()]);
732         }
733
734         private static int computeIterations(int flags) {
735                 switch (flags) {
736                         case NODE_ONLY:
737                                 return 1;
738                         case INCLUDE_ALL_PARENTS:
739                                 return Integer.MAX_VALUE;
740                         case INCLUDE_FIRST_PARENT:
741                                 return 2;
742                         default:
743                                 return 1;
744                 }
745         }
746
747
748         private static int getOrderPreference(BodyDeclaration member, MembersOrderPreferenceCache store) {
749                 int memberType= member.getNodeType();
750                 int modifiers= member.getModifiers();
751
752                 switch (memberType) {
753                         case ASTNode.TYPE_DECLARATION:
754                         case ASTNode.ENUM_DECLARATION :
755                         case ASTNode.ANNOTATION_TYPE_DECLARATION :
756                                 return store.getCategoryIndex(MembersOrderPreferenceCache.TYPE_INDEX) * 2;
757                         case ASTNode.FIELD_DECLARATION:
758                                 if (Modifier.isStatic(modifiers)) {
759                                         int index= store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_FIELDS_INDEX) * 2;
760                                         if (Modifier.isFinal(modifiers)) {
761                                                 return index; // first final static, then static
762                                         }
763                                         return index + 1;
764                                 }
765                                 return store.getCategoryIndex(MembersOrderPreferenceCache.FIELDS_INDEX) * 2;
766                         case ASTNode.INITIALIZER:
767                                 if (Modifier.isStatic(modifiers)) {
768                                         return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_INIT_INDEX) * 2;
769                                 }
770                                 return store.getCategoryIndex(MembersOrderPreferenceCache.INIT_INDEX) * 2;
771                         case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
772                                 return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2;
773                         case ASTNode.METHOD_DECLARATION:
774                                 if (Modifier.isStatic(modifiers)) {
775                                         return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_METHODS_INDEX) * 2;
776                                 }
777                                 if (((MethodDeclaration) member).isConstructor()) {
778                                         return store.getCategoryIndex(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX) * 2;
779                                 }
780                                 return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2;
781                         default:
782                                 return 100;
783                 }
784         }
785
786         /**
787          * Computes the insertion index to be used to add the given member to the
788          * the list <code>container</code>.
789          * @param member the member to add
790          * @param container a list containing objects of type <code>BodyDeclaration</code>
791          * @return the insertion index to be used
792          */
793         public static int getInsertionIndex(BodyDeclaration member, List<? extends BodyDeclaration> container) {
794                 int containerSize= container.size();
795
796                 MembersOrderPreferenceCache orderStore= JavaPlugin.getDefault().getMemberOrderPreferenceCache();
797
798                 int orderIndex= getOrderPreference(member, orderStore);
799
800                 int insertPos= containerSize;
801                 int insertPosOrderIndex= -1;
802
803                 for (int i= containerSize - 1; i >= 0; i--) {
804                         int currOrderIndex= getOrderPreference(container.get(i), orderStore);
805                         if (orderIndex == currOrderIndex) {
806                                 if (insertPosOrderIndex != orderIndex) { // no perfect match yet
807                                         insertPos= i + 1; // after a same kind
808                                         insertPosOrderIndex= orderIndex; // perfect match
809                                 }
810                         } else if (insertPosOrderIndex != orderIndex) { // not yet a perfect match
811                                 if (currOrderIndex < orderIndex) { // we are bigger
812                                         if (insertPosOrderIndex == -1) {
813                                                 insertPos= i + 1; // after
814                                                 insertPosOrderIndex= currOrderIndex;
815                                         }
816                                 } else {
817                                         insertPos= i; // before
818                                         insertPosOrderIndex= currOrderIndex;
819                                 }
820                         }
821                 }
822                 return insertPos;
823         }
824
825         public static SimpleName getLeftMostSimpleName(Name name) {
826                 if (name instanceof SimpleName) {
827                         return (SimpleName)name;
828                 } else {
829                         final SimpleName[] result= new SimpleName[1];
830                         ASTVisitor visitor= new ASTVisitor() {
831                                 @Override
832                                 public boolean visit(QualifiedName qualifiedName) {
833                                         Name left= qualifiedName.getQualifier();
834                                         if (left instanceof SimpleName)
835                                                 result[0]= (SimpleName)left;
836                                         else
837                                                 left.accept(this);
838                                         return false;
839                                 }
840                         };
841                         name.accept(visitor);
842                         return result[0];
843                 }
844         }
845
846         public static SimpleType getLeftMostSimpleType(QualifiedType type) {
847                 final SimpleType[] result= new SimpleType[1];
848                 ASTVisitor visitor= new ASTVisitor() {
849                         @Override
850                         public boolean visit(QualifiedType qualifiedType) {
851                                 Type left= qualifiedType.getQualifier();
852                                 if (left instanceof SimpleType)
853                                         result[0]= (SimpleType)left;
854                                 else
855                                         left.accept(this);
856                                 return false;
857                         }
858                 };
859                 type.accept(visitor);
860                 return result[0];
861         }
862
863         public static Name getTopMostName(Name name) {
864                 Name result= name;
865                 while(result.getParent() instanceof Name) {
866                         result= (Name)result.getParent();
867                 }
868                 return result;
869         }
870
871         public static Type getTopMostType(Type type) {
872                 Type result= type;
873                 while(result.getParent() instanceof Type) {
874                         result= (Type)result.getParent();
875                 }
876                 return result;
877         }
878
879         public static int changeVisibility(int modifiers, int visibility) {
880                 return (modifiers & CLEAR_VISIBILITY) | visibility;
881         }
882
883         /**
884          * Adds flags to the given node and all its descendants.
885          * @param root The root node
886          * @param flags The flags to set
887          */
888         public static void setFlagsToAST(ASTNode root, final int flags) {
889                 root.accept(new GenericVisitor(true) {
890                         @Override
891                         protected boolean visitNode(ASTNode node) {
892                                 node.setFlags(node.getFlags() | flags);
893                                 return true;
894                         }
895                 });
896         }
897
898         public static String getQualifier(Name name) {
899                 if (name.isQualifiedName()) {
900                         return ((QualifiedName) name).getQualifier().getFullyQualifiedName();
901                 }
902                 return ""; //$NON-NLS-1$
903         }
904
905         public static String getSimpleNameIdentifier(Name name) {
906                 if (name.isQualifiedName()) {
907                         return ((QualifiedName) name).getName().getIdentifier();
908                 } else {
909                         return ((SimpleName) name).getIdentifier();
910                 }
911         }
912
913         public static boolean isDeclaration(Name name) {
914                 if (name.isQualifiedName()) {
915                         return ((QualifiedName) name).getName().isDeclaration();
916                 } else {
917                         return ((SimpleName) name).isDeclaration();
918                 }
919         }
920
921         public static Modifier findModifierNode(int flag, List<IExtendedModifier> modifiers) {
922                 for (int i= 0; i < modifiers.size(); i++) {
923                         Object curr= modifiers.get(i);
924                         if (curr instanceof Modifier && ((Modifier) curr).getKeyword().toFlagValue() == flag) {
925                                 return (Modifier) curr;
926                         }
927                 }
928                 return null;
929         }
930
931         public static ITypeBinding getTypeBinding(CompilationUnit root, IType type) throws JavaModelException {
932                 if (type.isAnonymous()) {
933                         final IJavaElement parent= type.getParent();
934                         if (parent instanceof IField && Flags.isEnum(((IMember) parent).getFlags())) {
935                                 final EnumConstantDeclaration constant= (EnumConstantDeclaration) NodeFinder.perform(root, ((ISourceReference) parent).getSourceRange());
936                                 if (constant != null) {
937                                         final AnonymousClassDeclaration declaration= constant.getAnonymousClassDeclaration();
938                                         if (declaration != null)
939                                                 return declaration.resolveBinding();
940                                 }
941                         } else {
942                                 final ClassInstanceCreation creation= (ClassInstanceCreation) getParent(NodeFinder.perform(root, type.getNameRange()), ClassInstanceCreation.class);
943                                 if (creation != null)
944                                         return creation.resolveTypeBinding();
945                         }
946                 } else {
947                         final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getParent(NodeFinder.perform(root, type.getNameRange()), AbstractTypeDeclaration.class);
948                         if (declaration != null)
949                                 return declaration.resolveBinding();
950                 }
951                 return null;
952         }
953
954         /**
955          * Escapes a string value to a literal that can be used in Java source.
956          * 
957          * @param stringValue the string value 
958          * @return the escaped string
959          * @see StringLiteral#getEscapedValue()
960          */
961         public static String getEscapedStringLiteral(String stringValue) {
962                 StringLiteral stringLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newStringLiteral();
963                 stringLiteral.setLiteralValue(stringValue);
964                 return stringLiteral.getEscapedValue();
965         }
966
967         /**
968          * Escapes a character value to a literal that can be used in Java source.
969          * 
970          * @param ch the character value 
971          * @return the escaped string
972          * @see CharacterLiteral#getEscapedValue()
973          */
974         public static String getEscapedCharacterLiteral(char ch) {
975                 CharacterLiteral characterLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newCharacterLiteral();
976                 characterLiteral.setCharValue(ch);
977                 return characterLiteral.getEscapedValue();
978         }
979
980 }