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@google.com (Bob Lee) 33 */ 34 public class Matchers { Matchers()35 private Matchers() {} 36 37 /** 38 * Returns a matcher which matches any input. 39 */ any()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 { matches(Object o)47 public boolean matches(Object o) { 48 return true; 49 } 50 toString()51 @Override public String toString() { 52 return "any()"; 53 } 54 readResolve()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 */ not(final Matcher<? super T> p)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 Not(Matcher<? super T> delegate)72 private Not(Matcher<? super T> delegate) { 73 this.delegate = checkNotNull(delegate, "delegate"); 74 } 75 matches(T t)76 public boolean matches(T t) { 77 return !delegate.matches(t); 78 } 79 equals(Object other)80 @Override public boolean equals(Object other) { 81 return other instanceof Not 82 && ((Not) other).delegate.equals(delegate); 83 } 84 hashCode()85 @Override public int hashCode() { 86 return -delegate.hashCode(); 87 } 88 toString()89 @Override public String toString() { 90 return "not(" + delegate + ")"; 91 } 92 93 private static final long serialVersionUID = 0; 94 } 95 checkForRuntimeRetention( Class<? extends Annotation> annotationType)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 */ annotatedWith( final Class<? extends Annotation> annotationType)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 AnnotatedWithType(Class<? extends Annotation> annotationType)116 public AnnotatedWithType(Class<? extends Annotation> annotationType) { 117 this.annotationType = checkNotNull(annotationType, "annotation type"); 118 checkForRuntimeRetention(annotationType); 119 } 120 matches(AnnotatedElement element)121 public boolean matches(AnnotatedElement element) { 122 return element.isAnnotationPresent(annotationType); 123 } 124 equals(Object other)125 @Override public boolean equals(Object other) { 126 return other instanceof AnnotatedWithType 127 && ((AnnotatedWithType) other).annotationType.equals(annotationType); 128 } 129 hashCode()130 @Override public int hashCode() { 131 return 37 * annotationType.hashCode(); 132 } 133 toString()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 */ annotatedWith( final Annotation annotation)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 AnnotatedWith(Annotation annotation)154 public AnnotatedWith(Annotation annotation) { 155 this.annotation = checkNotNull(annotation, "annotation"); 156 checkForRuntimeRetention(annotation.annotationType()); 157 } 158 matches(AnnotatedElement element)159 public boolean matches(AnnotatedElement element) { 160 Annotation fromElement = element.getAnnotation(annotation.annotationType()); 161 return fromElement != null && annotation.equals(fromElement); 162 } 163 equals(Object other)164 @Override public boolean equals(Object other) { 165 return other instanceof AnnotatedWith 166 && ((AnnotatedWith) other).annotation.equals(annotation); 167 } 168 hashCode()169 @Override public int hashCode() { 170 return 37 * annotation.hashCode(); 171 } 172 toString()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 */ subclassesOf(final Class<?> superclass)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 SubclassesOf(Class<?> superclass)192 public SubclassesOf(Class<?> superclass) { 193 this.superclass = checkNotNull(superclass, "superclass"); 194 } 195 matches(Class subclass)196 public boolean matches(Class subclass) { 197 return superclass.isAssignableFrom(subclass); 198 } 199 equals(Object other)200 @Override public boolean equals(Object other) { 201 return other instanceof SubclassesOf 202 && ((SubclassesOf) other).superclass.equals(superclass); 203 } 204 hashCode()205 @Override public int hashCode() { 206 return 37 * superclass.hashCode(); 207 } 208 toString()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 */ only(Object value)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 Only(Object value)227 public Only(Object value) { 228 this.value = checkNotNull(value, "value"); 229 } 230 matches(Object other)231 public boolean matches(Object other) { 232 return value.equals(other); 233 } 234 equals(Object other)235 @Override public boolean equals(Object other) { 236 return other instanceof Only 237 && ((Only) other).value.equals(value); 238 } 239 hashCode()240 @Override public int hashCode() { 241 return 37 * value.hashCode(); 242 } 243 toString()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 */ identicalTo(final Object value)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 IdenticalTo(Object value)262 public IdenticalTo(Object value) { 263 this.value = checkNotNull(value, "value"); 264 } 265 matches(Object other)266 public boolean matches(Object other) { 267 return value == other; 268 } 269 equals(Object other)270 @Override public boolean equals(Object other) { 271 return other instanceof IdenticalTo 272 && ((IdenticalTo) other).value == value; 273 } 274 hashCode()275 @Override public int hashCode() { 276 return 37 * System.identityHashCode(value); 277 } 278 toString()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 */ inPackage(final Package targetPackage)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 InPackage(Package targetPackage)298 public InPackage(Package targetPackage) { 299 this.targetPackage = checkNotNull(targetPackage, "package"); 300 this.packageName = targetPackage.getName(); 301 } 302 matches(Class c)303 public boolean matches(Class c) { 304 return c.getPackage().equals(targetPackage); 305 } 306 equals(Object other)307 @Override public boolean equals(Object other) { 308 return other instanceof InPackage 309 && ((InPackage) other).targetPackage.equals(targetPackage); 310 } 311 hashCode()312 @Override public int hashCode() { 313 return 37 * targetPackage.hashCode(); 314 } 315 toString()316 @Override public String toString() { 317 return "inPackage(" + targetPackage.getName() + ")"; 318 } 319 readResolve()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 */ inSubpackage(final String targetPackageName)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 InSubpackage(String targetPackageName)340 public InSubpackage(String targetPackageName) { 341 this.targetPackageName = targetPackageName; 342 } 343 matches(Class c)344 public boolean matches(Class c) { 345 String classPackageName = c.getPackage().getName(); 346 return classPackageName.equals(targetPackageName) 347 || classPackageName.startsWith(targetPackageName + "."); 348 } 349 equals(Object other)350 @Override public boolean equals(Object other) { 351 return other instanceof InSubpackage 352 && ((InSubpackage) other).targetPackageName.equals(targetPackageName); 353 } 354 hashCode()355 @Override public int hashCode() { 356 return 37 * targetPackageName.hashCode(); 357 } 358 toString()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 */ returns( final Matcher<? super Class<?>> returnType)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 Returns(Matcher<? super Class<?>> returnType)377 public Returns(Matcher<? super Class<?>> returnType) { 378 this.returnType = checkNotNull(returnType, "return type matcher"); 379 } 380 matches(Method m)381 public boolean matches(Method m) { 382 return returnType.matches(m.getReturnType()); 383 } 384 equals(Object other)385 @Override public boolean equals(Object other) { 386 return other instanceof Returns 387 && ((Returns) other).returnType.equals(returnType); 388 } 389 hashCode()390 @Override public int hashCode() { 391 return 37 * returnType.hashCode(); 392 } 393 toString()394 @Override public String toString() { 395 return "returns(" + returnType + ")"; 396 } 397 398 private static final long serialVersionUID = 0; 399 } 400 } 401