blob: a1e7b78c31264139f95f950dab19faa771328324 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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.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.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BodyDeclaration;
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.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.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.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.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.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.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JDTUIHelperClasses;
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;
/**
* 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 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 getChildListProperty(node.getParent(), (ChildListPropertyDescriptor) locationInParent);
}
return null;
}
/**
* 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.
* @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 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<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;
}
/**
* 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.
*
* @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();
}
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 (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
* <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;
if (parent instanceof MethodInvocation || parent instanceof SuperMethodInvocation) {
if (invocationQualifier != null) {
invocationTargetType= invocationQualifier.resolveTypeBinding();
if (invocationTargetType != null && parent instanceof SuperMethodInvocation) {
invocationTargetType= invocationTargetType.getSuperclass();
}
} else {
ITypeBinding enclosingType= getEnclosingType(parent);
if (enclosingType != null && parent instanceof SuperMethodInvocation) {
enclosingType= enclosingType.getSuperclass();
}
if (enclosingType != null) {
IMethodBinding methodInHierarchy= Bindings.findMethodInHierarchy(enclosingType, methodBinding.getName(), methodBinding.getParameterTypes());
if (methodInHierarchy != null) {
invocationTargetType= enclosingType;
} else {
invocationTargetType= methodBinding.getDeclaringClass();
}
} else {
// not expected
invocationTargetType= methodBinding.getDeclaringClass();
}
}
} else {
invocationTargetType= methodBinding.getDeclaringClass();
}
if (invocationTargetType != null) {
TypeBindingVisitor visitor= new AmbiguousTargetMethodAnalyzer(invocationTargetType, methodBinding, argumentIndex, argumentCount, expressionIsExplicitlyTyped);
return !(visitor.visit(invocationTargetType) && Bindings.visitHierarchy(invocationTargetType, visitor));
}
}
return true;
}
private static class AmbiguousTargetMethodAnalyzer implements TypeBindingVisitor {
private ITypeBinding fDeclaringType;
private IMethodBinding fOriginalMethod;
private int fArgIndex;
private int fArgumentCount;
private boolean fExpressionIsExplicitlyTyped;
/**
* @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;
}
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.
*
* @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 == 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.
*
* @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;
}
private 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 <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;
}
/**
* 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 <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];
}
}
/**
* 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 <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.
* @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();
}
/**
* 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 {@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.
*
* @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<String>();
CompilationUnit root= (CompilationUnit) node.getRoot();
IBinding[] bindings= new ScopeAnalyzer(root).
getDeclarationsInScope(node.getStartPosition(), ScopeAnalyzer.VARIABLES | ScopeAnalyzer.CHECK_VISIBILITY);
for (IBinding binding : bindings) {
if (binding instanceof IVariableBinding && !((IVariableBinding) binding).isField()) {
variableNames.add(binding.getName());
}
}
return variableNames;
}
}