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 /** Returns a matcher which matches any input. */ any()38 public static Matcher<Object> any() { 39 return ANY; 40 } 41 42 private static final Matcher<Object> ANY = new Any(); 43 44 private static class Any extends AbstractMatcher<Object> implements Serializable { 45 @Override matches(Object o)46 public boolean matches(Object o) { 47 return true; 48 } 49 50 @Override toString()51 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 /** Inverts the given matcher. */ not(final Matcher<? super T> p)63 public static <T> Matcher<T> not(final Matcher<? super T> p) { 64 return new Not<T>(p); 65 } 66 67 private static class Not<T> extends AbstractMatcher<T> implements Serializable { 68 final Matcher<? super T> delegate; 69 Not(Matcher<? super T> delegate)70 private Not(Matcher<? super T> delegate) { 71 this.delegate = checkNotNull(delegate, "delegate"); 72 } 73 74 @Override matches(T t)75 public boolean matches(T t) { 76 return !delegate.matches(t); 77 } 78 79 @Override equals(Object other)80 public boolean equals(Object other) { 81 return other instanceof Not && ((Not) other).delegate.equals(delegate); 82 } 83 84 @Override hashCode()85 public int hashCode() { 86 return -delegate.hashCode(); 87 } 88 89 @Override toString()90 public String toString() { 91 return "not(" + delegate + ")"; 92 } 93 94 private static final long serialVersionUID = 0; 95 } 96 checkForRuntimeRetention(Class<? extends Annotation> annotationType)97 private static void checkForRuntimeRetention(Class<? extends Annotation> annotationType) { 98 Retention retention = annotationType.getAnnotation(Retention.class); 99 checkArgument( 100 retention != null && retention.value() == RetentionPolicy.RUNTIME, 101 "Annotation %s is missing RUNTIME retention", 102 annotationType.getSimpleName()); 103 } 104 105 /** Returns a matcher which matches elements (methods, classes, etc.) with a given annotation. */ annotatedWith( final Class<? extends Annotation> annotationType)106 public static Matcher<AnnotatedElement> annotatedWith( 107 final Class<? extends Annotation> annotationType) { 108 return new AnnotatedWithType(annotationType); 109 } 110 111 private static class AnnotatedWithType extends AbstractMatcher<AnnotatedElement> 112 implements Serializable { 113 private final Class<? extends Annotation> annotationType; 114 AnnotatedWithType(Class<? extends Annotation> annotationType)115 public AnnotatedWithType(Class<? extends Annotation> annotationType) { 116 this.annotationType = checkNotNull(annotationType, "annotation type"); 117 checkForRuntimeRetention(annotationType); 118 } 119 120 @Override matches(AnnotatedElement element)121 public boolean matches(AnnotatedElement element) { 122 return element.isAnnotationPresent(annotationType); 123 } 124 125 @Override equals(Object other)126 public boolean equals(Object other) { 127 return other instanceof AnnotatedWithType 128 && ((AnnotatedWithType) other).annotationType.equals(annotationType); 129 } 130 131 @Override hashCode()132 public int hashCode() { 133 return 37 * annotationType.hashCode(); 134 } 135 136 @Override toString()137 public String toString() { 138 return "annotatedWith(" + annotationType.getSimpleName() + ".class)"; 139 } 140 141 private static final long serialVersionUID = 0; 142 } 143 144 /** Returns a matcher which matches elements (methods, classes, etc.) with a given annotation. */ annotatedWith(final Annotation annotation)145 public static Matcher<AnnotatedElement> annotatedWith(final Annotation annotation) { 146 return new AnnotatedWith(annotation); 147 } 148 149 private static class AnnotatedWith extends AbstractMatcher<AnnotatedElement> 150 implements Serializable { 151 private final Annotation annotation; 152 AnnotatedWith(Annotation annotation)153 public AnnotatedWith(Annotation annotation) { 154 this.annotation = checkNotNull(annotation, "annotation"); 155 checkForRuntimeRetention(annotation.annotationType()); 156 } 157 158 @Override 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 164 @Override equals(Object other)165 public boolean equals(Object other) { 166 return other instanceof AnnotatedWith 167 && ((AnnotatedWith) other).annotation.equals(annotation); 168 } 169 170 @Override hashCode()171 public int hashCode() { 172 return 37 * annotation.hashCode(); 173 } 174 175 @Override toString()176 public String toString() { 177 return "annotatedWith(" + annotation + ")"; 178 } 179 180 private static final long serialVersionUID = 0; 181 } 182 183 /** Returns a matcher which matches subclasses of the given type (as well as the given type). */ 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> implements Serializable { 189 private final Class<?> superclass; 190 SubclassesOf(Class<?> superclass)191 public SubclassesOf(Class<?> superclass) { 192 this.superclass = checkNotNull(superclass, "superclass"); 193 } 194 195 @Override matches(Class subclass)196 public boolean matches(Class subclass) { 197 return superclass.isAssignableFrom(subclass); 198 } 199 200 @Override equals(Object other)201 public boolean equals(Object other) { 202 return other instanceof SubclassesOf && ((SubclassesOf) other).superclass.equals(superclass); 203 } 204 205 @Override hashCode()206 public int hashCode() { 207 return 37 * superclass.hashCode(); 208 } 209 210 @Override toString()211 public String toString() { 212 return "subclassesOf(" + superclass.getSimpleName() + ".class)"; 213 } 214 215 private static final long serialVersionUID = 0; 216 } 217 218 /** Returns a matcher which matches objects equal to the given object. */ 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> implements Serializable { 224 private final Object value; 225 Only(Object value)226 public Only(Object value) { 227 this.value = checkNotNull(value, "value"); 228 } 229 230 @Override matches(Object other)231 public boolean matches(Object other) { 232 return value.equals(other); 233 } 234 235 @Override equals(Object other)236 public boolean equals(Object other) { 237 return other instanceof Only && ((Only) other).value.equals(value); 238 } 239 240 @Override hashCode()241 public int hashCode() { 242 return 37 * value.hashCode(); 243 } 244 245 @Override toString()246 public String toString() { 247 return "only(" + value + ")"; 248 } 249 250 private static final long serialVersionUID = 0; 251 } 252 253 /** Returns a matcher which matches only the given object. */ 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> implements Serializable { 259 private final Object value; 260 IdenticalTo(Object value)261 public IdenticalTo(Object value) { 262 this.value = checkNotNull(value, "value"); 263 } 264 265 @Override matches(Object other)266 public boolean matches(Object other) { 267 return value == other; 268 } 269 270 @Override equals(Object other)271 public boolean equals(Object other) { 272 return other instanceof IdenticalTo && ((IdenticalTo) other).value == value; 273 } 274 275 @Override hashCode()276 public int hashCode() { 277 return 37 * System.identityHashCode(value); 278 } 279 280 @Override toString()281 public String toString() { 282 return "identicalTo(" + value + ")"; 283 } 284 285 private static final long serialVersionUID = 0; 286 } 287 288 /** 289 * Returns a matcher which matches classes in the given package. Packages are specific to their 290 * classloader, so classes with the same package name may not have the same package at runtime. 291 */ inPackage(final Package targetPackage)292 public static Matcher<Class> inPackage(final Package targetPackage) { 293 return new InPackage(targetPackage); 294 } 295 296 private static class InPackage extends AbstractMatcher<Class> implements Serializable { 297 private final transient Package targetPackage; 298 private final String packageName; 299 InPackage(Package targetPackage)300 public InPackage(Package targetPackage) { 301 this.targetPackage = checkNotNull(targetPackage, "package"); 302 this.packageName = targetPackage.getName(); 303 } 304 305 @Override matches(Class c)306 public boolean matches(Class c) { 307 return c.getPackage().equals(targetPackage); 308 } 309 310 @Override equals(Object other)311 public boolean equals(Object other) { 312 return other instanceof InPackage && ((InPackage) other).targetPackage.equals(targetPackage); 313 } 314 315 @Override hashCode()316 public int hashCode() { 317 return 37 * targetPackage.hashCode(); 318 } 319 320 @Override toString()321 public String toString() { 322 return "inPackage(" + targetPackage.getName() + ")"; 323 } 324 readResolve()325 public Object readResolve() { 326 return inPackage(Package.getPackage(packageName)); 327 } 328 329 private static final long serialVersionUID = 0; 330 } 331 332 /** 333 * Returns a matcher which matches classes in the given package and its subpackages. Unlike {@link 334 * #inPackage(Package) inPackage()}, this matches classes from any classloader. 335 * 336 * @since 2.0 337 */ inSubpackage(final String targetPackageName)338 public static Matcher<Class> inSubpackage(final String targetPackageName) { 339 return new InSubpackage(targetPackageName); 340 } 341 342 private static class InSubpackage extends AbstractMatcher<Class> implements Serializable { 343 private final String targetPackageName; 344 InSubpackage(String targetPackageName)345 public InSubpackage(String targetPackageName) { 346 this.targetPackageName = targetPackageName; 347 } 348 349 @Override matches(Class c)350 public boolean matches(Class c) { 351 String classPackageName = c.getPackage().getName(); 352 return classPackageName.equals(targetPackageName) 353 || classPackageName.startsWith(targetPackageName + "."); 354 } 355 356 @Override equals(Object other)357 public boolean equals(Object other) { 358 return other instanceof InSubpackage 359 && ((InSubpackage) other).targetPackageName.equals(targetPackageName); 360 } 361 362 @Override hashCode()363 public int hashCode() { 364 return 37 * targetPackageName.hashCode(); 365 } 366 367 @Override toString()368 public String toString() { 369 return "inSubpackage(" + targetPackageName + ")"; 370 } 371 372 private static final long serialVersionUID = 0; 373 } 374 375 /** Returns a matcher which matches methods with matching return types. */ returns(final Matcher<? super Class<?>> returnType)376 public static Matcher<Method> returns(final Matcher<? super Class<?>> returnType) { 377 return new Returns(returnType); 378 } 379 380 private static class Returns extends AbstractMatcher<Method> implements Serializable { 381 private final Matcher<? super Class<?>> returnType; 382 Returns(Matcher<? super Class<?>> returnType)383 public Returns(Matcher<? super Class<?>> returnType) { 384 this.returnType = checkNotNull(returnType, "return type matcher"); 385 } 386 387 @Override matches(Method m)388 public boolean matches(Method m) { 389 return returnType.matches(m.getReturnType()); 390 } 391 392 @Override equals(Object other)393 public boolean equals(Object other) { 394 return other instanceof Returns && ((Returns) other).returnType.equals(returnType); 395 } 396 397 @Override hashCode()398 public int hashCode() { 399 return 37 * returnType.hashCode(); 400 } 401 402 @Override toString()403 public String toString() { 404 return "returns(" + returnType + ")"; 405 } 406 407 private static final long serialVersionUID = 0; 408 } 409 } 410