Not all reflection is bad
| Code Block |
|---|
public class Reflectomatic {
public static List<Field> allFieldsIn(Class<?> declaringClass, Matcher<? super Field> fieldMatcher) {
List<Field> fields = new ArrayList<Field>();
collectFields(declaringClass, fieldMatcher, fields);
return fields;
}
public static Field fieldIn(Class<?> declaringClass, Matcher<? super Field> fieldMatcher) {
List<Field> fields = new ArrayList<Field>();
collectFields(declaringClass, fieldMatcher, fields);
if (fields.size() != 1) {
throw new Defect("Wanted a single field, but got " + fields.size());
}
return fields.get(0);
}
private static void collectFields(Class<?> type, Matcher<? super Field> fieldMatcher, List<Field> collection) {
if (type != Object.class) {
for (Field field : type.getDeclaredFields()) {
if (fieldMatcher.matches(field)) {
collection.add(field);
}
}
collectFields(type.getSuperclass(), fieldMatcher, collection);
}
}
public static List<Method> allMethodsIn(Class<?> declaringClass, Matcher<? super Method> methodMatcher) {
List<Method> methods = new ArrayList<Method>();
collectMethods(declaringClass, methodMatcher, methods);
return methods;
}
public static List<Method> allDeclaredMethodsIn(Class<?> declaringClass, Matcher<? super Method> methodMatcher) {
List<Method> methods = new ArrayList<Method>();
collectDeclaredMethods(declaringClass, methodMatcher, methods);
return methods;
}
private static void collectMethods(Class<?> type, Matcher<? super Method> methodMatcher, List<Method> collection) {
if (type != Object.class) {
collectDeclaredMethods(type, methodMatcher, collection);
collectMethods(type.getSuperclass(), methodMatcher, collection);
}
}
private static void collectDeclaredMethods(Class<?> type, Matcher<? super Method> methodMatcher, List<Method> collection) {
for (Method method : type.getDeclaredMethods()) {
if (methodMatcher.matches(method)) {
collection.add(method);
}
}
}
public static Set<Class> allInterfacesDeclaredBy(Object object) {
Set<Class> allInterfaces = com.google.common.collect.Sets.newHashSet();
Class type = object.getClass();
while (type != Object.class) {
Class[] interfaces = type.getInterfaces();
allInterfaces.addAll(Arrays.asList(interfaces));
type = type.getSuperclass();
}
return allInterfaces;
}
public static Matcher<Member> isPublic() {
return hasModifiers(java.lang.reflect.Modifier.PUBLIC);
}
public static Matcher<Member> hasModifiers(final int expectedModifiers) {
return new TypeSafeMatcher<Member>() {
public boolean matchesSafely(Member member) {
return (member.getModifiers() & expectedModifiers) != 0;
}
public void describeTo(Description description) {
description.appendText("a public member");
}
};
}
public static Matcher<Member> isStatic() {
return hasModifiers(Modifier.STATIC);
}
public static <T> Constructor<?> findConstructor(Class<T> type, Class<?>... argTypes) {
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
if (Arrays.equals(constructor.getParameterTypes(), argTypes)) {
return constructor;
}
}
throw new IllegalArgumentException("no matching constructor found");
}
private static Map<Class<?>, Class<?>> boxes = new HashMap<Class<?>, Class<?>>() {{
put(boolean.class, Boolean.class);
put(char.class, Character.class);
put(short.class, Short.class);
put(int.class, Integer.class);
put(long.class, Long.class);
put(float.class, Float.class);
put(double.class, Double.class);
}};
@SuppressWarnings("unchecked")
public static <T> Class<T> boxedTypeFor(Class<T> type) {
if (boxes.containsKey(type)) {
return (Class<T>) boxes.get(type);
} else {
return type;
}
}
public static void inject(Object thingToInjectInto, Object thingToInject, String fieldName) throws IllegalAccessException {
Field field = Reflectomatic.allFieldsIn(thingToInjectInto.getClass(), ReflectionMatchers.fieldNamed(org.hamcrest.Matchers.equalTo(fieldName))).get(0);
field.setAccessible(true);
field.set(thingToInjectInto, thingToInject);
}
public static void copyFields(Object to, Object from) {
for (Field f : allFieldsIn(to.getClass(), not(finalField()))) {
f.setAccessible(true);
try {
f.set(to, f.get(from));
} catch (IllegalAccessException e) {
throw new Defect("could not set accessible field", e);
}
}
}
}
|
And some matchers that match that....
| Code Block |
|---|
public class ReflectionMatchers {
public static <T> Matcher<T> samePublicFieldsAs(final T example) {
return new TypeSafeMatcher<T>() {
Object actualValue;
Object exampleValue;
Field currentField;
public boolean matchesSafely(Object o) {
if (example.getClass() != o.getClass()) {
return false;
}
for (Field field : Reflectomatic.allFieldsIn(example.getClass(), Reflectomatic.isPublic())) {
try {
exampleValue = field.get(example);
actualValue = field.get(o);
currentField = field;
if (!safeEquals(exampleValue, actualValue)) return false;
} catch (IllegalAccessException e) {
throw new Defect("could not get value of public field");
}
}
return true;
}
public void describeTo(Description description) {
description.appendText("Not all public fields matched, " +
" for field '" + currentField.getName() + "'" +
"\n\t\texpected:").appendValue(exampleValue)
.appendText("\n\t\tbut got: ").appendValue(actualValue);
}
};
}
private static boolean safeEquals(Object exampleValue, Object actualValue) {
return (exampleValue == null && actualValue == null)
|| (exampleValue != null && exampleValue.equals(actualValue));
}
public static Matcher<? super Field> fieldNamed(final Matcher<? super String> matcher) {
return new TypeSafeMatcher<Field>() {
public boolean matchesSafely(Field field) {
return matcher.matches(field.getName());
}
public void describeTo(Description description) {
description.appendText("Field named ");
description.appendDescriptionOf(matcher);
}
};
}
public static Matcher<? super Field> primitiveField() {
return new TypeSafeMatcher<Field>() {
public boolean matchesSafely(Field field) {
return field.getDeclaringClass().isPrimitive();
}
public void describeTo(Description description) {
description.appendText("Field is primitive");
}
};
}
public static Matcher<? super Field> finalField() {
return new TypeSafeMatcher<Field>() {
public boolean matchesSafely(Field field) {
return Modifier.isFinal(field.getModifiers());
}
public void describeTo(Description description) {
description.appendText("Field is final");
}
};
}
public static Matcher<? super Method> methodNamed(final Matcher<String> matcher) {
return new TypeSafeMatcher<Method>() {
public boolean matchesSafely(Method method) {
return matcher.matches(method.getName());
}
public void describeTo(Description description) {
description.appendText("Method named ");
description.appendDescriptionOf(matcher);
}
};
}
public static Matcher<? super Field> nonStaticField() {
return new TypeSafeMatcher<Field>() {
@Override
public boolean matchesSafely(Field field) {
return !Modifier.isStatic(field.getModifiers());
}
public void describeTo(Description description) {
description.appendText("Non static fields");
}
};
}
public static Matcher<? super Field> isNull(final Object o) {
return new TypeSafeMatcher<Field>() {
@Override
public boolean matchesSafely(Field field) {
try {
return field.get(o) == null;
} catch (IllegalAccessException e) {
throw new Defect("Should not happen", e);
}
}
public void describeTo(Description description) {
description.appendText("Null fields");
}
};
}
}
|