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
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;
19 import java.util.ArrayList;
20 import java.util.List;
23 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.text.edits.TextEdit;
27 import org.eclipse.jface.text.BadLocationException;
28 import org.eclipse.jface.text.Document;
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;
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;
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;
99 public class ASTNodes {
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;
105 public static final int WARNING= 1 << 0;
106 public static final int ERROR= 1 << 1;
107 public static final int PROBLEMS= WARNING | ERROR;
109 private static final Message[] EMPTY_MESSAGES= new Message[0];
110 private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0];
112 private static final int CLEAR_VISIBILITY= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
119 public static String asString(ASTNode node) {
120 ASTFlattener flattener= new ASTFlattener();
121 node.accept(flattener);
122 return flattener.getResult();
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);
129 Document document= new Document(unformatted);
131 edit.apply(document, TextEdit.NONE);
132 } catch (BadLocationException e) {
135 return document.get();
137 return unformatted; // unknown node
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.
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();
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);
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());
166 } catch (JavaModelException e) {
174 * Returns the list that contains the given ASTNode. If the node
175 * isn't part of any list, <code>null</code> is returned.
177 * @param node the node in question
178 * @return the list that contains the node or <code>null</code>
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);
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
193 public static List<ASTNode> getChildren(ASTNode node) {
194 ChildrenCollector visitor= new ChildrenCollector();
195 node.accept(visitor);
196 return visitor.result;
199 private static class ChildrenCollector extends GenericVisitor {
200 public List<ASTNode> result;
202 public ChildrenCollector() {
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>();
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.
222 * @param node the node to be tested.
223 * @return true if this is an existing node, false if not.
225 public static boolean isExistingNode(ASTNode node) {
226 return node.getStartPosition() != -1;
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.
235 public static Type getElementType(Type type) {
236 if (! type.isArrayType())
238 return ((ArrayType)type).getElementType();
241 public static ASTNode findDeclaration(IBinding binding, ASTNode root) {
242 root= root.getRoot();
243 if (root instanceof CompilationUnit) {
244 return ((CompilationUnit)root).findDeclaringNode(binding);
249 public static VariableDeclaration findVariableDeclaration(IVariableBinding binding, ASTNode root) {
250 if (binding.isField())
252 ASTNode result= findDeclaration(binding, root);
253 if (result instanceof VariableDeclaration)
254 return (VariableDeclaration)result;
260 * Returns the type node for the given declaration.
261 * @param declaration the declaration
262 * @return the type node
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();
276 Assert.isTrue(false, "Unknown VariableDeclaration"); //$NON-NLS-1$
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();
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();
300 return new ArrayList<IExtendedModifier>(0);
303 public static boolean isSingleDeclaration(VariableDeclaration declaration) {
304 Assert.isNotNull(declaration);
305 if (declaration instanceof SingleVariableDeclaration) {
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;
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;
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;
329 public static boolean isStatic(BodyDeclaration declaration) {
330 return Modifier.isStatic(declaration.getModifiers());
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();
339 // should not happen.
340 Assert.isTrue(false);
345 * Returns the structural property descriptor for the "bodyDeclarations" property
346 * of this node (element type: {@link BodyDeclaration}).
348 * @param node the node, either an {@link AbstractTypeDeclaration} or an {@link AnonymousClassDeclaration}
349 * @return the property descriptor
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;
357 // should not happen.
358 Assert.isTrue(false);
362 public static String getTypeName(Type type) {
363 final StringBuffer buffer= new StringBuffer();
364 ASTVisitor visitor= new ASTVisitor() {
366 public boolean visit(PrimitiveType node) {
367 buffer.append(node.getPrimitiveTypeCode().toString());
371 public boolean visit(SimpleName node) {
372 buffer.append(node.getIdentifier());
376 public boolean visit(QualifiedName node) {
377 buffer.append(node.getName().getIdentifier());
381 public void endVisit(ArrayType node) {
382 buffer.append("[]"); //$NON-NLS-1$
385 type.accept(visitor);
386 return buffer.toString();
389 public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) {
390 if (operator.equals(Assignment.Operator.PLUS_ASSIGN))
391 return InfixExpression.Operator.PLUS;
393 if (operator.equals(Assignment.Operator.MINUS_ASSIGN))
394 return InfixExpression.Operator.MINUS;
396 if (operator.equals(Assignment.Operator.TIMES_ASSIGN))
397 return InfixExpression.Operator.TIMES;
399 if (operator.equals(Assignment.Operator.DIVIDE_ASSIGN))
400 return InfixExpression.Operator.DIVIDE;
402 if (operator.equals(Assignment.Operator.BIT_AND_ASSIGN))
403 return InfixExpression.Operator.AND;
405 if (operator.equals(Assignment.Operator.BIT_OR_ASSIGN))
406 return InfixExpression.Operator.OR;
408 if (operator.equals(Assignment.Operator.BIT_XOR_ASSIGN))
409 return InfixExpression.Operator.XOR;
411 if (operator.equals(Assignment.Operator.REMAINDER_ASSIGN))
412 return InfixExpression.Operator.REMAINDER;
414 if (operator.equals(Assignment.Operator.LEFT_SHIFT_ASSIGN))
415 return InfixExpression.Operator.LEFT_SHIFT;
417 if (operator.equals(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN))
418 return InfixExpression.Operator.RIGHT_SHIFT_SIGNED;
420 if (operator.equals(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN))
421 return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED;
423 Assert.isTrue(false, "Cannot convert assignment operator"); //$NON-NLS-1$
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>
432 * @param locationInParent Location of the body node
433 * @return Returns true if the location is a body node location of a control statement.
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;
445 * Returns the type to which an inlined variable initializer should be cast, or
446 * <code>null</code> if no cast is necessary.
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
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)
459 if (initializerType.isPrimitive() && referenceType.isPrimitive() && ! referenceType.isEqualTo(initializerType)) {
460 return referenceType;
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;
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;
474 } else if (initializerType.isRawType() && referenceType.isParameterizedType()) {
475 return referenceType; // don't lose the unchecked conversion
477 } else if (! TypeRules.canAssign(initializerType, referenceType)) {
478 if (!Bindings.containsTypeVariables(referenceType))
479 return referenceType;
486 * Returns whether an expression at the given location needs explicit boxing.
488 * @param expression the expression
489 * @return <code>true</code> iff an expression at the given location needs explicit boxing
492 private static boolean needsExplicitBoxing(Expression expression) {
493 StructuralPropertyDescriptor locationInParent= expression.getLocationInParent();
494 if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY)
495 return needsExplicitBoxing((ParenthesizedExpression) expression.getParent());
497 if (locationInParent == ClassInstanceCreation.EXPRESSION_PROPERTY
498 || locationInParent == FieldAccess.EXPRESSION_PROPERTY
499 || locationInParent == MethodInvocation.EXPRESSION_PROPERTY)
506 * Returns the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none.
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.
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
516 public static ASTNode getParent(ASTNode node, Class<? extends ASTNode> parentClass) {
518 node= node.getParent();
519 } while (node != null && !parentClass.isInstance(node));
524 * Returns the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none.
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.
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
534 public static ASTNode getParent(ASTNode node, int nodeType) {
536 node= node.getParent();
537 } while (node != null && node.getNodeType() != nodeType);
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()))
550 current= current.getParent();
558 public static ASTNode getNormalizedNode(ASTNode node) {
559 ASTNode current= node;
561 if (QualifiedName.NAME_PROPERTY.equals(current.getLocationInParent())) {
562 current= current.getParent();
565 if (QualifiedType.NAME_PROPERTY.equals(current.getLocationInParent()) ||
566 SimpleType.NAME_PROPERTY.equals(current.getLocationInParent())) {
567 current= current.getParent();
569 // normalize parameterized types
570 if (ParameterizedType.TYPE_PROPERTY.equals(current.getLocationInParent())) {
571 current= current.getParent();
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>).
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>
584 public static boolean isParent(ASTNode node, ASTNode parent) {
585 Assert.isNotNull(parent);
587 node= node.getParent();
590 } while (node != null);
594 public static int getExclusiveEnd(ASTNode node){
595 return node.getStartPosition() + node.getLength();
598 public static int getInclusiveEnd(ASTNode node){
599 return node.getStartPosition() + node.getLength() - 1;
602 public static IMethodBinding getMethodBinding(Name node) {
603 IBinding binding= node.resolveBinding();
604 if (binding instanceof IMethodBinding)
605 return (IMethodBinding)binding;
609 public static IVariableBinding getVariableBinding(Name node) {
610 IBinding binding= node.resolveBinding();
611 if (binding instanceof IVariableBinding)
612 return (IVariableBinding)binding;
616 public static IVariableBinding getLocalVariableBinding(Name node) {
617 IVariableBinding result= getVariableBinding(node);
618 if (result == null || result.isField())
624 public static IVariableBinding getFieldBinding(Name node) {
625 IVariableBinding result= getVariableBinding(node);
626 if (result == null || !result.isField())
632 public static ITypeBinding getTypeBinding(Name node) {
633 IBinding binding= node.resolveBinding();
634 if (binding instanceof ITypeBinding)
635 return (ITypeBinding)binding;
640 * Returns the receiver's type binding of the given method invocation.
642 * @param invocation method invocation to resolve type of
643 * @return the type binding of the receiver
645 public static ITypeBinding getReceiverTypeBinding(MethodInvocation invocation) {
646 ITypeBinding result= null;
647 Expression exp= invocation.getExpression();
649 return exp.resolveTypeBinding();
652 AbstractTypeDeclaration type= (AbstractTypeDeclaration)getParent(invocation, AbstractTypeDeclaration.class);
654 return type.resolveBinding();
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();
666 node= node.getParent();
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();
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)
685 else if ((severity & WARNING) != 0)
686 consider= problem.isWarning();
687 else if ((severity & ERROR) != 0)
688 consider= problem.isError();
691 int count= iterations;
693 int nodeOffset= temp.getStartPosition();
694 int problemOffset= problem.getSourceStart();
695 if (nodeOffset <= problemOffset && problemOffset < nodeOffset + temp.getLength()) {
701 } while ((temp= temp.getParent()) != null && count > 0);
704 return result.toArray(new IProblem[result.size()]);
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();
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];
719 int count= iterations;
721 int nodeOffset= temp.getStartPosition();
722 int messageOffset= message.getStartPosition();
723 if (nodeOffset <= messageOffset && messageOffset < nodeOffset + temp.getLength()) {
729 } while ((temp= temp.getParent()) != null && count > 0);
731 return result.toArray(new Message[result.size()]);
734 private static int computeIterations(int flags) {
738 case INCLUDE_ALL_PARENTS:
739 return Integer.MAX_VALUE;
740 case INCLUDE_FIRST_PARENT:
748 private static int getOrderPreference(BodyDeclaration member, MembersOrderPreferenceCache store) {
749 int memberType= member.getNodeType();
750 int modifiers= member.getModifiers();
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
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;
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;
777 if (((MethodDeclaration) member).isConstructor()) {
778 return store.getCategoryIndex(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX) * 2;
780 return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2;
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
793 public static int getInsertionIndex(BodyDeclaration member, List<? extends BodyDeclaration> container) {
794 int containerSize= container.size();
796 MembersOrderPreferenceCache orderStore= JavaPlugin.getDefault().getMemberOrderPreferenceCache();
798 int orderIndex= getOrderPreference(member, orderStore);
800 int insertPos= containerSize;
801 int insertPosOrderIndex= -1;
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
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;
817 insertPos= i; // before
818 insertPosOrderIndex= currOrderIndex;
825 public static SimpleName getLeftMostSimpleName(Name name) {
826 if (name instanceof SimpleName) {
827 return (SimpleName)name;
829 final SimpleName[] result= new SimpleName[1];
830 ASTVisitor visitor= new ASTVisitor() {
832 public boolean visit(QualifiedName qualifiedName) {
833 Name left= qualifiedName.getQualifier();
834 if (left instanceof SimpleName)
835 result[0]= (SimpleName)left;
841 name.accept(visitor);
846 public static SimpleType getLeftMostSimpleType(QualifiedType type) {
847 final SimpleType[] result= new SimpleType[1];
848 ASTVisitor visitor= new ASTVisitor() {
850 public boolean visit(QualifiedType qualifiedType) {
851 Type left= qualifiedType.getQualifier();
852 if (left instanceof SimpleType)
853 result[0]= (SimpleType)left;
859 type.accept(visitor);
863 public static Name getTopMostName(Name name) {
865 while(result.getParent() instanceof Name) {
866 result= (Name)result.getParent();
871 public static Type getTopMostType(Type type) {
873 while(result.getParent() instanceof Type) {
874 result= (Type)result.getParent();
879 public static int changeVisibility(int modifiers, int visibility) {
880 return (modifiers & CLEAR_VISIBILITY) | visibility;
884 * Adds flags to the given node and all its descendants.
885 * @param root The root node
886 * @param flags The flags to set
888 public static void setFlagsToAST(ASTNode root, final int flags) {
889 root.accept(new GenericVisitor(true) {
891 protected boolean visitNode(ASTNode node) {
892 node.setFlags(node.getFlags() | flags);
898 public static String getQualifier(Name name) {
899 if (name.isQualifiedName()) {
900 return ((QualifiedName) name).getQualifier().getFullyQualifiedName();
902 return ""; //$NON-NLS-1$
905 public static String getSimpleNameIdentifier(Name name) {
906 if (name.isQualifiedName()) {
907 return ((QualifiedName) name).getName().getIdentifier();
909 return ((SimpleName) name).getIdentifier();
913 public static boolean isDeclaration(Name name) {
914 if (name.isQualifiedName()) {
915 return ((QualifiedName) name).getName().isDeclaration();
917 return ((SimpleName) name).isDeclaration();
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;
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();
942 final ClassInstanceCreation creation= (ClassInstanceCreation) getParent(NodeFinder.perform(root, type.getNameRange()), ClassInstanceCreation.class);
943 if (creation != null)
944 return creation.resolveTypeBinding();
947 final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getParent(NodeFinder.perform(root, type.getNameRange()), AbstractTypeDeclaration.class);
948 if (declaration != null)
949 return declaration.resolveBinding();
955 * Escapes a string value to a literal that can be used in Java source.
957 * @param stringValue the string value
958 * @return the escaped string
959 * @see StringLiteral#getEscapedValue()
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();
968 * Escapes a character value to a literal that can be used in Java source.
970 * @param ch the character value
971 * @return the escaped string
972 * @see CharacterLiteral#getEscapedValue()
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();