--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
+ * bug "inline method - doesn't handle implicit cast" (see
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
+ * bug Encapsulate field can fail when two variables in one variable declaration (see
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=51540).
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.text.edits.TextEdit;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.IBuffer;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.Message;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
+import org.eclipse.jdt.internal.corext.util.Strings;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
+import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
+import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
+
+public class ASTNodes {
+
+ public static final int NODE_ONLY= 0;
+ public static final int INCLUDE_FIRST_PARENT= 1;
+ public static final int INCLUDE_ALL_PARENTS= 2;
+
+ public static final int WARNING= 1 << 0;
+ public static final int ERROR= 1 << 1;
+ public static final int PROBLEMS= WARNING | ERROR;
+
+ private static final Message[] EMPTY_MESSAGES= new Message[0];
+ private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0];
+
+ private static final int CLEAR_VISIBILITY= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
+
+
+ private ASTNodes() {
+ // no instance;
+ }
+
+ public static String asString(ASTNode node) {
+ ASTFlattener flattener= new ASTFlattener();
+ node.accept(flattener);
+ return flattener.getResult();
+ }
+
+ public static String asFormattedString(ASTNode node, int indent, String lineDelim, Map<String, String> options) {
+ String unformatted= asString(node);
+ TextEdit edit= CodeFormatterUtil.format2(node, unformatted, indent, lineDelim, options);
+ if (edit != null) {
+ Document document= new Document(unformatted);
+ try {
+ edit.apply(document, TextEdit.NONE);
+ } catch (BadLocationException e) {
+ JavaPlugin.log(e);
+ }
+ return document.get();
+ }
+ return unformatted; // unknown node
+ }
+
+
+ /**
+ * Returns the source of the given node from the location where it was parsed.
+ * @param node the node to get the source from
+ * @param extendedRange if set, the extended ranges of the nodes should ne used
+ * @param removeIndent if set, the indentation is removed.
+ * @return return the source for the given node or null if accessing the source failed.
+ */
+ public static String getNodeSource(ASTNode node, boolean extendedRange, boolean removeIndent) {
+ ASTNode root= node.getRoot();
+ if (root instanceof CompilationUnit) {
+ CompilationUnit astRoot= (CompilationUnit) root;
+ ITypeRoot typeRoot= astRoot.getTypeRoot();
+ try {
+ if (typeRoot != null && typeRoot.getBuffer() != null) {
+ IBuffer buffer= typeRoot.getBuffer();
+ int offset= extendedRange ? astRoot.getExtendedStartPosition(node) : node.getStartPosition();
+ int length= extendedRange ? astRoot.getExtendedLength(node) : node.getLength();
+ String str= buffer.getText(offset, length);
+ if (removeIndent) {
+ IJavaProject project= typeRoot.getJavaProject();
+ int indent= StubUtility.getIndentUsed(buffer, node.getStartPosition(), project);
+ str= Strings.changeIndent(str, indent, project, new String(), typeRoot.findRecommendedLineSeparator());
+ }
+ return str;
+ }
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the list that contains the given ASTNode. If the node
+ * isn't part of any list, <code>null</code> is returned.
+ *
+ * @param node the node in question
+ * @return the list that contains the node or <code>null</code>
+ */
+ public static List<? extends ASTNode> getContainingList(ASTNode node) {
+ StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
+ if (locationInParent != null && locationInParent.isChildListProperty()) {
+ return (List<? extends ASTNode>) node.getParent().getStructuralProperty(locationInParent);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of the direct children of a node. The siblings are ordered by start offset.
+ * @param node the node to get the children for
+ * @return the children
+ */
+ public static List<ASTNode> getChildren(ASTNode node) {
+ ChildrenCollector visitor= new ChildrenCollector();
+ node.accept(visitor);
+ return visitor.result;
+ }
+
+ private static class ChildrenCollector extends GenericVisitor {
+ public List<ASTNode> result;
+
+ public ChildrenCollector() {
+ super(true);
+ result= null;
+ }
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ if (result == null) { // first visitNode: on the node's parent: do nothing, return true
+ result= new ArrayList<ASTNode>();
+ return true;
+ }
+ result.add(node);
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if this is an existing node, i.e. it was created as part of
+ * a parsing process of a source code file. Returns false if this is a newly
+ * created node which has not yet been given a source position.
+ *
+ * @param node the node to be tested.
+ * @return true if this is an existing node, false if not.
+ */
+ public static boolean isExistingNode(ASTNode node) {
+ return node.getStartPosition() != -1;
+ }
+
+ /**
+ * Returns the element type. This is a convenience method that returns its
+ * argument if it is a simple type and the element type if the parameter is an array type.
+ * @param type The type to get the element type from.
+ * @return The element type of the type or the type itself.
+ */
+ public static Type getElementType(Type type) {
+ if (! type.isArrayType())
+ return type;
+ return ((ArrayType)type).getElementType();
+ }
+
+ public static ASTNode findDeclaration(IBinding binding, ASTNode root) {
+ root= root.getRoot();
+ if (root instanceof CompilationUnit) {
+ return ((CompilationUnit)root).findDeclaringNode(binding);
+ }
+ return null;
+ }
+
+ public static VariableDeclaration findVariableDeclaration(IVariableBinding binding, ASTNode root) {
+ if (binding.isField())
+ return null;
+ ASTNode result= findDeclaration(binding, root);
+ if (result instanceof VariableDeclaration)
+ return (VariableDeclaration)result;
+
+ return null;
+ }
+
+ /**
+ * Returns the type node for the given declaration.
+ * @param declaration the declaration
+ * @return the type node
+ */
+ public static Type getType(VariableDeclaration declaration) {
+ if (declaration instanceof SingleVariableDeclaration) {
+ return ((SingleVariableDeclaration)declaration).getType();
+ } else if (declaration instanceof VariableDeclarationFragment) {
+ ASTNode parent= ((VariableDeclarationFragment)declaration).getParent();
+ if (parent instanceof VariableDeclarationExpression)
+ return ((VariableDeclarationExpression)parent).getType();
+ else if (parent instanceof VariableDeclarationStatement)
+ return ((VariableDeclarationStatement)parent).getType();
+ else if (parent instanceof FieldDeclaration)
+ return ((FieldDeclaration)parent).getType();
+ }
+ Assert.isTrue(false, "Unknown VariableDeclaration"); //$NON-NLS-1$
+ return null;
+ }
+
+ public static int getDimensions(VariableDeclaration declaration) {
+ int dim= declaration.getExtraDimensions();
+ Type type= getType(declaration);
+ if (type instanceof ArrayType) {
+ dim += ((ArrayType) type).getDimensions();
+ }
+ return dim;
+ }
+
+ public static List<IExtendedModifier> getModifiers(VariableDeclaration declaration) {
+ Assert.isNotNull(declaration);
+ if (declaration instanceof SingleVariableDeclaration) {
+ return ((SingleVariableDeclaration)declaration).modifiers();
+ } else if (declaration instanceof VariableDeclarationFragment) {
+ ASTNode parent= declaration.getParent();
+ if (parent instanceof VariableDeclarationExpression)
+ return ((VariableDeclarationExpression)parent).modifiers();
+ else if (parent instanceof VariableDeclarationStatement)
+ return ((VariableDeclarationStatement)parent).modifiers();
+ }
+ return new ArrayList<IExtendedModifier>(0);
+ }
+
+ public static boolean isSingleDeclaration(VariableDeclaration declaration) {
+ Assert.isNotNull(declaration);
+ if (declaration instanceof SingleVariableDeclaration) {
+ return true;
+ } else if (declaration instanceof VariableDeclarationFragment) {
+ ASTNode parent= declaration.getParent();
+ if (parent instanceof VariableDeclarationExpression)
+ return ((VariableDeclarationExpression)parent).fragments().size() == 1;
+ else if (parent instanceof VariableDeclarationStatement)
+ return ((VariableDeclarationStatement)parent).fragments().size() == 1;
+ }
+ return false;
+ }
+
+ public static boolean isLiteral(Expression expression) {
+ int type= expression.getNodeType();
+ return type == ASTNode.BOOLEAN_LITERAL || type == ASTNode.CHARACTER_LITERAL || type == ASTNode.NULL_LITERAL ||
+ type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL;
+ }
+
+ public static boolean isLabel(SimpleName name) {
+ int parentType= name.getParent().getNodeType();
+ return parentType == ASTNode.LABELED_STATEMENT ||
+ parentType == ASTNode.BREAK_STATEMENT || parentType != ASTNode.CONTINUE_STATEMENT;
+ }
+
+ public static boolean isStatic(BodyDeclaration declaration) {
+ return Modifier.isStatic(declaration.getModifiers());
+ }
+
+ public static List<BodyDeclaration> getBodyDeclarations(ASTNode node) {
+ if (node instanceof AbstractTypeDeclaration) {
+ return ((AbstractTypeDeclaration)node).bodyDeclarations();
+ } else if (node instanceof AnonymousClassDeclaration) {
+ return ((AnonymousClassDeclaration)node).bodyDeclarations();
+ }
+ // should not happen.
+ Assert.isTrue(false);
+ return null;
+ }
+
+ /**
+ * Returns the structural property descriptor for the "bodyDeclarations" property
+ * of this node (element type: {@link BodyDeclaration}).
+ *
+ * @param node the node, either an {@link AbstractTypeDeclaration} or an {@link AnonymousClassDeclaration}
+ * @return the property descriptor
+ */
+ public static ChildListPropertyDescriptor getBodyDeclarationsProperty(ASTNode node) {
+ if (node instanceof AbstractTypeDeclaration) {
+ return ((AbstractTypeDeclaration)node).getBodyDeclarationsProperty();
+ } else if (node instanceof AnonymousClassDeclaration) {
+ return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY;
+ }
+ // should not happen.
+ Assert.isTrue(false);
+ return null;
+ }
+
+ public static String getTypeName(Type type) {
+ final StringBuffer buffer= new StringBuffer();
+ ASTVisitor visitor= new ASTVisitor() {
+ @Override
+ public boolean visit(PrimitiveType node) {
+ buffer.append(node.getPrimitiveTypeCode().toString());
+ return false;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ buffer.append(node.getIdentifier());
+ return false;
+ }
+ @Override
+ public boolean visit(QualifiedName node) {
+ buffer.append(node.getName().getIdentifier());
+ return false;
+ }
+ @Override
+ public void endVisit(ArrayType node) {
+ buffer.append("[]"); //$NON-NLS-1$
+ }
+ };
+ type.accept(visitor);
+ return buffer.toString();
+ }
+
+ public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) {
+ if (operator.equals(Assignment.Operator.PLUS_ASSIGN))
+ return InfixExpression.Operator.PLUS;
+
+ if (operator.equals(Assignment.Operator.MINUS_ASSIGN))
+ return InfixExpression.Operator.MINUS;
+
+ if (operator.equals(Assignment.Operator.TIMES_ASSIGN))
+ return InfixExpression.Operator.TIMES;
+
+ if (operator.equals(Assignment.Operator.DIVIDE_ASSIGN))
+ return InfixExpression.Operator.DIVIDE;
+
+ if (operator.equals(Assignment.Operator.BIT_AND_ASSIGN))
+ return InfixExpression.Operator.AND;
+
+ if (operator.equals(Assignment.Operator.BIT_OR_ASSIGN))
+ return InfixExpression.Operator.OR;
+
+ if (operator.equals(Assignment.Operator.BIT_XOR_ASSIGN))
+ return InfixExpression.Operator.XOR;
+
+ if (operator.equals(Assignment.Operator.REMAINDER_ASSIGN))
+ return InfixExpression.Operator.REMAINDER;
+
+ if (operator.equals(Assignment.Operator.LEFT_SHIFT_ASSIGN))
+ return InfixExpression.Operator.LEFT_SHIFT;
+
+ if (operator.equals(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN))
+ return InfixExpression.Operator.RIGHT_SHIFT_SIGNED;
+
+ if (operator.equals(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN))
+ return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED;
+
+ Assert.isTrue(false, "Cannot convert assignment operator"); //$NON-NLS-1$
+ return null;
+ }
+
+ /**
+ * Returns true if a node at a given location is a body of a control statement. Such body nodes are
+ * interesting as when replacing them, it has to be evaluates if a Block is needed instead.
+ * E.g. <code> if (x) do(); -> if (x) { do1(); do2() } </code>
+ *
+ * @param locationInParent Location of the body node
+ * @return Returns true if the location is a body node location of a control statement.
+ */
+ public static boolean isControlStatementBody(StructuralPropertyDescriptor locationInParent) {
+ return locationInParent == IfStatement.THEN_STATEMENT_PROPERTY
+ || locationInParent == IfStatement.ELSE_STATEMENT_PROPERTY
+ || locationInParent == ForStatement.BODY_PROPERTY
+ || locationInParent == EnhancedForStatement.BODY_PROPERTY
+ || locationInParent == WhileStatement.BODY_PROPERTY
+ || locationInParent == DoStatement.BODY_PROPERTY;
+ }
+
+ /**
+ * Returns the type to which an inlined variable initializer should be cast, or
+ * <code>null</code> if no cast is necessary.
+ *
+ * @param initializer the initializer expression of the variable to inline
+ * @param reference the reference to the variable (which is to be inlined)
+ * @return a type binding to which the initializer should be cast, or <code>null</code> iff no cast is necessary
+ * @since 3.6
+ */
+ public static ITypeBinding getExplicitCast(Expression initializer, Expression reference) {
+ ITypeBinding initializerType= initializer.resolveTypeBinding();
+ ITypeBinding referenceType= reference.resolveTypeBinding();
+ if (initializerType == null || referenceType == null)
+ return null;
+
+ if (initializerType.isPrimitive() && referenceType.isPrimitive() && ! referenceType.isEqualTo(initializerType)) {
+ return referenceType;
+
+ } else if (initializerType.isPrimitive() && ! referenceType.isPrimitive()) { // initializer is autoboxed
+ ITypeBinding unboxedReferenceType= Bindings.getUnboxedTypeBinding(referenceType, reference.getAST());
+ if (!unboxedReferenceType.isEqualTo(initializerType))
+ return unboxedReferenceType;
+ else if (needsExplicitBoxing(reference))
+ return referenceType;
+
+ } else if (! initializerType.isPrimitive() && referenceType.isPrimitive()) { // initializer is autounboxed
+ ITypeBinding unboxedInitializerType= Bindings.getUnboxedTypeBinding(initializerType, reference.getAST());
+ if (!unboxedInitializerType.isEqualTo(referenceType))
+ return referenceType;
+
+ } else if (initializerType.isRawType() && referenceType.isParameterizedType()) {
+ return referenceType; // don't lose the unchecked conversion
+
+ } else if (! TypeRules.canAssign(initializerType, referenceType)) {
+ if (!Bindings.containsTypeVariables(referenceType))
+ return referenceType;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether an expression at the given location needs explicit boxing.
+ *
+ * @param expression the expression
+ * @return <code>true</code> iff an expression at the given location needs explicit boxing
+ * @since 3.6
+ */
+ private static boolean needsExplicitBoxing(Expression expression) {
+ StructuralPropertyDescriptor locationInParent= expression.getLocationInParent();
+ if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY)
+ return needsExplicitBoxing((ParenthesizedExpression) expression.getParent());
+
+ if (locationInParent == ClassInstanceCreation.EXPRESSION_PROPERTY
+ || locationInParent == FieldAccess.EXPRESSION_PROPERTY
+ || locationInParent == MethodInvocation.EXPRESSION_PROPERTY)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Returns the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none.
+ * <p>
+ * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc.
+ * The resulting node may be in a totally different scope than the given node.
+ * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead.
+ * </p>
+ * @param node the node
+ * @param parentClass the class of the sought ancestor node
+ * @return the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none
+ */
+ public static ASTNode getParent(ASTNode node, Class<? extends ASTNode> parentClass) {
+ do {
+ node= node.getParent();
+ } while (node != null && !parentClass.isInstance(node));
+ return node;
+ }
+
+ /**
+ * Returns the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none.
+ * <p>
+ * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc.
+ * The resulting node may be in a totally different scope than the given node.
+ * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead.
+ * </p>
+ * @param node the node
+ * @param nodeType the node type constant from {@link ASTNode}
+ * @return the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none
+ */
+ public static ASTNode getParent(ASTNode node, int nodeType) {
+ do {
+ node= node.getParent();
+ } while (node != null && node.getNodeType() != nodeType);
+ return node;
+ }
+
+ public static ASTNode findParent(ASTNode node, StructuralPropertyDescriptor[][] pathes) {
+ for (int p= 0; p < pathes.length; p++) {
+ StructuralPropertyDescriptor[] path= pathes[p];
+ ASTNode current= node;
+ int d= path.length - 1;
+ for (; d >= 0 && current != null; d--) {
+ StructuralPropertyDescriptor descriptor= path[d];
+ if (!descriptor.equals(current.getLocationInParent()))
+ break;
+ current= current.getParent();
+ }
+ if (d < 0)
+ return current;
+ }
+ return null;
+ }
+
+ public static ASTNode getNormalizedNode(ASTNode node) {
+ ASTNode current= node;
+ // normalize name
+ if (QualifiedName.NAME_PROPERTY.equals(current.getLocationInParent())) {
+ current= current.getParent();
+ }
+ // normalize type
+ if (QualifiedType.NAME_PROPERTY.equals(current.getLocationInParent()) ||
+ SimpleType.NAME_PROPERTY.equals(current.getLocationInParent())) {
+ current= current.getParent();
+ }
+ // normalize parameterized types
+ if (ParameterizedType.TYPE_PROPERTY.equals(current.getLocationInParent())) {
+ current= current.getParent();
+ }
+ return current;
+ }
+
+ /**
+ * Returns <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
+ * (i.e. returns <code>false</code> if <code>parent == node</code>).
+ *
+ * @param node node to test
+ * @param parent assumed parent
+ * @return <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
+ */
+ public static boolean isParent(ASTNode node, ASTNode parent) {
+ Assert.isNotNull(parent);
+ do {
+ node= node.getParent();
+ if (node == parent)
+ return true;
+ } while (node != null);
+ return false;
+ }
+
+ public static int getExclusiveEnd(ASTNode node){
+ return node.getStartPosition() + node.getLength();
+ }
+
+ public static int getInclusiveEnd(ASTNode node){
+ return node.getStartPosition() + node.getLength() - 1;
+ }
+
+ public static IMethodBinding getMethodBinding(Name node) {
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IMethodBinding)
+ return (IMethodBinding)binding;
+ return null;
+ }
+
+ public static IVariableBinding getVariableBinding(Name node) {
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IVariableBinding)
+ return (IVariableBinding)binding;
+ return null;
+ }
+
+ public static IVariableBinding getLocalVariableBinding(Name node) {
+ IVariableBinding result= getVariableBinding(node);
+ if (result == null || result.isField())
+ return null;
+
+ return result;
+ }
+
+ public static IVariableBinding getFieldBinding(Name node) {
+ IVariableBinding result= getVariableBinding(node);
+ if (result == null || !result.isField())
+ return null;
+
+ return result;
+ }
+
+ public static ITypeBinding getTypeBinding(Name node) {
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof ITypeBinding)
+ return (ITypeBinding)binding;
+ return null;
+ }
+
+ /**
+ * Returns the receiver's type binding of the given method invocation.
+ *
+ * @param invocation method invocation to resolve type of
+ * @return the type binding of the receiver
+ */
+ public static ITypeBinding getReceiverTypeBinding(MethodInvocation invocation) {
+ ITypeBinding result= null;
+ Expression exp= invocation.getExpression();
+ if(exp != null) {
+ return exp.resolveTypeBinding();
+ }
+ else {
+ AbstractTypeDeclaration type= (AbstractTypeDeclaration)getParent(invocation, AbstractTypeDeclaration.class);
+ if (type != null)
+ return type.resolveBinding();
+ }
+ return result;
+ }
+
+ public static ITypeBinding getEnclosingType(ASTNode node) {
+ while(node != null) {
+ if (node instanceof AbstractTypeDeclaration) {
+ return ((AbstractTypeDeclaration)node).resolveBinding();
+ } else if (node instanceof AnonymousClassDeclaration) {
+ return ((AnonymousClassDeclaration)node).resolveBinding();
+ }
+ node= node.getParent();
+ }
+ return null;
+ }
+
+ public static IProblem[] getProblems(ASTNode node, int scope, int severity) {
+ ASTNode root= node.getRoot();
+ if (!(root instanceof CompilationUnit))
+ return EMPTY_PROBLEMS;
+ IProblem[] problems= ((CompilationUnit)root).getProblems();
+ if (root == node)
+ return problems;
+ final int iterations= computeIterations(scope);
+ List<IProblem> result= new ArrayList<IProblem>(5);
+ for (int i= 0; i < problems.length; i++) {
+ IProblem problem= problems[i];
+ boolean consider= false;
+ if ((severity & PROBLEMS) == PROBLEMS)
+ consider= true;
+ else if ((severity & WARNING) != 0)
+ consider= problem.isWarning();
+ else if ((severity & ERROR) != 0)
+ consider= problem.isError();
+ if (consider) {
+ ASTNode temp= node;
+ int count= iterations;
+ do {
+ int nodeOffset= temp.getStartPosition();
+ int problemOffset= problem.getSourceStart();
+ if (nodeOffset <= problemOffset && problemOffset < nodeOffset + temp.getLength()) {
+ result.add(problem);
+ count= 0;
+ } else {
+ count--;
+ }
+ } while ((temp= temp.getParent()) != null && count > 0);
+ }
+ }
+ return result.toArray(new IProblem[result.size()]);
+ }
+
+ public static Message[] getMessages(ASTNode node, int flags) {
+ ASTNode root= node.getRoot();
+ if (!(root instanceof CompilationUnit))
+ return EMPTY_MESSAGES;
+ Message[] messages= ((CompilationUnit)root).getMessages();
+ if (root == node)
+ return messages;
+ final int iterations= computeIterations(flags);
+ List<Message> result= new ArrayList<Message>(5);
+ for (int i= 0; i < messages.length; i++) {
+ Message message= messages[i];
+ ASTNode temp= node;
+ int count= iterations;
+ do {
+ int nodeOffset= temp.getStartPosition();
+ int messageOffset= message.getStartPosition();
+ if (nodeOffset <= messageOffset && messageOffset < nodeOffset + temp.getLength()) {
+ result.add(message);
+ count= 0;
+ } else {
+ count--;
+ }
+ } while ((temp= temp.getParent()) != null && count > 0);
+ }
+ return result.toArray(new Message[result.size()]);
+ }
+
+ private static int computeIterations(int flags) {
+ switch (flags) {
+ case NODE_ONLY:
+ return 1;
+ case INCLUDE_ALL_PARENTS:
+ return Integer.MAX_VALUE;
+ case INCLUDE_FIRST_PARENT:
+ return 2;
+ default:
+ return 1;
+ }
+ }
+
+
+ private static int getOrderPreference(BodyDeclaration member, MembersOrderPreferenceCache store) {
+ int memberType= member.getNodeType();
+ int modifiers= member.getModifiers();
+
+ switch (memberType) {
+ case ASTNode.TYPE_DECLARATION:
+ case ASTNode.ENUM_DECLARATION :
+ case ASTNode.ANNOTATION_TYPE_DECLARATION :
+ return store.getCategoryIndex(MembersOrderPreferenceCache.TYPE_INDEX) * 2;
+ case ASTNode.FIELD_DECLARATION:
+ if (Modifier.isStatic(modifiers)) {
+ int index= store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_FIELDS_INDEX) * 2;
+ if (Modifier.isFinal(modifiers)) {
+ return index; // first final static, then static
+ }
+ return index + 1;
+ }
+ return store.getCategoryIndex(MembersOrderPreferenceCache.FIELDS_INDEX) * 2;
+ case ASTNode.INITIALIZER:
+ if (Modifier.isStatic(modifiers)) {
+ return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_INIT_INDEX) * 2;
+ }
+ return store.getCategoryIndex(MembersOrderPreferenceCache.INIT_INDEX) * 2;
+ case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
+ return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2;
+ case ASTNode.METHOD_DECLARATION:
+ if (Modifier.isStatic(modifiers)) {
+ return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_METHODS_INDEX) * 2;
+ }
+ if (((MethodDeclaration) member).isConstructor()) {
+ return store.getCategoryIndex(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX) * 2;
+ }
+ return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2;
+ default:
+ return 100;
+ }
+ }
+
+ /**
+ * Computes the insertion index to be used to add the given member to the
+ * the list <code>container</code>.
+ * @param member the member to add
+ * @param container a list containing objects of type <code>BodyDeclaration</code>
+ * @return the insertion index to be used
+ */
+ public static int getInsertionIndex(BodyDeclaration member, List<? extends BodyDeclaration> container) {
+ int containerSize= container.size();
+
+ MembersOrderPreferenceCache orderStore= JavaPlugin.getDefault().getMemberOrderPreferenceCache();
+
+ int orderIndex= getOrderPreference(member, orderStore);
+
+ int insertPos= containerSize;
+ int insertPosOrderIndex= -1;
+
+ for (int i= containerSize - 1; i >= 0; i--) {
+ int currOrderIndex= getOrderPreference(container.get(i), orderStore);
+ if (orderIndex == currOrderIndex) {
+ if (insertPosOrderIndex != orderIndex) { // no perfect match yet
+ insertPos= i + 1; // after a same kind
+ insertPosOrderIndex= orderIndex; // perfect match
+ }
+ } else if (insertPosOrderIndex != orderIndex) { // not yet a perfect match
+ if (currOrderIndex < orderIndex) { // we are bigger
+ if (insertPosOrderIndex == -1) {
+ insertPos= i + 1; // after
+ insertPosOrderIndex= currOrderIndex;
+ }
+ } else {
+ insertPos= i; // before
+ insertPosOrderIndex= currOrderIndex;
+ }
+ }
+ }
+ return insertPos;
+ }
+
+ public static SimpleName getLeftMostSimpleName(Name name) {
+ if (name instanceof SimpleName) {
+ return (SimpleName)name;
+ } else {
+ final SimpleName[] result= new SimpleName[1];
+ ASTVisitor visitor= new ASTVisitor() {
+ @Override
+ public boolean visit(QualifiedName qualifiedName) {
+ Name left= qualifiedName.getQualifier();
+ if (left instanceof SimpleName)
+ result[0]= (SimpleName)left;
+ else
+ left.accept(this);
+ return false;
+ }
+ };
+ name.accept(visitor);
+ return result[0];
+ }
+ }
+
+ public static SimpleType getLeftMostSimpleType(QualifiedType type) {
+ final SimpleType[] result= new SimpleType[1];
+ ASTVisitor visitor= new ASTVisitor() {
+ @Override
+ public boolean visit(QualifiedType qualifiedType) {
+ Type left= qualifiedType.getQualifier();
+ if (left instanceof SimpleType)
+ result[0]= (SimpleType)left;
+ else
+ left.accept(this);
+ return false;
+ }
+ };
+ type.accept(visitor);
+ return result[0];
+ }
+
+ public static Name getTopMostName(Name name) {
+ Name result= name;
+ while(result.getParent() instanceof Name) {
+ result= (Name)result.getParent();
+ }
+ return result;
+ }
+
+ public static Type getTopMostType(Type type) {
+ Type result= type;
+ while(result.getParent() instanceof Type) {
+ result= (Type)result.getParent();
+ }
+ return result;
+ }
+
+ public static int changeVisibility(int modifiers, int visibility) {
+ return (modifiers & CLEAR_VISIBILITY) | visibility;
+ }
+
+ /**
+ * Adds flags to the given node and all its descendants.
+ * @param root The root node
+ * @param flags The flags to set
+ */
+ public static void setFlagsToAST(ASTNode root, final int flags) {
+ root.accept(new GenericVisitor(true) {
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ node.setFlags(node.getFlags() | flags);
+ return true;
+ }
+ });
+ }
+
+ public static String getQualifier(Name name) {
+ if (name.isQualifiedName()) {
+ return ((QualifiedName) name).getQualifier().getFullyQualifiedName();
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ public static String getSimpleNameIdentifier(Name name) {
+ if (name.isQualifiedName()) {
+ return ((QualifiedName) name).getName().getIdentifier();
+ } else {
+ return ((SimpleName) name).getIdentifier();
+ }
+ }
+
+ public static boolean isDeclaration(Name name) {
+ if (name.isQualifiedName()) {
+ return ((QualifiedName) name).getName().isDeclaration();
+ } else {
+ return ((SimpleName) name).isDeclaration();
+ }
+ }
+
+ public static Modifier findModifierNode(int flag, List<IExtendedModifier> modifiers) {
+ for (int i= 0; i < modifiers.size(); i++) {
+ Object curr= modifiers.get(i);
+ if (curr instanceof Modifier && ((Modifier) curr).getKeyword().toFlagValue() == flag) {
+ return (Modifier) curr;
+ }
+ }
+ return null;
+ }
+
+ public static ITypeBinding getTypeBinding(CompilationUnit root, IType type) throws JavaModelException {
+ if (type.isAnonymous()) {
+ final IJavaElement parent= type.getParent();
+ if (parent instanceof IField && Flags.isEnum(((IMember) parent).getFlags())) {
+ final EnumConstantDeclaration constant= (EnumConstantDeclaration) NodeFinder.perform(root, ((ISourceReference) parent).getSourceRange());
+ if (constant != null) {
+ final AnonymousClassDeclaration declaration= constant.getAnonymousClassDeclaration();
+ if (declaration != null)
+ return declaration.resolveBinding();
+ }
+ } else {
+ final ClassInstanceCreation creation= (ClassInstanceCreation) getParent(NodeFinder.perform(root, type.getNameRange()), ClassInstanceCreation.class);
+ if (creation != null)
+ return creation.resolveTypeBinding();
+ }
+ } else {
+ final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getParent(NodeFinder.perform(root, type.getNameRange()), AbstractTypeDeclaration.class);
+ if (declaration != null)
+ return declaration.resolveBinding();
+ }
+ return null;
+ }
+
+ /**
+ * Escapes a string value to a literal that can be used in Java source.
+ *
+ * @param stringValue the string value
+ * @return the escaped string
+ * @see StringLiteral#getEscapedValue()
+ */
+ public static String getEscapedStringLiteral(String stringValue) {
+ StringLiteral stringLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newStringLiteral();
+ stringLiteral.setLiteralValue(stringValue);
+ return stringLiteral.getEscapedValue();
+ }
+
+ /**
+ * Escapes a character value to a literal that can be used in Java source.
+ *
+ * @param ch the character value
+ * @return the escaped string
+ * @see CharacterLiteral#getEscapedValue()
+ */
+ public static String getEscapedCharacterLiteral(char ch) {
+ CharacterLiteral characterLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newCharacterLiteral();
+ characterLiteral.setCharValue(ch);
+ return characterLiteral.getEscapedValue();
+ }
+
+}