]> git.uio.no Git - ifi-stolz-refaktor.git/blame - 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
CommitLineData
1b2798f6
EK
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 *******************************************************************************/
17package org.eclipse.jdt.internal.corext.dom;
18
19import java.util.ArrayList;
20import java.util.List;
21import java.util.Map;
22
23import org.eclipse.core.runtime.Assert;
24
25import org.eclipse.text.edits.TextEdit;
26
27import org.eclipse.jface.text.BadLocationException;
28import org.eclipse.jface.text.Document;
29
30import org.eclipse.jdt.core.Flags;
31import org.eclipse.jdt.core.IBuffer;
32import org.eclipse.jdt.core.IField;
33import org.eclipse.jdt.core.IJavaElement;
34import org.eclipse.jdt.core.IJavaProject;
35import org.eclipse.jdt.core.IMember;
36import org.eclipse.jdt.core.ISourceReference;
37import org.eclipse.jdt.core.IType;
38import org.eclipse.jdt.core.ITypeRoot;
39import org.eclipse.jdt.core.JavaModelException;
40import org.eclipse.jdt.core.compiler.IProblem;
41import org.eclipse.jdt.core.dom.AST;
42import org.eclipse.jdt.core.dom.ASTNode;
43import org.eclipse.jdt.core.dom.ASTVisitor;
44import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
45import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
46import org.eclipse.jdt.core.dom.ArrayType;
47import org.eclipse.jdt.core.dom.Assignment;
48import org.eclipse.jdt.core.dom.BodyDeclaration;
49import org.eclipse.jdt.core.dom.CharacterLiteral;
50import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
51import org.eclipse.jdt.core.dom.ClassInstanceCreation;
52import org.eclipse.jdt.core.dom.CompilationUnit;
53import org.eclipse.jdt.core.dom.DoStatement;
54import org.eclipse.jdt.core.dom.EnhancedForStatement;
55import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
56import org.eclipse.jdt.core.dom.Expression;
57import org.eclipse.jdt.core.dom.FieldAccess;
58import org.eclipse.jdt.core.dom.FieldDeclaration;
59import org.eclipse.jdt.core.dom.ForStatement;
60import org.eclipse.jdt.core.dom.IBinding;
61import org.eclipse.jdt.core.dom.IExtendedModifier;
62import org.eclipse.jdt.core.dom.IMethodBinding;
63import org.eclipse.jdt.core.dom.ITypeBinding;
64import org.eclipse.jdt.core.dom.IVariableBinding;
65import org.eclipse.jdt.core.dom.IfStatement;
66import org.eclipse.jdt.core.dom.InfixExpression;
67import org.eclipse.jdt.core.dom.Message;
68import org.eclipse.jdt.core.dom.MethodDeclaration;
69import org.eclipse.jdt.core.dom.MethodInvocation;
70import org.eclipse.jdt.core.dom.Modifier;
71import org.eclipse.jdt.core.dom.Name;
72import org.eclipse.jdt.core.dom.NodeFinder;
73import org.eclipse.jdt.core.dom.ParameterizedType;
74import org.eclipse.jdt.core.dom.ParenthesizedExpression;
75import org.eclipse.jdt.core.dom.PrimitiveType;
76import org.eclipse.jdt.core.dom.QualifiedName;
77import org.eclipse.jdt.core.dom.QualifiedType;
78import org.eclipse.jdt.core.dom.SimpleName;
79import org.eclipse.jdt.core.dom.SimpleType;
80import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
81import org.eclipse.jdt.core.dom.StringLiteral;
82import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
83import org.eclipse.jdt.core.dom.Type;
84import org.eclipse.jdt.core.dom.VariableDeclaration;
85import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
86import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
87import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
88import org.eclipse.jdt.core.dom.WhileStatement;
89
90import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
91import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
92import org.eclipse.jdt.internal.corext.util.Strings;
93
94import org.eclipse.jdt.internal.ui.JavaPlugin;
95import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
96import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
97import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
98
99public 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}