Copyright (c) 2000, 2019 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 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). Stephan Herrmann - Configuration for Bug 463360 - [override method][null] generating method override should not create redundant null annotations
/******************************************************************************* * Copyright (c) 2000, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * 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). * Stephan Herrmann - Configuration for * Bug 463360 - [override method][null] generating method override should not create redundant null annotations *******************************************************************************/
package org.eclipse.jdt.internal.corext.dom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; 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.ICompilationUnit; 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.compiler.ITerminalSymbols; 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.Annotation; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.BooleanLiteral; import org.eclipse.jdt.core.dom.BreakStatement; import org.eclipse.jdt.core.dom.CastExpression; 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.ConditionalExpression; import org.eclipse.jdt.core.dom.ConstructorInvocation; 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.ExpressionStatement; 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.IfStatement; 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.InfixExpression; import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.MemberValuePair; 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.MethodReference; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NameQualifiedType; 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.PrefixExpression; 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.ReturnStatement; 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.Statement; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.UnionType; 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.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.formatter.IndentManipulation; import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; import org.eclipse.jdt.internal.core.manipulation.util.Strings; import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
JDT-UI-internal helper methods that deal with ASTNodes:
  • additional operations on ASTNodes and subtypes
  • finding related nodes in an AST
  • some methods that deal with bindings (new such methods should go into Bindings)
/** * JDT-UI-internal helper methods that deal with {@link ASTNode}s: * <ul> * <li>additional operations on {@link ASTNode}s and subtypes</li> * <li>finding related nodes in an AST</li> * <li>some methods that deal with bindings (new such methods should go into {@link Bindings})</li> * </ul> */
// @see JDTUIHelperClasses 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 INFO= 1 << 2; public static final int PROBLEMS= WARNING | ERROR | INFO; 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) { // bug in the formatter JavaManipulationPlugin.log(e); } return document.get(); } return unformatted; // unknown node }
Returns the source of the given node from the location where it was parsed.
Params:
  • node – the node to get the source from
  • extendedRange – if set, the extended ranges of the nodes should ne used
  • removeIndent – if set, the indentation is removed.
Returns:return the source for the given node or null if accessing the source failed.
/** * 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= getIndentUsed(buffer, node.getStartPosition(), project); str= Strings.changeIndent(str, indent, project, new String(), typeRoot.findRecommendedLineSeparator()); } return str; } } catch (JavaModelException e) { // ignore } } return null; } private static int getIndentUsed(IBuffer buffer, int offset, IJavaProject project) { int i= offset; // find beginning of line while (i > 0 && !IndentManipulation.isLineDelimiterChar(buffer.getChar(i - 1))) { i--; } return Strings.computeIndentUnits(buffer.getText(i, offset - i), project); }
Returns the list that contains the given ASTNode. If the node isn't part of any list, null is returned.
Params:
  • node – the node in question
Returns:the list that contains the node or 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 getChildListProperty(node.getParent(), (ChildListPropertyDescriptor) locationInParent); } return null; }
Variant of ASTNode.getStructuralProperty(StructuralPropertyDescriptor) that avoids unchecked casts in the caller.

To improve type-safety, callers can add the expected element type as explicit type argument, e.g.:

ASTNodes.<BodyDeclaration>getChildListProperty(typeDecl, bodyDeclarationsProperty)

Params:
  • node – the node
  • propertyDescriptor – the child list property to get
Throws:
Returns:the child list
/** * Variant of {@link ASTNode#getStructuralProperty(StructuralPropertyDescriptor)} that avoids * unchecked casts in the caller. * <p> * To improve type-safety, callers can add the expected element type as explicit type argument, e.g.: * <p> * {@code ASTNodes.<BodyDeclaration>getChildListProperty(typeDecl, bodyDeclarationsProperty)} * * @param node the node * @param propertyDescriptor the child list property to get * @return the child list * @exception RuntimeException if this node does not have the given property */
@SuppressWarnings("unchecked") public static <T extends ASTNode> List<T> getChildListProperty(ASTNode node, ChildListPropertyDescriptor propertyDescriptor) { return (List<T>) node.getStructuralProperty(propertyDescriptor); }
Returns a list of the direct children of a node. The siblings are ordered by start offset.
Params:
  • node – the node to get the children for
