Mike Ward's inSubpackage feature for issue 75.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@531 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/matcher/Matchers.java b/src/com/google/inject/matcher/Matchers.java
index d4643ea..32c1a2e 100644
--- a/src/com/google/inject/matcher/Matchers.java
+++ b/src/com/google/inject/matcher/Matchers.java
@@ -16,13 +16,14 @@
package com.google.inject.matcher;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
-import static com.google.common.base.Preconditions.checkNotNull;
/**
* Matcher implementations. Supports matching classes and methods.
@@ -90,10 +91,8 @@
private static void checkForRuntimeRetention(
Class<? extends Annotation> annotationType) {
Retention retention = annotationType.getAnnotation(Retention.class);
- if (retention == null || retention.value() != RetentionPolicy.RUNTIME) {
- throw new IllegalArgumentException("Annotation "
- + annotationType.getSimpleName() + " is missing RUNTIME retention");
- }
+ checkArgument(retention != null && retention.value() == RetentionPolicy.RUNTIME,
+ "Annotation " + annotationType.getSimpleName() + " is missing RUNTIME retention");
}
/**
@@ -270,7 +269,8 @@
}
/**
- * Returns a matcher which matches classes in the given package.
+ * Returns a matcher which matches classes in the given package. Packages are specific to their
+ * classloader, so classes with the same package name may not have the same package at runtime.
*/
public static Matcher<Class> inPackage(final Package targetPackage) {
return new InPackage(targetPackage);
@@ -308,6 +308,41 @@
}
/**
+ * Returns a matcher which matches classes in the given package and its subpackages. Unlike
+ * {@link #inPackage(Package) inPackage()}, this matches classes from any classloader.
+ */
+ public static Matcher<Class> inSubpackage(final String targetPackageName) {
+ return new InSubpackage(targetPackageName);
+ }
+
+ private static class InSubpackage extends AbstractMatcher<Class> implements Serializable {
+ private final String targetPackageName;
+
+ public InSubpackage(String targetPackageName) {
+ this.targetPackageName = targetPackageName;
+ }
+
+ public boolean matches(Class c) {
+ String classPackageName = c.getPackage().getName();
+ return classPackageName.equals(targetPackageName)
+ || classPackageName.startsWith(targetPackageName + ".");
+ }
+
+ @Override public boolean equals(Object other) {
+ return other instanceof InSubpackage
+ && ((InSubpackage) other).targetPackageName.equals(targetPackageName);
+ }
+
+ @Override public int hashCode() {
+ return 37 * targetPackageName.hashCode();
+ }
+
+ @Override public String toString() {
+ return "inSubpackage(" + targetPackageName + ")";
+ }
+ }
+
+ /**
* Returns a matcher which matches methods with matching return types.
*/
public static Matcher<Method> returns(
@@ -339,4 +374,4 @@
return "returns(" + returnType + ")";
}
}
-}
\ No newline at end of file
+}
diff --git a/test/com/google/inject/matcher/MatcherTest.java b/test/com/google/inject/matcher/MatcherTest.java
index bc6a3d8..8b3ac98 100644
--- a/test/com/google/inject/matcher/MatcherTest.java
+++ b/test/com/google/inject/matcher/MatcherTest.java
@@ -18,16 +18,23 @@
import static com.google.inject.Asserts.assertEqualWhenReserialized;
import static com.google.inject.Asserts.assertEqualsBothWays;
-import static com.google.inject.matcher.Matchers.*;
+import static com.google.inject.matcher.Matchers.annotatedWith;
+import static com.google.inject.matcher.Matchers.any;
+import static com.google.inject.matcher.Matchers.identicalTo;
+import static com.google.inject.matcher.Matchers.inPackage;
+import static com.google.inject.matcher.Matchers.inSubpackage;
+import static com.google.inject.matcher.Matchers.not;
+import static com.google.inject.matcher.Matchers.only;
+import static com.google.inject.matcher.Matchers.returns;
+import static com.google.inject.matcher.Matchers.subclassesOf;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
-import junit.framework.TestCase;
-
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.AbstractList;
+import junit.framework.TestCase;
/**
* @author crazybob@google.com (Bob Lee)
@@ -115,6 +122,17 @@
assertFalse(inPackage(matchersPackage).equals(inPackage(Object.class.getPackage())));
}
+ public void testInSubpackage() {
+ String stringPackageName = String.class.getPackage().getName();
+ assertEquals("inSubpackage(java.lang)", inSubpackage(stringPackageName).toString());
+ assertTrue(inSubpackage(stringPackageName).matches(Object.class));
+ assertTrue(inSubpackage(stringPackageName).matches(Method.class));
+ assertFalse(inSubpackage(stringPackageName).matches(Matchers.class));
+ assertFalse(inSubpackage("jav").matches(Object.class));
+ assertEqualsBothWays(inSubpackage(stringPackageName), inSubpackage(stringPackageName));
+ assertFalse(inSubpackage(stringPackageName).equals(inSubpackage(Matchers.class.getPackage().getName())));
+ }
+
public void testReturns() throws NoSuchMethodException {
Matcher<Method> predicate = returns(only(String.class));
assertTrue(predicate.matches(
@@ -134,6 +152,7 @@
assertEqualWhenReserialized(only("foo"));
assertEqualWhenReserialized(identicalTo(Object.class));
assertEqualWhenReserialized(inPackage(String.class.getPackage()));
+ assertEqualWhenReserialized(inSubpackage(String.class.getPackage().getName()));
assertEqualWhenReserialized(returns(any()));
assertEqualWhenReserialized(subclassesOf(AbstractList.class));
assertEqualWhenReserialized(only("a").or(only("b")));