blob: c565d29ca90f620682d2e81d95c5235baa0cc185 [file] [log] [blame]
package annotator.find;
import java.util.LinkedHashMap;
import java.util.Map;
import annotations.el.BoundLocation;
import annotations.el.InnerTypeLocation;
import annotations.el.LocalLocation;
import annotations.el.RelativeLocation;
import annotations.el.TypeIndexLocation;
import annotations.io.ASTPath;
import annotations.io.DebugWriter;
import annotator.Main;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
/**
* Represents a set of Criterion objects for locating a program element in
* a source tree.
* <p>
*
* This class also contains static factory methods for creating a {@code
* Criterion}.
*/
public final class Criteria {
public static DebugWriter dbug = new DebugWriter();
/** The set of criterion objects, indexed by kind. */
private final Map<Criterion.Kind, Criterion> criteria;
/**
* Creates a new {@code Criteria} without any {@code Criterion}.
*/
public Criteria() {
this.criteria = new LinkedHashMap<Criterion.Kind, Criterion>();
}
/**
* Add a {@code Criterion} to this {@code Criteria}.
*
* @param c the criterion to add
*/
public void add(Criterion c) {
criteria.put(c.getKind(), c);
}
/**
* Determines whether or not the program element at the leaf of the
* specified path is satisfied by these criteria.
*
* @param path the tree path to check against
* @param leaf the tree at the leaf of the path; only relevant when the path
* is null, in which case the leaf is a CompilationUnitTree
* @return true if all of these criteria are satisfied by the given path,
* false otherwise
*/
public boolean isSatisfiedBy(TreePath path, Tree leaf) {
assert path == null || path.getLeaf() == leaf;
for (Criterion c : criteria.values()) {
if (! c.isSatisfiedBy(path, leaf)) {
dbug.debug("UNsatisfied criterion:%n %s%n %s%n",
c, Main.pathToString(path));
return false;
} else {
dbug.debug("satisfied criterion:%n %s%n %s%n",
c, Main.pathToString(path));
}
}
return true;
}
/**
* Determines whether or not the program element at the leaf of the
* specified path is satisfied by these criteria.
*
* @param path the tree path to check against
* @return true if all of these criteria are satisfied by the given path,
* false otherwise
*/
public boolean isSatisfiedBy(TreePath path) {
for (Criterion c : criteria.values()) {
if (! c.isSatisfiedBy(path)) {
dbug.debug("UNsatisfied criterion: %s%n", c);
return false;
} else {
dbug.debug("satisfied criterion: %s%n", c);
}
}
return true;
}
/**
* Determines whether this is the criteria on a receiver.
*
* @return true iff this is the criteria on a receiver
*/
public boolean isOnReceiver() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.RECEIVER) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on a package.
*
* @return true iff this is the criteria on a package
*/
public boolean isOnPackage() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.PACKAGE) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on a return type.
*
* @return true iff this is the criteria on a return type
*/
public boolean isOnReturnType() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.RETURN_TYPE) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on a local variable.
*
* @return true iff this is the criteria on a local variable
*/
public boolean isOnLocalVariable() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.LOCAL_VARIABLE) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on the RHS of an occurrence
* of 'instanceof'.
*/
public boolean isOnInstanceof() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.INSTANCE_OF) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on an object initializer.
*/
public boolean isOnNew() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.NEW) {
return true;
}
}
return false;
}
/**
* Determines whether this is the criteria on a class {@code extends} bound.
*/
public boolean isOnTypeDeclarationExtendsClause() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.EXTIMPLS_LOCATION) {
return ((ExtImplsLocationCriterion) c).getIndex() == -1;
}
}
return false;
}
/**
* Returns true if this Criteria is on the given method.
*/
public boolean isOnMethod(String methodname) {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.IN_METHOD) {
if (((InMethodCriterion) c).name.equals(methodname)) {
return true;
}
}
}
return false;
}
/**
* Returns true if this Criteria is on the given method.
*/
public boolean isOnFieldDeclaration() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.FIELD
&& ((FieldCriterion) c).isDeclaration) {
return true;
}
}
return false;
}
/**
* Gives the AST path specified in the criteria, if any.
*
* @return AST path from {@link ASTPathCriterion}, or null if none present
*/
public ASTPath getASTPath() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.AST_PATH) {
return ((ASTPathCriterion) c).astPath;
}
}
return null;
}
/**
* Returns the name of the class specified in the Criteria, if any.
*
* @return class name from {@link InClassCriterion}, or null if none present
*/
public String getClassName() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.IN_CLASS) {
return ((InClassCriterion) c).className;
}
}
return null;
}
/**
* Returns the name of the method specified in the Criteria, if any.
*
* @return method name from {@link InMethodCriterion}, or null if none present
*/
public String getMethodName() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.IN_METHOD) {
return ((InMethodCriterion) c).name;
}
}
return null;
}
/**
* Returns the name of the member field specified in the Criteria, if any.
*
* @return field name from {@link FieldCriterion}, or null if none present
*/
public String getFieldName() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.FIELD) {
return ((FieldCriterion) c).varName;
}
}
return null;
}
/**
* @return a GenericArrayLocationCriterion if this has one, else null
*/
public GenericArrayLocationCriterion getGenericArrayLocation() {
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.GENERIC_ARRAY_LOCATION) {
return (GenericArrayLocationCriterion) c;
}
}
return null;
}
/**
* @return a RelativeCriterion if this has one, else null
*/
public RelativeLocation getCastRelativeLocation() {
RelativeLocation result = null;
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.CAST) {
result = ((CastCriterion) c).getLocation();
}
}
return result;
}
// Returns the last one. Should really return the outermost one.
// However, there should not be more than one unless all are equivalent.
/**
* @return an InClassCriterion if this has one, else null
*/
public InClassCriterion getInClass() {
InClassCriterion result = null;
for (Criterion c : criteria.values()) {
if (c.getKind() == Criterion.Kind.IN_CLASS) {
result = (InClassCriterion) c;
}
}
return result;
}
/**
* @return true if this is on the zeroth bound of a type
*/
// Used when determining whether an annotation is on an implicit upper
// bound (the "extends Object" that is customarily omitted).
public boolean onBoundZero() {
for (Criterion c : criteria.values()) {
switch (c.getKind()) {
case CLASS_BOUND:
if (((ClassBoundCriterion) c).boundLoc.boundIndex != 0) { break; }
return true;
case METHOD_BOUND:
if (((MethodBoundCriterion) c).boundLoc.boundIndex != 0) { break; }
return true;
case AST_PATH:
ASTPath astPath = ((ASTPathCriterion) c).astPath;
if (!astPath.isEmpty()) {
ASTPath.ASTEntry entry = astPath.get(-1);
if (entry.childSelectorIs(ASTPath.BOUND)
&& entry.getArgument() == 0) {
return true;
}
}
break;
default:
break;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return criteria.toString();
}
///////////////////////////////////////////////////////////////////////////
/// Factory methods
///
/**
* Creates an "is" criterion: that a program element has the specified
* kind and name.
*
* @param kind the program element's kind
* @param name the program element's name
* @return an "is" criterion
*/
public final static Criterion is(Tree.Kind kind, String name) {
return new IsCriterion(kind, name);
}
/**
* Creates an "enclosed by" criterion: that a program element is enclosed
* by the specified kind of program element.
*
* @param kind the kind of enclosing program element
* @return an "enclosed by" criterion
*/
public final static Criterion enclosedBy(Tree.Kind kind) {
return new EnclosedByCriterion(kind);
}
/**
* Creates an "in package" criterion: that a program element is enclosed
* by the specified package.
*
* @param name the name of the enclosing package
* @return an "in package" criterion
*/
public final static Criterion inPackage(String name) {
return new InPackageCriterion(name);
}
/**
* Creates an "in class" criterion: that a program element is enclosed
* by the specified class.
*
* @param name the name of the enclosing class
* @param exact whether to match only in the class itself, not in its inner classes
* @return an "in class" criterion
*/
public final static Criterion inClass(String name, boolean exact) {
return new InClassCriterion(name, /*exactmatch=*/ true);
}
/**
* Creates an "in method" criterion: that a program element is enclosed
* by the specified method.
*
* @param name the name of the enclosing method
* @return an "in method" criterion
*/
public final static Criterion inMethod(String name) {
return new InMethodCriterion(name);
}
/**
* Creates a "not in method" criterion: that a program element is not
* enclosed by any method.
*
* @return a "not in method" criterion
*/
public final static Criterion notInMethod() {
return new NotInMethodCriterion();
}
public final static Criterion packageDecl(String packageName) {
return new PackageCriterion(packageName);
}
public final static Criterion atLocation() {
return new GenericArrayLocationCriterion();
}
public final static Criterion atLocation(InnerTypeLocation loc) {
return new GenericArrayLocationCriterion(loc);
}
@Deprecated
public final static Criterion field(String varName) {
return new FieldCriterion(varName);
}
public final static Criterion field(String varName, boolean isOnDeclaration) {
return new FieldCriterion(varName, isOnDeclaration);
}
public final static Criterion inStaticInit(int blockID) {
return new InInitBlockCriterion(blockID, true);
}
public final static Criterion inInstanceInit(int blockID) {
return new InInitBlockCriterion(blockID, false);
}
public final static Criterion inFieldInit(String varName) {
return new InFieldInitCriterion(varName);
}
public final static Criterion receiver(String methodName) {
return new ReceiverCriterion(methodName);
}
public final static Criterion returnType(String className, String methodName) {
return new ReturnTypeCriterion(className, methodName);
}
public final static Criterion isSigMethod(String methodName) {
return new IsSigMethodCriterion(methodName);
}
public final static Criterion param(String methodName, Integer pos) {
return new ParamCriterion(methodName, pos);
}
//
// public final static Criterion param(String methodName, Integer pos, InnerTypeLocation loc) {
// return new ParamCriterion(methodName, pos, loc);
// }
public final static Criterion local(String methodName, LocalLocation loc) {
return new LocalVariableCriterion(methodName, loc);
}
public final static Criterion cast(String methodName, RelativeLocation loc) {
return new CastCriterion(methodName, loc);
}
public final static Criterion newObject(String methodName, RelativeLocation loc) {
return new NewCriterion(methodName, loc);
}
public final static Criterion instanceOf(String methodName, RelativeLocation loc) {
return new InstanceOfCriterion(methodName, loc);
}
public static Criterion memberReference(String methodName, RelativeLocation loc) {
return new MemberReferenceCriterion(methodName, loc);
}
public static Criterion methodCall(String methodName, RelativeLocation loc) {
return new CallCriterion(methodName, loc);
}
public final static Criterion typeArgument(String methodName, RelativeLocation loc) {
return new TypeArgumentCriterion(methodName, loc);
}
public final static Criterion lambda(String methodName, RelativeLocation loc) {
return new LambdaCriterion(methodName, loc);
}
public final static Criterion atBoundLocation(BoundLocation loc) {
return new BoundLocationCriterion(loc);
}
public final static Criterion atExtImplsLocation(String className, TypeIndexLocation loc) {
return new ExtImplsLocationCriterion(className, loc);
}
public final static Criterion methodBound(String methodName, BoundLocation boundLoc) {
return new MethodBoundCriterion(methodName, boundLoc);
}
public final static Criterion classBound(String className, BoundLocation boundLoc) {
return new ClassBoundCriterion(className, boundLoc);
}
public final static Criterion astPath(ASTPath astPath) {
return new ASTPathCriterion(astPath);
}
}