1 package annotator.find; 2 3 import java.util.LinkedHashMap; 4 import java.util.Map; 5 6 import annotations.el.BoundLocation; 7 import annotations.el.InnerTypeLocation; 8 import annotations.el.LocalLocation; 9 import annotations.el.RelativeLocation; 10 import annotations.el.TypeIndexLocation; 11 import annotations.io.ASTPath; 12 import annotations.io.DebugWriter; 13 import annotator.Main; 14 15 import com.sun.source.tree.Tree; 16 import com.sun.source.util.TreePath; 17 18 /** 19 * Represents a set of Criterion objects for locating a program element in 20 * a source tree. 21 * <p> 22 * 23 * This class also contains static factory methods for creating a {@code 24 * Criterion}. 25 */ 26 public final class Criteria { 27 public static DebugWriter dbug = new DebugWriter(); 28 29 /** The set of criterion objects, indexed by kind. */ 30 private final Map<Criterion.Kind, Criterion> criteria; 31 32 /** 33 * Creates a new {@code Criteria} without any {@code Criterion}. 34 */ 35 public Criteria() { 36 this.criteria = new LinkedHashMap<Criterion.Kind, Criterion>(); 37 } 38 39 /** 40 * Add a {@code Criterion} to this {@code Criteria}. 41 * 42 * @param c the criterion to add 43 */ 44 public void add(Criterion c) { 45 criteria.put(c.getKind(), c); 46 } 47 48 /** 49 * Determines whether or not the program element at the leaf of the 50 * specified path is satisfied by these criteria. 51 * 52 * @param path the tree path to check against 53 * @param leaf the tree at the leaf of the path; only relevant when the path 54 * is null, in which case the leaf is a CompilationUnitTree 55 * @return true if all of these criteria are satisfied by the given path, 56 * false otherwise 57 */ 58 public boolean isSatisfiedBy(TreePath path, Tree leaf) { 59 assert path == null || path.getLeaf() == leaf; 60 for (Criterion c : criteria.values()) { 61 if (! c.isSatisfiedBy(path, leaf)) { 62 dbug.debug("UNsatisfied criterion:%n %s%n %s%n", 63 c, Main.pathToString(path)); 64 return false; 65 } else { 66 dbug.debug("satisfied criterion:%n %s%n %s%n", 67 c, Main.pathToString(path)); 68 } 69 } 70 return true; 71 } 72 73 /** 74 * Determines whether or not the program element at the leaf of the 75 * specified path is satisfied by these criteria. 76 * 77 * @param path the tree path to check against 78 * @return true if all of these criteria are satisfied by the given path, 79 * false otherwise 80 */ 81 public boolean isSatisfiedBy(TreePath path) { 82 for (Criterion c : criteria.values()) { 83 if (! c.isSatisfiedBy(path)) { 84 dbug.debug("UNsatisfied criterion: %s%n", c); 85 return false; 86 } else { 87 dbug.debug("satisfied criterion: %s%n", c); 88 } 89 } 90 return true; 91 } 92 93 /** 94 * Determines whether this is the criteria on a receiver. 95 * 96 * @return true iff this is the criteria on a receiver 97 */ 98 public boolean isOnReceiver() { 99 for (Criterion c : criteria.values()) { 100 if (c.getKind() == Criterion.Kind.RECEIVER) { 101 return true; 102 } 103 } 104 105 return false; 106 } 107 108 /** 109 * Determines whether this is the criteria on a package. 110 * 111 * @return true iff this is the criteria on a package 112 */ 113 public boolean isOnPackage() { 114 for (Criterion c : criteria.values()) { 115 if (c.getKind() == Criterion.Kind.PACKAGE) { 116 return true; 117 } 118 } 119 120 return false; 121 } 122 123 /** 124 * Determines whether this is the criteria on a return type. 125 * 126 * @return true iff this is the criteria on a return type 127 */ 128 public boolean isOnReturnType() { 129 for (Criterion c : criteria.values()) { 130 if (c.getKind() == Criterion.Kind.RETURN_TYPE) { 131 return true; 132 } 133 } 134 135 return false; 136 } 137 138 /** 139 * Determines whether this is the criteria on a local variable. 140 * 141 * @return true iff this is the criteria on a local variable 142 */ 143 public boolean isOnLocalVariable() { 144 for (Criterion c : criteria.values()) { 145 if (c.getKind() == Criterion.Kind.LOCAL_VARIABLE) { 146 return true; 147 } 148 } 149 150 return false; 151 } 152 153 /** 154 * Determines whether this is the criteria on the RHS of an occurrence 155 * of 'instanceof'. 156 */ 157 public boolean isOnInstanceof() { 158 for (Criterion c : criteria.values()) { 159 if (c.getKind() == Criterion.Kind.INSTANCE_OF) { 160 return true; 161 } 162 } 163 return false; 164 } 165 166 /** 167 * Determines whether this is the criteria on an object initializer. 168 */ 169 public boolean isOnNew() { 170 for (Criterion c : criteria.values()) { 171 if (c.getKind() == Criterion.Kind.NEW) { 172 return true; 173 } 174 } 175 return false; 176 } 177 178 /** 179 * Determines whether this is the criteria on a class {@code extends} bound. 180 */ 181 public boolean isOnTypeDeclarationExtendsClause() { 182 for (Criterion c : criteria.values()) { 183 if (c.getKind() == Criterion.Kind.EXTIMPLS_LOCATION) { 184 return ((ExtImplsLocationCriterion) c).getIndex() == -1; 185 } 186 } 187 return false; 188 } 189 190 /** 191 * Returns true if this Criteria is on the given method. 192 */ 193 public boolean isOnMethod(String methodname) { 194 for (Criterion c : criteria.values()) { 195 if (c.getKind() == Criterion.Kind.IN_METHOD) { 196 if (((InMethodCriterion) c).name.equals(methodname)) { 197 return true; 198 } 199 } 200 } 201 return false; 202 } 203 204 /** 205 * Returns true if this Criteria is on the given method. 206 */ 207 public boolean isOnFieldDeclaration() { 208 for (Criterion c : criteria.values()) { 209 if (c.getKind() == Criterion.Kind.FIELD 210 && ((FieldCriterion) c).isDeclaration) { 211 return true; 212 } 213 } 214 return false; 215 } 216 217 /** 218 * Gives the AST path specified in the criteria, if any. 219 * 220 * @return AST path from {@link ASTPathCriterion}, or null if none present 221 */ 222 public ASTPath getASTPath() { 223 for (Criterion c : criteria.values()) { 224 if (c.getKind() == Criterion.Kind.AST_PATH) { 225 return ((ASTPathCriterion) c).astPath; 226 } 227 } 228 229 return null; 230 } 231 232 /** 233 * Returns the name of the class specified in the Criteria, if any. 234 * 235 * @return class name from {@link InClassCriterion}, or null if none present 236 */ 237 public String getClassName() { 238 for (Criterion c : criteria.values()) { 239 if (c.getKind() == Criterion.Kind.IN_CLASS) { 240 return ((InClassCriterion) c).className; 241 } 242 } 243 244 return null; 245 } 246 247 /** 248 * Returns the name of the method specified in the Criteria, if any. 249 * 250 * @return method name from {@link InMethodCriterion}, or null if none present 251 */ 252 public String getMethodName() { 253 for (Criterion c : criteria.values()) { 254 if (c.getKind() == Criterion.Kind.IN_METHOD) { 255 return ((InMethodCriterion) c).name; 256 } 257 } 258 259 return null; 260 } 261 262 /** 263 * Returns the name of the member field specified in the Criteria, if any. 264 * 265 * @return field name from {@link FieldCriterion}, or null if none present 266 */ 267 public String getFieldName() { 268 for (Criterion c : criteria.values()) { 269 if (c.getKind() == Criterion.Kind.FIELD) { 270 return ((FieldCriterion) c).varName; 271 } 272 } 273 274 return null; 275 } 276 277 /** 278 * @return a GenericArrayLocationCriterion if this has one, else null 279 */ 280 public GenericArrayLocationCriterion getGenericArrayLocation() { 281 for (Criterion c : criteria.values()) { 282 if (c.getKind() == Criterion.Kind.GENERIC_ARRAY_LOCATION) { 283 return (GenericArrayLocationCriterion) c; 284 } 285 } 286 return null; 287 } 288 289 /** 290 * @return a RelativeCriterion if this has one, else null 291 */ 292 public RelativeLocation getCastRelativeLocation() { 293 RelativeLocation result = null; 294 for (Criterion c : criteria.values()) { 295 if (c.getKind() == Criterion.Kind.CAST) { 296 result = ((CastCriterion) c).getLocation(); 297 } 298 } 299 return result; 300 } 301 302 // Returns the last one. Should really return the outermost one. 303 // However, there should not be more than one unless all are equivalent. 304 /** 305 * @return an InClassCriterion if this has one, else null 306 */ 307 public InClassCriterion getInClass() { 308 InClassCriterion result = null; 309 for (Criterion c : criteria.values()) { 310 if (c.getKind() == Criterion.Kind.IN_CLASS) { 311 result = (InClassCriterion) c; 312 } 313 } 314 return result; 315 } 316 317 /** 318 * @return true if this is on the zeroth bound of a type 319 */ 320 // Used when determining whether an annotation is on an implicit upper 321 // bound (the "extends Object" that is customarily omitted). 322 public boolean onBoundZero() { 323 for (Criterion c : criteria.values()) { 324 switch (c.getKind()) { 325 case CLASS_BOUND: 326 if (((ClassBoundCriterion) c).boundLoc.boundIndex != 0) { break; } 327 return true; 328 case METHOD_BOUND: 329 if (((MethodBoundCriterion) c).boundLoc.boundIndex != 0) { break; } 330 return true; 331 case AST_PATH: 332 ASTPath astPath = ((ASTPathCriterion) c).astPath; 333 if (!astPath.isEmpty()) { 334 ASTPath.ASTEntry entry = astPath.get(-1); 335 if (entry.childSelectorIs(ASTPath.BOUND) 336 && entry.getArgument() == 0) { 337 return true; 338 } 339 } 340 break; 341 default: 342 break; 343 } 344 } 345 return false; 346 } 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override 352 public String toString() { 353 return criteria.toString(); 354 } 355 356 357 /////////////////////////////////////////////////////////////////////////// 358 /// Factory methods 359 /// 360 361 /** 362 * Creates an "is" criterion: that a program element has the specified 363 * kind and name. 364 * 365 * @param kind the program element's kind 366 * @param name the program element's name 367 * @return an "is" criterion 368 */ 369 public final static Criterion is(Tree.Kind kind, String name) { 370 return new IsCriterion(kind, name); 371 } 372 373 /** 374 * Creates an "enclosed by" criterion: that a program element is enclosed 375 * by the specified kind of program element. 376 * 377 * @param kind the kind of enclosing program element 378 * @return an "enclosed by" criterion 379 */ 380 public final static Criterion enclosedBy(Tree.Kind kind) { 381 return new EnclosedByCriterion(kind); 382 } 383 384 /** 385 * Creates an "in package" criterion: that a program element is enclosed 386 * by the specified package. 387 * 388 * @param name the name of the enclosing package 389 * @return an "in package" criterion 390 */ 391 public final static Criterion inPackage(String name) { 392 return new InPackageCriterion(name); 393 } 394 395 /** 396 * Creates an "in class" criterion: that a program element is enclosed 397 * by the specified class. 398 * 399 * @param name the name of the enclosing class 400 * @param exact whether to match only in the class itself, not in its inner classes 401 * @return an "in class" criterion 402 */ 403 public final static Criterion inClass(String name, boolean exact) { 404 return new InClassCriterion(name, /*exactmatch=*/ true); 405 } 406 407 /** 408 * Creates an "in method" criterion: that a program element is enclosed 409 * by the specified method. 410 * 411 * @param name the name of the enclosing method 412 * @return an "in method" criterion 413 */ 414 public final static Criterion inMethod(String name) { 415 return new InMethodCriterion(name); 416 } 417 418 /** 419 * Creates a "not in method" criterion: that a program element is not 420 * enclosed by any method. 421 * 422 * @return a "not in method" criterion 423 */ 424 public final static Criterion notInMethod() { 425 return new NotInMethodCriterion(); 426 } 427 428 public final static Criterion packageDecl(String packageName) { 429 return new PackageCriterion(packageName); 430 } 431 432 public final static Criterion atLocation() { 433 return new GenericArrayLocationCriterion(); 434 } 435 436 public final static Criterion atLocation(InnerTypeLocation loc) { 437 return new GenericArrayLocationCriterion(loc); 438 } 439 440 @Deprecated 441 public final static Criterion field(String varName) { 442 return new FieldCriterion(varName); 443 } 444 445 public final static Criterion field(String varName, boolean isOnDeclaration) { 446 return new FieldCriterion(varName, isOnDeclaration); 447 } 448 449 public final static Criterion inStaticInit(int blockID) { 450 return new InInitBlockCriterion(blockID, true); 451 } 452 453 public final static Criterion inInstanceInit(int blockID) { 454 return new InInitBlockCriterion(blockID, false); 455 } 456 457 public final static Criterion inFieldInit(String varName) { 458 return new InFieldInitCriterion(varName); 459 } 460 461 public final static Criterion receiver(String methodName) { 462 return new ReceiverCriterion(methodName); 463 } 464 465 public final static Criterion returnType(String className, String methodName) { 466 return new ReturnTypeCriterion(className, methodName); 467 } 468 469 public final static Criterion isSigMethod(String methodName) { 470 return new IsSigMethodCriterion(methodName); 471 } 472 473 474 public final static Criterion param(String methodName, Integer pos) { 475 return new ParamCriterion(methodName, pos); 476 } 477 // 478 // public final static Criterion param(String methodName, Integer pos, InnerTypeLocation loc) { 479 // return new ParamCriterion(methodName, pos, loc); 480 // } 481 482 483 public final static Criterion local(String methodName, LocalLocation loc) { 484 return new LocalVariableCriterion(methodName, loc); 485 } 486 487 public final static Criterion cast(String methodName, RelativeLocation loc) { 488 return new CastCriterion(methodName, loc); 489 } 490 491 public final static Criterion newObject(String methodName, RelativeLocation loc) { 492 return new NewCriterion(methodName, loc); 493 } 494 495 public final static Criterion instanceOf(String methodName, RelativeLocation loc) { 496 return new InstanceOfCriterion(methodName, loc); 497 } 498 499 public static Criterion memberReference(String methodName, RelativeLocation loc) { 500 return new MemberReferenceCriterion(methodName, loc); 501 } 502 503 public static Criterion methodCall(String methodName, RelativeLocation loc) { 504 return new CallCriterion(methodName, loc); 505 } 506 507 public final static Criterion typeArgument(String methodName, RelativeLocation loc) { 508 return new TypeArgumentCriterion(methodName, loc); 509 } 510 511 public final static Criterion lambda(String methodName, RelativeLocation loc) { 512 return new LambdaCriterion(methodName, loc); 513 } 514 515 public final static Criterion atBoundLocation(BoundLocation loc) { 516 return new BoundLocationCriterion(loc); 517 } 518 519 public final static Criterion atExtImplsLocation(String className, TypeIndexLocation loc) { 520 return new ExtImplsLocationCriterion(className, loc); 521 } 522 523 public final static Criterion methodBound(String methodName, BoundLocation boundLoc) { 524 return new MethodBoundCriterion(methodName, boundLoc); 525 } 526 527 public final static Criterion classBound(String className, BoundLocation boundLoc) { 528 return new ClassBoundCriterion(className, boundLoc); 529 } 530 531 public final static Criterion astPath(ASTPath astPath) { 532 return new ASTPathCriterion(astPath); 533 } 534 } 535