1 /** 2 * Copyright (C) 2006 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.inject.matcher; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import java.io.Serializable; 23 import java.lang.annotation.Annotation; 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.lang.reflect.AnnotatedElement; 27 import java.lang.reflect.Method; 28 29 /** 30 * Matcher implementations. Supports matching classes and methods. 31 * 32 * @author crazybob (at) google.com (Bob Lee) 33 */ 34 public class Matchers { 35 private Matchers() {} 36 37 /** 38 * Returns a matcher which matches any input. 39 */ 40 public static Matcher<Object> any() { 41 return ANY; 42 } 43 44 private static final Matcher<Object> ANY = new Any(); 45 46 private static class Any extends AbstractMatcher<Object> implements Serializable { 47 public boolean matches(Object o) { 48 return true; 49 } 50 51 @Override public String toString() { 52 return "any()"; 53 } 54 55 public Object readResolve() { 56 return any(); 57 } 58 59 private static final long serialVersionUID = 0; 60 } 61 62 /** 63 * Inverts the given matcher. 64 */ 65 public static <T> Matcher<T> not(final Matcher<? super T> p) { 66 return new Not<T>(p); 67 } 68 69 private static class Not<T> extends AbstractMatcher<T> implements Serializable { 70 final Matcher<? super T> delegate; 71 72 private Not(Matcher<? super T> delegate) { 73 this.delegate = checkNotNull(delegate, "delegate"); 74 } 75 76 public boolean matches(T t) { 77 return !delegate.matches(t); 78 } 79 80 @Override public boolean equals(Object other) { 81 return other instanceof Not 82 && ((Not) other).delegate.equals(delegate); 83 } 84 85 @Override public int hashCode() { 86 return -delegate.hashCode(); 87 } 88 89 @Override public String toString() { 90 return "not(" + delegate + ")"; 91 } 92 93 private static final long serialVersionUID = 0; 94 } 95 96 private static void checkForRuntimeRetention( 97 Class<? extends Annotation> annotationType) { 98 Retention retention = annotationType.getAnnotation(Retention.class); 99 checkArgument(retention != null && retention.value() == RetentionPolicy.RUNTIME, 100 "Annotation %s is missing RUNTIME retention", annotationType.getSimpleName()); 101 } 102 103 /** 104 * Returns a matcher which matches elements (methods, classes, etc.) 105 * with a given annotation. 106 */ 107 public static Matcher<AnnotatedElement> annotatedWith( 108 final Class<? extends Annotation> annotationType) { 109 return new AnnotatedWithType(annotationType); 110 } 111 112 private static class AnnotatedWithType extends AbstractMatcher<AnnotatedElement> 113 implements Serializable { 114 private final Class<? extends Annotation> annotationType; 115 116 public AnnotatedWithType(Class<? extends Annotation> annotationType) { 117 this.annotationType = checkNotNull(annotationType, "annotation type"); 118 checkForRuntimeRetention(annotationType); 119 } 120 121 public boolean matches(AnnotatedElement element) { 122 return element.isAnnotationPresent(annotationType); 123 } 124 125 @Override public boolean equals(Object other) { 126 return other instanceof AnnotatedWithType 127 && ((AnnotatedWithType) other).annotationType.equals(annotationType); 128 } 129 130 @Override public int hashCode() { 131 return 37 * annotationType.hashCode(); 132 } 133 134 @Override public String toString() { 135 return "annotatedWith(" + annotationType.getSimpleName() + ".class)"; 136 } 137 138 private static final long serialVersionUID = 0; 139 } 140 141 /** 142 * Returns a matcher which matches elements (methods, classes, etc.) 143 * with a given annotation. 144 */ 145 public static Matcher<AnnotatedElement> annotatedWith( 146 final Annotation annotation) { 147 return new AnnotatedWith(annotation); 148 } 149 150 private static class AnnotatedWith extends AbstractMatcher<AnnotatedElement> 151 implements Serializable { 152 private final Annotation annotation; 153 154 public AnnotatedWith(Annotation annotation) { 155 this.annotation = checkNotNull(annotation, "annotation"); 156 checkForRuntimeRetention(annotation.annotationType()); 157 } 158 159 public boolean matches(AnnotatedElement element) { 160 Annotation fromElement = element.getAnnotation(annotation.annotationType()); 161 return fromElement != null && annotation.equals(fromElement); 162 } 163 164 @Override public boolean equals(Object other) { 165 return other instanceof AnnotatedWith 166 && ((AnnotatedWith) other).annotation.equals(annotation); 167 } 168 169 @Override public int hashCode() { 170 return 37 * annotation.hashCode(); 171 } 172 173 @Override public String toString() { 174 return "annotatedWith(" + annotation + ")"; 175 } 176 177 private static final long serialVersionUID = 0; 178 } 179 180 /** 181 * Returns a matcher which matches subclasses of the given type (as well as 182 * the given type). 183 */ 184 public static Matcher<Class> subclassesOf(final Class<?> superclass) { 185 return new SubclassesOf(superclass); 186 } 187 188 private static class SubclassesOf extends AbstractMatcher<Class> 189 implements Serializable { 190 private final Class<?> superclass; 191 192 public SubclassesOf(Class<?> superclass) { 193 this.superclass = checkNotNull(superclass, "superclass"); 194 } 195 196 public boolean matches(Class subclass) { 197 return superclass.isAssignableFrom(subclass); 198 } 199 200 @Override public boolean equals(Object other) { 201 return other instanceof SubclassesOf 202 && ((SubclassesOf) other).superclass.equals(superclass); 203 } 204 205 @Override public int hashCode() { 206 return 37 * superclass.hashCode(); 207 } 208 209 @Override public String toString() { 210 return "subclassesOf(" + superclass.getSimpleName() + ".class)"; 211 } 212 213 private static final long serialVersionUID = 0; 214 } 215 216 /** 217 * Returns a matcher which matches objects equal to the given object. 218 */ 219 public static Matcher<Object> only(Object value) { 220 return new Only(value); 221 } 222 223 private static class Only extends AbstractMatcher<Object> 224 implements Serializable { 225 private final Object value; 226 227 public Only(Object value) { 228 this.value = checkNotNull(value, "value"); 229 } 230 231 public boolean matches(Object other) { 232 return value.equals(other); 233 } 234 235 @Override public boolean equals(Object other) { 236 return other instanceof Only 237 && ((Only) other).value.equals(value); 238 } 239 240 @Override public int hashCode() { 241 return 37 * value.hashCode(); 242 } 243 244 @Override public String toString() { 245 return "only(" + value + ")"; 246 } 247 248 private static final long serialVersionUID = 0; 249 } 250 251 /** 252 * Returns a matcher which matches only the given object. 253 */ 254 public static Matcher<Object> identicalTo(final Object value) { 255 return new IdenticalTo(value); 256 } 257 258 private static class IdenticalTo extends AbstractMatcher<Object> 259 implements Serializable { 260 private final Object value; 261 262 public IdenticalTo(Object value) { 263 this.value = checkNotNull(value, "value"); 264 } 265 266 public boolean matches(Object other) { 267 return value == other; 268 } 269 270 @Override public boolean equals(Object other) { 271 return other instanceof IdenticalTo 272 && ((IdenticalTo) other).value == value; 273 } 274 275 @Override public int hashCode() { 276 return 37 * System.identityHashCode(value); 277 } 278 279 @Override public String toString() { 280 return "identicalTo(" + value + ")"; 281 } 282 283 private static final long serialVersionUID = 0; 284 } 285 286 /** 287 * Returns a matcher which matches classes in the given package. Packages are specific to their 288 * classloader, so classes with the same package name may not have the same package at runtime. 289 */ 290 public static Matcher<Class> inPackage(final Package targetPackage) { 291 return new InPackage(targetPackage); 292 } 293 294 private static class InPackage extends AbstractMatcher<Class> implements Serializable { 295 private final transient Package targetPackage; 296 private final String packageName; 297 298 public InPackage(Package targetPackage) { 299 this.targetPackage = checkNotNull(targetPackage, "package"); 300 this.packageName = targetPackage.getName(); 301 } 302 303 public boolean matches(Class c) { 304 return c.getPackage().equals(targetPackage); 305 } 306 307 @Override public boolean equals(Object other) { 308 return other instanceof InPackage 309 && ((InPackage) other).targetPackage.equals(targetPackage); 310 } 311 312 @Override public int hashCode() { 313 return 37 * targetPackage.hashCode(); 314 } 315 316 @Override public String toString() { 317 return "inPackage(" + targetPackage.getName() + ")"; 318 } 319 320 public Object readResolve() { 321 return inPackage(Package.getPackage(packageName)); 322 } 323 324 private static final long serialVersionUID = 0; 325 } 326 327 /** 328 * Returns a matcher which matches classes in the given package and its subpackages. Unlike 329 * {@link #inPackage(Package) inPackage()}, this matches classes from any classloader. 330 * 331 * @since 2.0 332 */ 333 public static Matcher<Class> inSubpackage(final String targetPackageName) { 334 return new InSubpackage(targetPackageName); 335 } 336 337 private static class InSubpackage extends AbstractMatcher<Class> implements Serializable { 338 private final String targetPackageName; 339 340 public InSubpackage(String targetPackageName) { 341 this.targetPackageName = targetPackageName; 342 } 343 344 public boolean matches(Class c) { 345 String classPackageName = c.getPackage().getName(); 346 return classPackageName.equals(targetPackageName) 347 || classPackageName.startsWith(targetPackageName + "."); 348 } 349 350 @Override public boolean equals(Object other) { 351 return other instanceof InSubpackage 352 && ((InSubpackage) other).targetPackageName.equals(targetPackageName); 353 } 354 355 @Override public int hashCode() { 356 return 37 * targetPackageName.hashCode(); 357 } 358 359 @Override public String toString() { 360 return "inSubpackage(" + targetPackageName + ")"; 361 } 362 363 private static final long serialVersionUID = 0; 364 } 365 366 /** 367 * Returns a matcher which matches methods with matching return types. 368 */ 369 public static Matcher<Method> returns( 370 final Matcher<? super Class<?>> returnType) { 371 return new Returns(returnType); 372 } 373 374 private static class Returns extends AbstractMatcher<Method> implements Serializable { 375 private final Matcher<? super Class<?>> returnType; 376 377 public Returns(Matcher<? super Class<?>> returnType) { 378 this.returnType = checkNotNull(returnType, "return type matcher"); 379 } 380 381 public boolean matches(Method m) { 382 return returnType.matches(m.getReturnType()); 383 } 384 385 @Override public boolean equals(Object other) { 386 return other instanceof Returns 387 && ((Returns) other).returnType.equals(returnType); 388 } 389 390 @Override public int hashCode() { 391 return 37 * returnType.hashCode(); 392 } 393 394 @Override public String toString() { 395 return "returns(" + returnType + ")"; 396 } 397 398 private static final long serialVersionUID = 0; 399 } 400 } 401