Home | History | Annotate | Download | only in matcher
      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