001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.bm.ejb3guice.matcher;
016:
017: import com.bm.ejb3guice.internal.Objects;
018:
019: import java.lang.annotation.Annotation;
020: import java.lang.annotation.Retention;
021: import java.lang.annotation.RetentionPolicy;
022: import java.lang.reflect.AnnotatedElement;
023: import java.lang.reflect.Method;
024:
025: /**
026: * Matcher implementations. Supports matching classes and methods.
027: *
028: * @author crazybob@google.com (Bob Lee)
029: */
030: public class Matchers {
031:
032: private Matchers() {
033: }
034:
035: static Matcher<Object> ANY = new AbstractMatcher<Object>() {
036: public boolean matches(Object o) {
037: return true;
038: }
039:
040: public String toString() {
041: return "any()";
042: }
043: };
044:
045: /**
046: * Returns a matcher which matches any input.
047: */
048: public static Matcher<Object> any() {
049: return ANY;
050: }
051:
052: /**
053: * Inverts the given matcher.
054: */
055: public static <T> Matcher<T> not(final Matcher<? super T> p) {
056: Objects.nonNull(p, "p");
057: return new AbstractMatcher<T>() {
058: public boolean matches(T t) {
059: return !p.matches(t);
060: }
061:
062: public String toString() {
063: return "not(" + p + ")";
064: }
065: };
066: }
067:
068: private static void checkForRuntimeRetention(
069: Class<? extends Annotation> annotationType) {
070: Retention retention = annotationType
071: .getAnnotation(Retention.class);
072: if (retention == null
073: || retention.value() != RetentionPolicy.RUNTIME) {
074: throw new IllegalArgumentException("Annotation "
075: + annotationType.getSimpleName()
076: + " is missing RUNTIME retention");
077: }
078: }
079:
080: /**
081: * Returns a matcher which matches elements (methods, classes, etc.)
082: * with a given annotation.
083: */
084: public static Matcher<AnnotatedElement> annotatedWith(
085: final Class<? extends Annotation> annotationType) {
086: Objects.nonNull(annotationType, "annotation type");
087: checkForRuntimeRetention(annotationType);
088: return new AbstractMatcher<AnnotatedElement>() {
089: public boolean matches(AnnotatedElement element) {
090: Annotation annotation = element
091: .getAnnotation(annotationType);
092: return annotation != null;
093: }
094:
095: public String toString() {
096: return "annotatedWith("
097: + annotationType.getSimpleName() + ".class)";
098: }
099: };
100: }
101:
102: /**
103: * Returns a matcher which matches elements (methods, classes, etc.)
104: * with a given annotation.
105: */
106: public static Matcher<AnnotatedElement> annotatedWith(
107: final Annotation annotation) {
108: Objects.nonNull(annotation, "annotation");
109: checkForRuntimeRetention(annotation.annotationType());
110: return new AbstractMatcher<AnnotatedElement>() {
111: public boolean matches(AnnotatedElement element) {
112: Annotation fromElement = element
113: .getAnnotation(annotation.annotationType());
114: return fromElement != null
115: && annotation.equals(fromElement);
116: }
117:
118: public String toString() {
119: return "annotatedWith(" + annotation + ")";
120: }
121: };
122: }
123:
124: /**
125: * Returns a matcher which matches subclasses of the given type (as well as
126: * the given type).
127: */
128: public static Matcher<Class> subclassesOf(final Class<?> super class) {
129: Objects.nonNull(super class, "superclass");
130: return new AbstractMatcher<Class>() {
131: public boolean matches(Class subclass) {
132: return super class.isAssignableFrom(subclass);
133: }
134:
135: public String toString() {
136: return "subclassesOf(" + super class.getSimpleName()
137: + ".class)";
138: }
139: };
140: }
141:
142: /**
143: * Returns a matcher which matches objects equal to the given object.
144: */
145: public static Matcher<Object> only(final Object o) {
146: Objects.nonNull(o, "o");
147: return new AbstractMatcher<Object>() {
148: public boolean matches(Object other) {
149: return o.equals(other);
150: }
151:
152: public String toString() {
153: return "only(" + o + ")";
154: }
155: };
156: }
157:
158: /**
159: * Returns a matcher which matches only the given object.
160: */
161: public static Matcher<Object> identicalTo(final Object o) {
162: Objects.nonNull(o, "o");
163: return new AbstractMatcher<Object>() {
164: public boolean matches(Object other) {
165: return o == other;
166: }
167:
168: public String toString() {
169: return "identicalTo(" + o + ")";
170: }
171: };
172: }
173:
174: /**
175: * Returns a matcher which matches classes in the given package.
176: */
177: public static Matcher<Class> inPackage(final Package p) {
178: Objects.nonNull(p, "package");
179: return new AbstractMatcher<Class>() {
180: public boolean matches(Class c) {
181: return c.getPackage().equals(p);
182: }
183:
184: public String toString() {
185: return "package(" + p.getName() + ")";
186: }
187: };
188: }
189:
190: /**
191: * Returns a matcher which matches methods with matching return types.
192: */
193: public static Matcher<Method> returns(
194: final Matcher<? super Class<?>> returnType) {
195: Objects.nonNull(returnType, "return type matcher");
196: return new AbstractMatcher<Method>() {
197: public boolean matches(Method m) {
198: return returnType.matches(m.getReturnType());
199: }
200:
201: public String toString() {
202: return "returns(" + returnType + ")";
203: }
204: };
205: }
206: }
|