Returns:the children
/** * 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<>(); 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.
Params:
  • node – the node to be tested.
Returns:true if this is an existing node, false if not.
/** * 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.
Params:
  • type – The type to get the element type from.
Returns:The element type of the type or the type itself.
/** * 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.
Params:
  • declaration – the declaration
Returns:the type node or null if the given declaration represents a type inferred parameter in lambda expression
/** * Returns the type node for the given declaration. * * @param declaration the declaration * @return the type node or <code>null</code> if the given declaration represents a type * inferred parameter in lambda expression */
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(); else if (parent instanceof LambdaExpression) return null; } Assert.isTrue(false, "Unknown VariableDeclaration"); //$NON-NLS-1$ return null; } public static int getDimensions(VariableDeclaration declaration) { int dim= declaration.getExtraDimensions(); if (declaration instanceof VariableDeclarationFragment && declaration.getParent() instanceof LambdaExpression) { LambdaExpression lambda= (LambdaExpression) declaration.getParent(); IMethodBinding methodBinding= lambda.resolveMethodBinding(); if (methodBinding != null) { ITypeBinding[] parameterTypes= methodBinding.getParameterTypes(); int index= lambda.parameters().indexOf(declaration); ITypeBinding typeBinding= parameterTypes[index]; return typeBinding.getDimensions(); } } else { 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<>(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 || type == ASTNode.TEXT_BLOCK; } public static boolean isLabel(SimpleName name) { int parentType= name.getParent().getNodeType(); return parentType == ASTNode.LABELED_STATEMENT || (parentType == ASTNode.BREAK_STATEMENT && name.getLocationInParent() == BreakStatement.LABEL_PROPERTY) || 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: BodyDeclaration).
Params:
Returns:the property descriptor
/** * 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; }
Returns the simple name of the type, followed by array dimensions. Skips qualifiers, type arguments, and type annotations.

Does not work for WildcardTypes, etc.!

Params:
  • type – a type that has a simple name
See Also:
Returns:the simple name, followed by array dimensions
Since:3.10
/** * Returns the simple name of the type, followed by array dimensions. * Skips qualifiers, type arguments, and type annotations. * <p> * Does <b>not</b> work for WildcardTypes, etc.! * * @param type a type that has a simple name * @return the simple name, followed by array dimensions * @see #getSimpleNameIdentifier(Name) * @since 3.10 */
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(SimpleType node) { buffer.append(getSimpleNameIdentifier(node.getName())); return false; } @Override public boolean visit(QualifiedType node) { buffer.append(node.getName().getIdentifier()); return false; } @Override public boolean visit(NameQualifiedType node) { buffer.append(node.getName().getIdentifier()); return false; } @Override public boolean visit(ParameterizedType node) { node.getType().accept(this); return false; } @Override public void endVisit(ArrayType node) { for (int i= 0; i < node.dimensions().size(); i++) { buffer.append("[]"); //$NON-NLS-1$ } } }; type.accept(visitor); return buffer.toString(); }
Returns the (potentially qualified) name of a type, followed by array dimensions. Skips type arguments and type annotations.
Params:
  • type – a type that has a name
Returns:the name, followed by array dimensions
Since:3.10
/** * Returns the (potentially qualified) name of a type, followed by array dimensions. * Skips type arguments and type annotations. * * @param type a type that has a name * @return the name, followed by array dimensions * @since 3.10 */
public static String getQualifiedTypeName(Type type) { final StringBuffer buffer= new StringBuffer(); ASTVisitor visitor= new ASTVisitor() { @Override public boolean visit(SimpleType node) { buffer.append(node.getName().getFullyQualifiedName()); return false; } @Override public boolean visit(QualifiedType node) { node.getQualifier().accept(this); buffer.append('.'); buffer.append(node.getName().getIdentifier()); return false; } @Override public boolean visit(NameQualifiedType node) { buffer.append(node.getQualifier().getFullyQualifiedName()); buffer.append('.'); buffer.append(node.getName().getIdentifier()); return false; } @Override public boolean visit(ParameterizedType node) { node.getType().accept(this); return false; } @Override public void endVisit(ArrayType node) { for (int i= 0; i < node.dimensions().size(); i++) { buffer.append("[]"); //$NON-NLS-1$ } } }; type.accept(visitor); return buffer.toString(); }
Returns the Boolean object value represented by the provided expression.
Params:
  • expression – the expression to analyze
Returns:the Boolean object value if the provided expression represents one, null otherwise
/** * Returns the {@link Boolean} object value represented by the provided expression. * * @param expression the expression to analyze * @return the {@link Boolean} object value if the provided expression represents one, null * otherwise */
public static Boolean getBooleanLiteral(Expression expression) { final BooleanLiteral bl= as(expression, BooleanLiteral.class); if (bl != null) { return Boolean.valueOf(bl.booleanValue()); } final QualifiedName qn= as(expression, QualifiedName.class); if (hasType(qn, Boolean.class.getCanonicalName())) { return getBooleanObject(qn); } return null; }
Casts the provided expression to an object of the provided type if type matches.
Params:
  • expression – the expression to cast
  • exprClass – the class representing the required expression type
Type parameters:
  • <T> – the required expression type
Returns:the provided expression as an object of the provided type if type matches, null otherwise
/** * Casts the provided expression to an object of the provided type if type matches. * * @param <T> the required expression type * @param expression the expression to cast * @param exprClass the class representing the required expression type * @return the provided expression as an object of the provided type if type matches, null * otherwise */
@SuppressWarnings("unchecked") public static <T extends Expression> T as(Expression expression, Class<T> exprClass) { if (expression != null) { if (exprClass.isAssignableFrom(expression.getClass())) { return (T) expression; } else if (expression instanceof ParenthesizedExpression) { return as(((ParenthesizedExpression) expression).getExpression(), exprClass); } } return null; }
Returns the provided statement as a non null list of statements:
  • if the statement is null, then an empty list is returned
  • if the statement is a Block, then its children are returned
  • otherwise, the current node is returned wrapped in a list
Params:
  • statement – the statement to analyze
Returns:the provided statement as a non null list of statements
/** * Returns the provided statement as a non null list of statements: * <ul> * <li>if the statement is null, then an empty list is returned</li> * <li>if the statement is a {@link Block}, then its children are returned</li> * <li>otherwise, the current node is returned wrapped in a list</li> * </ul> * * @param statement the statement to analyze * @return the provided statement as a non null list of statements */
public static List<Statement> asList(Statement statement) { if (statement == null) { return Collections.emptyList(); } if (statement instanceof Block) { return ((Block) statement).statements(); } return Arrays.asList(statement); }
Returns all the operands from the provided infix expressions.
Params:
  • node – the infix expression
Returns:a List of expressions
/** * Returns all the operands from the provided infix expressions. * * @param node the infix expression * @return a List of expressions */
public static List<Expression> allOperands(InfixExpression node) { final List<Expression> extOps= node.extendedOperands(); final List<Expression> results= new ArrayList<>(2 + extOps.size()); results.add(node.getLeftOperand()); results.add(node.getRightOperand()); results.addAll(extOps); return results; }
Returns the Boolean object constant value represented by the provided qualified name.
Params:
  • qualifiedName – the qualified name that must represent a Boolean object constant
Returns:the Boolean object constant value represented by the provided qualified name, or null if the qualified name does not represent a Boolean object constant value.
/** * Returns the {@link Boolean} object constant value represented by the provided qualified name. * * @param qualifiedName the qualified name that must represent a Boolean object constant * @return the {@link Boolean} object constant value represented by the provided qualified name, * or null if the qualified name does not represent a {@link Boolean} object constant * value. */
public static Boolean getBooleanObject(final QualifiedName qualifiedName) { final String fqn= qualifiedName.getFullyQualifiedName(); if ("Boolean.TRUE".equals(fqn)) { //$NON-NLS-1$ return Boolean.TRUE; } else if ("Boolean.FALSE".equals(fqn)) { //$NON-NLS-1$ return Boolean.FALSE; } return null; }
Returns whether the provided operator is the same as the one of provided node.
Params:
  • node – the node for which to test the operator
  • anOperator – the first operator to test
  • operators – the other operators to test
Returns:true if the provided node has the provided operator, false otherwise.
/** * Returns whether the provided operator is the same as the one of provided node. * * @param node the node for which to test the operator * @param anOperator the first operator to test * @param operators the other operators to test * @return true if the provided node has the provided operator, false otherwise. */
public static boolean hasOperator(InfixExpression node, InfixExpression.Operator anOperator, InfixExpression.Operator... operators) { return node != null && isOperatorInList(node.getOperator(), anOperator, operators); }
Returns whether the provided operator is the same as the one of provided node.
Params:
  • node – the node for which to test the operator
  • anOperator – the first operator to test
  • operators – the other operators to test
Returns:true if the provided node has the provided operator, false otherwise.
/** * Returns whether the provided operator is the same as the one of provided node. * * @param node the node for which to test the operator * @param anOperator the first operator to test * @param operators the other operators to test * @return true if the provided node has the provided operator, false otherwise. */
public static boolean hasOperator(PrefixExpression node, PrefixExpression.Operator anOperator, PrefixExpression.Operator... operators) { return node != null && isOperatorInList(node.getOperator(), anOperator, operators); } private static boolean isOperatorInList(Object realOperator, Object anOperator, Object[] operators) { return realOperator != null && (realOperator.equals(anOperator) || Arrays.asList(operators).contains(realOperator)); }
Returns whether the provided expression evaluates to exactly one of the provided type.
Params:
  • expression – the expression to analyze
  • oneOfQualifiedTypeNames – the type binding qualified name must be equal to one of these qualified type names
Returns:true if the provided expression evaluates to exactly one of the provided type, false otherwise
/** * Returns whether the provided expression evaluates to exactly one of the provided type. * * @param expression the expression to analyze * @param oneOfQualifiedTypeNames the type binding qualified name must be equal to one of these * qualified type names * @return true if the provided expression evaluates to exactly one of the provided type, false * otherwise */
public static boolean hasType(Expression expression, String... oneOfQualifiedTypeNames) { return expression != null && hasType(expression.resolveTypeBinding(), oneOfQualifiedTypeNames); }
Returns whether the provided type binding is exactly one of the provided type.
Params:
  • typeBinding – the type binding to analyze
  • oneOfQualifiedTypeNames – the type binding qualified name must be equal to one of these qualified type names
Returns:true if the provided type binding is exactly one of the provided type, false otherwise
/** * Returns whether the provided type binding is exactly one of the provided type. * * @param typeBinding the type binding to analyze * @param oneOfQualifiedTypeNames the type binding qualified name must be equal to one of these * qualified type names * @return {@code true} if the provided type binding is exactly one of the provided type, * {@code false} otherwise */
public static boolean hasType(final ITypeBinding typeBinding, String... oneOfQualifiedTypeNames) { if (typeBinding != null) { final String qualifiedName= typeBinding.getErasure().getQualifiedName(); for (String qualifiedTypeName : oneOfQualifiedTypeNames) { if (qualifiedTypeName.equals(qualifiedName)) { return true; } } } return false; }
Returns the opposite infix operator. For boolean operators, the operands should be negated too.
Params:
  • operator – the infix operator
Returns:the opposite infix operator
/** * Returns the opposite infix operator. For boolean operators, the operands should be negated * too. * * @param operator the infix operator * @return the opposite infix operator */
public static InfixExpression.Operator oppositeInfixOperator(InfixExpression.Operator operator) { if (InfixExpression.Operator.LESS.equals(operator)) return InfixExpression.Operator.GREATER_EQUALS; if (InfixExpression.Operator.LESS_EQUALS.equals(operator)) return InfixExpression.Operator.GREATER; if (InfixExpression.Operator.GREATER.equals(operator)) return InfixExpression.Operator.LESS_EQUALS; if (InfixExpression.Operator.GREATER_EQUALS.equals(operator)) return InfixExpression.Operator.LESS; if (InfixExpression.Operator.EQUALS.equals(operator)) return InfixExpression.Operator.NOT_EQUALS; if (InfixExpression.Operator.NOT_EQUALS.equals(operator)) return InfixExpression.Operator.EQUALS; if (InfixExpression.Operator.CONDITIONAL_AND.equals(operator)) return InfixExpression.Operator.CONDITIONAL_OR; if (InfixExpression.Operator.CONDITIONAL_OR.equals(operator)) return InfixExpression.Operator.CONDITIONAL_AND; return null; } 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. if (x) do(); -> if (x) { do1(); do2() }
Params:
  • locationInParent – Location of the body node
Returns:Returns true if the location is a body node location of a control statement.
/** * 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 null if no cast is necessary.
Params:
  • initializer – the initializer expression of the variable to inline
  • reference – the reference to the variable (which is to be inlined)
Returns:a type binding to which the initializer should be cast, or null iff no cast is necessary
Since:3.6
/** * 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 (initializer instanceof LambdaExpression || initializer instanceof MethodReference) { if (isTargetAmbiguous(reference, isExplicitlyTypedLambda(initializer))) { return referenceType; } else { ITypeBinding targetType= getTargetType(reference); if (targetType == null || targetType != referenceType) { return referenceType; } } } else if (! TypeRules.canAssign(initializerType, referenceType)) { if (!Bindings.containsTypeVariables(referenceType)) return referenceType; } return null; }
Checks whether overloaded methods can result in an ambiguous method call or a semantic change when the expression argument is replaced with a poly expression form of the functional interface instance.
Params:
  • expression – the method argument, which is a functional interface instance
  • expressionIsExplicitlyTyped – true iff the intended replacement for expression is an explicitly typed lambda expression (JLS8 15.27.1)
Returns:true if overloaded methods can result in an ambiguous method call or a semantic change, false otherwise
Since:3.10
/** * Checks whether overloaded methods can result in an ambiguous method call or a semantic change when the * <code>expression</code> argument is replaced with a poly expression form of the functional * interface instance. * * @param expression the method argument, which is a functional interface instance * @param expressionIsExplicitlyTyped <code>true</code> iff the intended replacement for <code>expression</code> * is an explicitly typed lambda expression (JLS8 15.27.1) * @return <code>true</code> if overloaded methods can result in an ambiguous method call or a semantic change, * <code>false</code> otherwise * * @since 3.10 */
public static boolean isTargetAmbiguous(Expression expression, boolean expressionIsExplicitlyTyped) { StructuralPropertyDescriptor locationInParent= expression.getLocationInParent(); while (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY || locationInParent == ConditionalExpression.THEN_EXPRESSION_PROPERTY || locationInParent == ConditionalExpression.ELSE_EXPRESSION_PROPERTY) { expression= (Expression) expression.getParent(); locationInParent= expression.getLocationInParent(); } ASTNode parent= expression.getParent(); IMethodBinding methodBinding; int argumentIndex; int argumentCount; Expression invocationQualifier= null; if (locationInParent == MethodInvocation.ARGUMENTS_PROPERTY) { MethodInvocation methodInvocation= (MethodInvocation) parent; methodBinding= methodInvocation.resolveMethodBinding(); argumentIndex= methodInvocation.arguments().indexOf(expression); argumentCount= methodInvocation.arguments().size(); invocationQualifier= methodInvocation.getExpression(); } else if (locationInParent == SuperMethodInvocation.ARGUMENTS_PROPERTY) { SuperMethodInvocation superMethodInvocation= (SuperMethodInvocation) parent; methodBinding= superMethodInvocation.resolveMethodBinding(); argumentIndex= superMethodInvocation.arguments().indexOf(expression); argumentCount= superMethodInvocation.arguments().size(); invocationQualifier= superMethodInvocation.getQualifier(); } else if (locationInParent == ConstructorInvocation.ARGUMENTS_PROPERTY) { ConstructorInvocation constructorInvocation= (ConstructorInvocation) parent; methodBinding= constructorInvocation.resolveConstructorBinding(); argumentIndex= constructorInvocation.arguments().indexOf(expression); argumentCount= constructorInvocation.arguments().size(); } else if (locationInParent == SuperConstructorInvocation.ARGUMENTS_PROPERTY) { SuperConstructorInvocation superConstructorInvocation= (SuperConstructorInvocation) parent; methodBinding= superConstructorInvocation.resolveConstructorBinding(); argumentIndex= superConstructorInvocation.arguments().indexOf(expression); argumentCount= superConstructorInvocation.arguments().size(); } else if (locationInParent == ClassInstanceCreation.ARGUMENTS_PROPERTY) { ClassInstanceCreation creation= (ClassInstanceCreation) parent; methodBinding= creation.resolveConstructorBinding(); argumentIndex= creation.arguments().indexOf(expression); argumentCount= creation.arguments().size(); } else if (locationInParent == EnumConstantDeclaration.ARGUMENTS_PROPERTY) { EnumConstantDeclaration enumConstantDecl= (EnumConstantDeclaration) parent; methodBinding= enumConstantDecl.resolveConstructorBinding(); argumentIndex= enumConstantDecl.arguments().indexOf(expression); argumentCount= enumConstantDecl.arguments().size(); } else { return false; } if (methodBinding != null) { ITypeBinding invocationTargetType; invocationTargetType= getInvocationType(parent, methodBinding, invocationQualifier); if (invocationTargetType != null) { TypeBindingVisitor visitor= new AmbiguousTargetMethodAnalyzer(invocationTargetType, methodBinding, argumentIndex, argumentCount, expressionIsExplicitlyTyped); return !(visitor.visit(invocationTargetType) && Bindings.visitHierarchy(invocationTargetType, visitor)); } } return true; }
Returns the binding of the type which declares the method being invoked.
Params:
  • invocationNode – the method invocation node
  • methodBinding – binding of the method being invoked
  • invocationQualifier – the qualifier used for method invocation, or null if none
Returns:the binding of the type which declares the method being invoked, or null if the type cannot be resolved
/** * Returns the binding of the type which declares the method being invoked. * * @param invocationNode the method invocation node * @param methodBinding binding of the method being invoked * @param invocationQualifier the qualifier used for method invocation, or <code>null</code> if * none * @return the binding of the type which declares the method being invoked, or <code>null</code> * if the type cannot be resolved */
public static ITypeBinding getInvocationType(ASTNode invocationNode, IMethodBinding methodBinding, Expression invocationQualifier) { ITypeBinding invocationType; if (invocationNode instanceof MethodInvocation || invocationNode instanceof SuperMethodInvocation) { if (invocationQualifier != null) { invocationType= invocationQualifier.resolveTypeBinding(); if (invocationType != null && invocationNode instanceof SuperMethodInvocation) { invocationType= invocationType.getSuperclass(); } } else { ITypeBinding enclosingType= getEnclosingType(invocationNode); if (enclosingType != null && invocationNode instanceof SuperMethodInvocation) { enclosingType= enclosingType.getSuperclass(); } if (enclosingType != null) { IMethodBinding methodInHierarchy= Bindings.findMethodInHierarchy(enclosingType, methodBinding.getName(), methodBinding.getParameterTypes()); if (methodInHierarchy != null) { invocationType= enclosingType; } else { invocationType= methodBinding.getDeclaringClass(); } } else { // not expected invocationType= methodBinding.getDeclaringClass(); } } } else { invocationType= methodBinding.getDeclaringClass(); } return invocationType; } private static class AmbiguousTargetMethodAnalyzer implements TypeBindingVisitor { private ITypeBinding fDeclaringType; private IMethodBinding fOriginalMethod; private int fArgIndex; private int fArgumentCount; private boolean fExpressionIsExplicitlyTyped;
Params:
  • declaringType – the type binding declaring the originalMethod
  • originalMethod – the method declaration binding corresponding to the method call
  • argumentIndex – the index of the functional interface instance argument in the method call
  • argumentCount – the number of arguments in the method call
  • expressionIsExplicitlyTyped – true iff the intended replacement for expression is an explicitly typed lambda expression (JLS8 15.27.1)
/** * @param declaringType the type binding declaring the <code>originalMethod</code> * @param originalMethod the method declaration binding corresponding to the method call * @param argumentIndex the index of the functional interface instance argument in the * method call * @param argumentCount the number of arguments in the method call * @param expressionIsExplicitlyTyped <code>true</code> iff the intended replacement for <code>expression</code> * is an explicitly typed lambda expression (JLS8 15.27.1) */
public AmbiguousTargetMethodAnalyzer(ITypeBinding declaringType, IMethodBinding originalMethod, int argumentIndex, int argumentCount, boolean expressionIsExplicitlyTyped) { fDeclaringType= declaringType; fOriginalMethod= originalMethod; fArgIndex= argumentIndex; fArgumentCount= argumentCount; fExpressionIsExplicitlyTyped= expressionIsExplicitlyTyped; } @Override public boolean visit(ITypeBinding type) { IMethodBinding[] methods= type.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { IMethodBinding candidate= methods[i]; if (candidate.getMethodDeclaration() == fOriginalMethod.getMethodDeclaration()) { continue; } ITypeBinding candidateDeclaringType= candidate.getDeclaringClass(); if (fDeclaringType != candidateDeclaringType) { int modifiers= candidate.getModifiers(); if (candidateDeclaringType.isInterface() && Modifier.isStatic(modifiers)) { continue; } if (Modifier.isPrivate(modifiers)) { continue; } } if (fOriginalMethod.getName().equals(candidate.getName()) && !fOriginalMethod.overrides(candidate)) { ITypeBinding[] originalParameterTypes= fOriginalMethod.getParameterTypes(); ITypeBinding[] candidateParameterTypes= candidate.getParameterTypes(); boolean couldBeAmbiguous; if (originalParameterTypes.length == candidateParameterTypes.length) { couldBeAmbiguous= true; } else if (fOriginalMethod.isVarargs() || candidate.isVarargs() ) { int candidateMinArgumentCount= candidateParameterTypes.length; if (candidate.isVarargs()) candidateMinArgumentCount--; couldBeAmbiguous= fArgumentCount >= candidateMinArgumentCount; } else { couldBeAmbiguous= false; } if (couldBeAmbiguous) { ITypeBinding parameterType= ASTResolving.getParameterTypeBinding(candidate, fArgIndex); if (parameterType != null && parameterType.getFunctionalInterfaceMethod() != null) { if (!fExpressionIsExplicitlyTyped) { /* According to JLS8 15.12.2.2, implicitly typed lambda expressions are not "pertinent to applicability" * and hence potentially applicable methods are always "applicable by strict invocation", * regardless of whether argument expressions are compatible with the method's parameter types or not. * If there are multiple such methods, 15.12.2.5 results in an ambiguous method invocation. */ return false; } /* Explicitly typed lambda expressions are pertinent to applicability, and hence * compatibility with the corresponding method parameter type is checked. And since this check * separates functional interface methods by their void-compatibility state, functional interfaces * with a different void compatibility are not applicable any more and hence can't cause * an ambiguous method invocation. */ ITypeBinding origParamType= ASTResolving.getParameterTypeBinding(fOriginalMethod, fArgIndex); boolean originalIsVoidCompatible= Bindings.isVoidType(origParamType.getFunctionalInterfaceMethod().getReturnType()); boolean candidateIsVoidCompatible= Bindings.isVoidType(parameterType.getFunctionalInterfaceMethod().getReturnType()); if (originalIsVoidCompatible == candidateIsVoidCompatible) { return false; } } } } } return true; } }
Derives the target type defined at the location of the given expression if the target context supports poly expressions.
Params:
  • expression – the expression at whose location the target type is required
Returns:the type binding of the target type defined at the location of the given expression if the target context supports poly expressions, or null if the target type could not be derived
Since:3.10
/** * Derives the target type defined at the location of the given expression if the target context * supports poly expressions. * * @param expression the expression at whose location the target type is required * @return the type binding of the target type defined at the location of the given expression * if the target context supports poly expressions, or <code>null</code> if the target * type could not be derived * * @since 3.10 */
public static ITypeBinding getTargetType(Expression expression) { ASTNode parent= expression.getParent(); StructuralPropertyDescriptor locationInParent= expression.getLocationInParent(); if (locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY || locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY) { return ((VariableDeclaration) parent).getName().resolveTypeBinding(); } else if (locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY) { return ((Assignment) parent).getLeftHandSide().resolveTypeBinding(); } else if (locationInParent == ReturnStatement.EXPRESSION_PROPERTY) { return getTargetTypeForReturnStmt((ReturnStatement) parent); } else if (locationInParent == ArrayInitializer.EXPRESSIONS_PROPERTY) { return getTargetTypeForArrayInitializer((ArrayInitializer) parent); } else if (locationInParent == ArrayAccess.INDEX_PROPERTY) { return parent.getAST().resolveWellKnownType(int.class.getSimpleName()); } else if (locationInParent == ConditionalExpression.EXPRESSION_PROPERTY || locationInParent == IfStatement.EXPRESSION_PROPERTY || locationInParent == WhileStatement.EXPRESSION_PROPERTY || locationInParent == DoStatement.EXPRESSION_PROPERTY) { return parent.getAST().resolveWellKnownType(boolean.class.getSimpleName()); } else if (locationInParent == SwitchStatement.EXPRESSION_PROPERTY) { final ITypeBinding discriminentType= expression.resolveTypeBinding(); if (discriminentType.isPrimitive() || discriminentType.isEnum() || discriminentType.getQualifiedName().equals(String.class.getCanonicalName())) { return discriminentType; } else { return Bindings.getUnboxedTypeBinding(discriminentType, parent.getAST()); } } else if (locationInParent == MethodInvocation.ARGUMENTS_PROPERTY) { MethodInvocation methodInvocation= (MethodInvocation) parent; IMethodBinding methodBinding= methodInvocation.resolveMethodBinding(); if (methodBinding != null) { return getParameterTypeBinding(expression, methodInvocation.arguments(), methodBinding); } } else if (locationInParent == SuperMethodInvocation.ARGUMENTS_PROPERTY) { SuperMethodInvocation superMethodInvocation= (SuperMethodInvocation) parent; IMethodBinding superMethodBinding= superMethodInvocation.resolveMethodBinding(); if (superMethodBinding != null) { return getParameterTypeBinding(expression, superMethodInvocation.arguments(), superMethodBinding); } } else if (locationInParent == ConstructorInvocation.ARGUMENTS_PROPERTY) { ConstructorInvocation constructorInvocation= (ConstructorInvocation) parent; IMethodBinding constructorBinding= constructorInvocation.resolveConstructorBinding(); if (constructorBinding != null) { return getParameterTypeBinding(expression, constructorInvocation.arguments(), constructorBinding); } } else if (locationInParent == SuperConstructorInvocation.ARGUMENTS_PROPERTY) { SuperConstructorInvocation superConstructorInvocation= (SuperConstructorInvocation) parent; IMethodBinding superConstructorBinding= superConstructorInvocation.resolveConstructorBinding(); if (superConstructorBinding != null) { return getParameterTypeBinding(expression, superConstructorInvocation.arguments(), superConstructorBinding); } } else if (locationInParent == ClassInstanceCreation.ARGUMENTS_PROPERTY) { ClassInstanceCreation creation= (ClassInstanceCreation) parent; IMethodBinding creationBinding= creation.resolveConstructorBinding(); if (creationBinding != null) { return getParameterTypeBinding(expression, creation.arguments(), creationBinding); } } else if (locationInParent == EnumConstantDeclaration.ARGUMENTS_PROPERTY) { EnumConstantDeclaration enumConstantDecl= (EnumConstantDeclaration) parent; IMethodBinding enumConstructorBinding= enumConstantDecl.resolveConstructorBinding(); if (enumConstructorBinding != null) { return getParameterTypeBinding(expression, enumConstantDecl.arguments(), enumConstructorBinding); } } else if (locationInParent == LambdaExpression.BODY_PROPERTY) { IMethodBinding methodBinding= ((LambdaExpression) parent).resolveMethodBinding(); if (methodBinding != null) { return methodBinding.getReturnType(); } } else if (locationInParent == ConditionalExpression.THEN_EXPRESSION_PROPERTY || locationInParent == ConditionalExpression.ELSE_EXPRESSION_PROPERTY) { return getTargetType((ConditionalExpression) parent); } else if (locationInParent == CastExpression.EXPRESSION_PROPERTY) { return ((CastExpression) parent).getType().resolveBinding(); } else if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY) { return getTargetType((ParenthesizedExpression) parent); } return null; } private static ITypeBinding getParameterTypeBinding(Expression expression, List<Expression> arguments, IMethodBinding methodBinding) { int index= arguments.indexOf(expression); return ASTResolving.getParameterTypeBinding(methodBinding, index); } private static ITypeBinding getTargetTypeForArrayInitializer(ArrayInitializer arrayInitializer) { ASTNode initializerParent= arrayInitializer.getParent(); while (initializerParent instanceof ArrayInitializer) { initializerParent= initializerParent.getParent(); } if (initializerParent instanceof ArrayCreation) { return ((ArrayCreation) initializerParent).getType().getElementType().resolveBinding(); } else if (initializerParent instanceof VariableDeclaration) { ITypeBinding typeBinding= ((VariableDeclaration) initializerParent).getName().resolveTypeBinding(); if (typeBinding != null) { return typeBinding.getElementType(); } } return null; } private static ITypeBinding getTargetTypeForReturnStmt(ReturnStatement returnStmt) { LambdaExpression enclosingLambdaExpr= ASTResolving.findEnclosingLambdaExpression(returnStmt); if (enclosingLambdaExpr != null) { IMethodBinding methodBinding= enclosingLambdaExpr.resolveMethodBinding(); return methodBinding == null ? null : methodBinding.getReturnType(); } MethodDeclaration enclosingMethodDecl= ASTResolving.findParentMethodDeclaration(returnStmt); if (enclosingMethodDecl != null) { IMethodBinding methodBinding= enclosingMethodDecl.resolveBinding(); return methodBinding == null ? null : methodBinding.getReturnType(); } return null; }
Returns whether an expression at the given location needs explicit boxing.
Params:
  • expression – the expression
Returns:true iff an expression at the given location needs explicit boxing
Since:3.6
/** * 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; }
Checks whether the given expression is a lambda expression with explicitly typed parameters.
Params:
  • expression – the expression to check
Returns:true if the expression is a lambda expression with explicitly typed parameters or no parameters, false otherwise
/** * Checks whether the given expression is a lambda expression with explicitly typed parameters. * * @param expression the expression to check * @return <code>true</code> if the expression is a lambda expression with explicitly typed * parameters or no parameters, <code>false</code> otherwise */
public static boolean isExplicitlyTypedLambda(Expression expression) { if (!(expression instanceof LambdaExpression)) return false; LambdaExpression lambda= (LambdaExpression) expression; List<VariableDeclaration> parameters= lambda.parameters(); if (parameters.isEmpty()) return true; return parameters.get(0) instanceof SingleVariableDeclaration; }
Returns the closest ancestor of node that is an instance of parentClass, or null if none.

Warning: 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 ASTResolving.find(..) methods instead.

Params:
  • node – the node
  • parentClass – the class of the sought ancestor node
Returns:the closest ancestor of node that is an instance of parentClass, or null if none
/** * 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 <T extends ASTNode> T getParent(ASTNode node, Class<T> parentClass) { do { node= node.getParent(); } while (node != null && !parentClass.isInstance(node)); return parentClass.cast(node); }
Returns the closest ancestor of node whose type is nodeType, or null if none.

Warning: 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 ASTResolving.find(..) methods instead.

Params:
  • node – the node
  • nodeType – the node type constant from ASTNode
Returns:the closest ancestor of node whose type is nodeType, or null if none
/** * 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; }
For Name or Type nodes, returns the topmost Type node that shares the same type binding as the given node.
Params:
  • node – an ASTNode
Returns:the normalized Type node or the original node
/** * For {@link Name} or {@link Type} nodes, returns the topmost {@link Type} node * that shares the same type binding as the given node. * * @param node an ASTNode * @return the normalized {@link Type} node or the original node */
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()) || NameQualifiedType.NAME_PROPERTY.equals(current.getLocationInParent())) { current= current.getParent(); } // normalize parameterized types if (ParameterizedType.TYPE_PROPERTY.equals(current.getLocationInParent())) { current= current.getParent(); } return current; }
Returns true iff parent is a true ancestor of node (i.e. returns false if parent == node).
Params:
  • node – node to test
  • parent – assumed parent
Returns:true iff parent is a true ancestor of node
/** * 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.
Params:
  • invocation – method invocation to resolve type of
Returns:the type binding of the receiver
/** * 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= 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<>(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(); else if ((severity & INFO) != 0) consider= problem.isInfo(); 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<>(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; } } 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]; } }
Returns the topmost ancestor of name that is still a Name.

Note: The returned node may resolve to a different binding than the given name!

Params:
  • name – a name node
See Also:
Returns:the topmost name
/** * Returns the topmost ancestor of <code>name</code> that is still a {@link Name}. * <p> * <b>Note:</b> The returned node may resolve to a different binding than the given <code>name</code>! * * @param name a name node * @return the topmost name * @see #getNormalizedNode(ASTNode) */
public static Name getTopMostName(Name name) { Name result= name; while(result.getParent() instanceof Name) { result= (Name)result.getParent(); } return result; }
Returns the topmost ancestor of node that is a Type (but not a UnionType).

Note: The returned node often resolves to a different binding than the given node!

Params:
  • node – the starting node, can be null
See Also:
Returns:the topmost type or null if the node is not a descendant of a type node
/** * Returns the topmost ancestor of <code>node</code> that is a {@link Type} (but not a {@link UnionType}). * <p> * <b>Note:</b> The returned node often resolves to a different binding than the given <code>node</code>! * * @param node the starting node, can be <code>null</code> * @return the topmost type or <code>null</code> if the node is not a descendant of a type node * @see #getNormalizedNode(ASTNode) */
public static Type getTopMostType(ASTNode node) { ASTNode result= null; while (node instanceof Type && !(node instanceof UnionType) || node instanceof Name || node instanceof Annotation || node instanceof MemberValuePair || node instanceof Expression) { // Expression could maybe be reduced to expression node types that can appear in an annotation result= node; node= node.getParent(); } if (result instanceof Type) return (Type) result; return null; } public static int changeVisibility(int modifiers, int visibility) { return (modifiers & CLEAR_VISIBILITY) | visibility; }
Adds flags to the given node and all its descendants.
Params:
  • root – The root node
  • flags – The flags to set
/** * 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(); } }
Returns whether the provided method invocation invokes a method with the provided method signature. The method signature is compared against the erasure of the invoked method.
Params:
  • node – the method invocation to compare
  • typeQualifiedName – the qualified name of the type declaring the method
  • methodName – the method name
  • parameterTypesQualifiedNames – the qualified names of the parameter types
Returns:true if the provided method invocation matches the provided method signature, false otherwise
/** * Returns whether the provided method invocation invokes a method with the * provided method signature. The method signature is compared against the * erasure of the invoked method. * * @param node the method invocation to compare * @param typeQualifiedName the qualified name of the type declaring * the method * @param methodName the method name * @param parameterTypesQualifiedNames the qualified names of the parameter * types * @return true if the provided method invocation matches the provided method * signature, false otherwise */
public static boolean usesGivenSignature(MethodInvocation node, String typeQualifiedName, String methodName, String... parameterTypesQualifiedNames) { if (node == null || node.resolveMethodBinding() == null || !node.resolveMethodBinding().getDeclaringClass().getQualifiedName().equals(typeQualifiedName) || !node.getName().getIdentifier().equals(methodName)) { return false; } if (node.arguments() == null && parameterTypesQualifiedNames == null) { return true; } if (node.arguments() == null || parameterTypesQualifiedNames == null) { return true; } if (node.arguments().size() == parameterTypesQualifiedNames.length) { return true; } for (int i= 0; i < parameterTypesQualifiedNames.length; i++) { if (((Type) node.typeArguments().get(i)).resolveBinding() == null || ((Type) node.typeArguments().get(i)).resolveBinding().getQualifiedName() != parameterTypesQualifiedNames[i]) { return false; } } return true; } 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= getParent(NodeFinder.perform(root, type.getNameRange()), ClassInstanceCreation.class); if (creation != null) return creation.resolveTypeBinding(); } } else { final AbstractTypeDeclaration declaration= 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.
Params:
  • stringValue – the string value
See Also:
Returns:the escaped string
/** * 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(IASTSharedValues.SHARED_AST_LEVEL, false).newStringLiteral(); stringLiteral.setLiteralValue(stringValue); return stringLiteral.getEscapedValue(); }
Escapes a character value to a literal that can be used in Java source.
Params:
  • ch – the character value
See Also:
Returns:the escaped string
/** * 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(IASTSharedValues.SHARED_AST_LEVEL, false).newCharacterLiteral(); characterLiteral.setCharValue(ch); return characterLiteral.getEscapedValue(); }
If the given node has already been rewritten, undo that rewrite and return the replacement version of the node. Otherwise, return the result of ASTRewrite.createCopyTarget(ASTNode).
Params:
  • rewrite – ASTRewrite for the given node
  • node – the node to get the replacement or to create a copy placeholder for
  • group – the edit group which collects the corresponding text edits, or null if ungrouped
Returns:the replacement node if the given node has already been rewritten or the new copy placeholder node
/** * If the given <code>node</code> has already been rewritten, undo that rewrite and return the * replacement version of the node. Otherwise, return the result of * {@link ASTRewrite#createCopyTarget(ASTNode)}. * * @param rewrite ASTRewrite for the given node * @param node the node to get the replacement or to create a copy placeholder for * @param group the edit group which collects the corresponding text edits, or <code>null</code> * if ungrouped * @return the replacement node if the given <code>node</code> has already been rewritten or the * new copy placeholder node */
public static ASTNode getCopyOrReplacement(ASTRewrite rewrite, ASTNode node, TextEditGroup group) { ASTNode rewrittenNode= (ASTNode) rewrite.get(node.getParent(), node.getLocationInParent()); if (rewrittenNode != node) { // Undo previous rewrite to avoid the problem that the same node would be inserted in two places: rewrite.replace(rewrittenNode, node, group); return rewrittenNode; } return rewrite.createCopyTarget(node); }
Params:
  • rewrite – ASTRewrite for the given node
  • node – the node to create a move placeholder for
Throws:
Returns:the new placeholder node
/** * Type-safe variant of {@link ASTRewrite#createMoveTarget(ASTNode)}. * * @param rewrite ASTRewrite for the given node * @param node the node to create a move placeholder for * @return the new placeholder node * @throws IllegalArgumentException if the node is null, or if the node * is not part of the rewrite's AST */
@SuppressWarnings("unchecked") public static <T extends ASTNode> T createMoveTarget(ASTRewrite rewrite, T node) { return (T) rewrite.createMoveTarget(node); }
Type-safe variant of ASTNode.copySubtree(AST, ASTNode).
Params:
  • target – the AST that is to own the nodes in the result
  • node – the node to copy, or null if none
Returns:the copied node, or null if node is null
/** * Type-safe variant of {@link ASTNode#copySubtree(AST, ASTNode)}. * * @param target the AST that is to own the nodes in the result * @param node the node to copy, or <code>null</code> if none * @return the copied node, or <code>null</code> if <code>node</code> * is <code>null</code> */
@SuppressWarnings("unchecked") public static <T extends ASTNode> T copySubtree(AST target, T node) { return (T) ASTNode.copySubtree(target, node); }
Returns a list of local variable names which are visible at the given node.
Params:
  • node – the AST node
See Also:
Returns:a list of local variable names visible at the given node
Since:3.10
/** * Returns a list of local variable names which are visible at the given node. * * @param node the AST node * @return a list of local variable names visible at the given node * @see ScopeAnalyzer#getDeclarationsInScope(int, int) * @since 3.10 */
public static List<String> getVisibleLocalVariablesInScope(ASTNode node) { List<String> variableNames= new ArrayList<>(); CompilationUnit root= (CompilationUnit) node.getRoot(); IBinding[] bindings= new ScopeAnalyzer(root). getDeclarationsInScope(node.getStartPosition(), ScopeAnalyzer.VARIABLES | ScopeAnalyzer.NO_FIELDS | ScopeAnalyzer.CHECK_VISIBILITY); for (IBinding binding : bindings) { variableNames.add(binding.getName()); } return variableNames; }
Checks whether the given exprStatement has a semicolon at the end.
Params:
Returns:true if the given exprStatement has a semicolon at the end, false otherwise
/** * Checks whether the given <code>exprStatement</code> has a semicolon at the end. * * @param exprStatement the {@link ExpressionStatement} to check the semicolon * @param cu the compilation unit * @return <code>true</code> if the given <code>exprStatement</code> has a semicolon at the end, * <code>false</code> otherwise */
public static boolean hasSemicolon(ExpressionStatement exprStatement, ICompilationUnit cu) { boolean hasSemicolon= true; if ((exprStatement.getFlags() & ASTNode.RECOVERED) != 0) { try { Expression expression= exprStatement.getExpression(); TokenScanner scanner= new TokenScanner(cu); hasSemicolon= scanner.readNext(expression.getStartPosition() + expression.getLength(), true) == ITerminalSymbols.TokenNameSEMICOLON; } catch (CoreException e) { hasSemicolon= false; } } return hasSemicolon; }
Checks if the given node is a VariableDeclarationStatement or a SimpleName whose type is 'var'.
Params:
  • node – the AST node
  • astRoot – the AST node of the compilation unit
Returns:true if the given ASTNode represents a SimpleName or VariableDeclarationStatement that has a 'var' type and false otherwise.
/** * Checks if the given <code>node</code> is a {@link VariableDeclarationStatement} * or a {@link SimpleName} whose type is 'var'. * * @param node the AST node * @param astRoot the AST node of the compilation unit * @return <code>true</code> if the given {@link ASTNode} represents a * {@link SimpleName} or {@link VariableDeclarationStatement} that has a 'var' type * and <code>false</code> otherwise. */
public static boolean isVarType(ASTNode node, CompilationUnit astRoot) { IJavaElement root= astRoot.getJavaElement(); if (root == null) { return false; } IJavaProject javaProject= root.getJavaProject(); if (javaProject == null) { return false; } if (!JavaModelUtil.is10OrHigher(javaProject)) { return false; } Type type= null; if (node instanceof SimpleName) { IBinding binding= null; SimpleName name= (SimpleName) node; binding= name.resolveBinding(); if (!(binding instanceof IVariableBinding)) { return false; } IVariableBinding varBinding= (IVariableBinding) binding; if (varBinding.isField() || varBinding.isParameter()) { return false; } ASTNode varDeclaration= astRoot.findDeclaringNode(varBinding); if (varDeclaration == null) { return false; } ITypeBinding typeBinding= varBinding.getType(); if (typeBinding == null || typeBinding.isAnonymous() || typeBinding.isIntersectionType() || typeBinding.isWildcardType()) { return false; } if (varDeclaration instanceof SingleVariableDeclaration) { type= ((SingleVariableDeclaration) varDeclaration).getType(); } else if (varDeclaration instanceof VariableDeclarationFragment) { ASTNode parent= varDeclaration.getParent(); if (parent instanceof VariableDeclarationStatement) { type= ((VariableDeclarationStatement) parent).getType(); } else if (parent instanceof VariableDeclarationExpression) { type= ((VariableDeclarationExpression) parent).getType(); } } } else if (node instanceof VariableDeclarationStatement) { type= ((VariableDeclarationStatement)node).getType(); } else { return false; } return type == null ? false : type.isVar(); } }