diff --git a/src/main/java/com/shazam/shazamcrest/BeanFinder.java b/src/main/java/com/shazam/shazamcrest/BeanFinder.java index 6d1bfa1..20c6ac6 100644 --- a/src/main/java/com/shazam/shazamcrest/BeanFinder.java +++ b/src/main/java/com/shazam/shazamcrest/BeanFinder.java @@ -9,13 +9,16 @@ */ package com.shazam.shazamcrest; -import static java.util.Arrays.asList; - import java.lang.reflect.Field; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; +import static java.lang.reflect.Modifier.isProtected; +import static java.lang.reflect.Modifier.isPublic; +import static java.util.Arrays.asList; + /** * Returns the object corresponding to the path specified */ @@ -30,7 +33,7 @@ public static Object findBeanAt(String fieldPath, Object object) { } private static Object findBeanAt(List fields, Object object) { - for (Field field : object.getClass().getDeclaredFields()) { + for (Field field : getAllFields(object.getClass())) { field.setAccessible(true); if (headOf(fields).equals(field.getName())) { try { @@ -42,11 +45,34 @@ private static Object findBeanAt(List fields, Object object) { } catch (IllegalAccessException e) {} } } - + throw new IllegalArgumentException(); } private static String headOf(final Collection paths) { return paths.iterator().next(); } + + private static List getAllFields(Class type) { + List allFields = new LinkedList(); + for (Class clazz = type; clazz != null; clazz = clazz.getSuperclass()) { + List fields = new LinkedList(); + for (Field field : clazz.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (fieldIsVisibleInType(clazz, modifiers, type)) { + fields.add(field); + } + } + allFields.addAll(fields); + } + return allFields; + } + + private static boolean fieldIsVisibleInType(Class clazz, int modifiers, Class type) { + return isProtected(modifiers) || isPublic(modifiers) || isDefault(modifiers) || clazz.equals(type); + } + + private static boolean isDefault(int modifiers) { + return modifiers == 0x0; + } } diff --git a/src/test/java/com/shazam/shazamcrest/CustomMatcherOnInheritedFieldTest.java b/src/test/java/com/shazam/shazamcrest/CustomMatcherOnInheritedFieldTest.java new file mode 100644 index 0000000..2b372d2 --- /dev/null +++ b/src/test/java/com/shazam/shazamcrest/CustomMatcherOnInheritedFieldTest.java @@ -0,0 +1,50 @@ +package com.shazam.shazamcrest; + +import org.junit.Test; + +import static com.shazam.shazamcrest.MatcherAssert.assertThat; +import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs; +import static org.hamcrest.Matchers.startsWith; + +public class CustomMatcherOnInheritedFieldTest { + + @Test + public void inheritedFieldsAreMatchedByCustomMatchers() { + assertThat(new ChildClass(), sameBeanAs(new ChildClass()) + .with("packageProtectedField", startsWith("inherit")) + .with("protectedField", startsWith("inherit")) + .with("publicField", startsWith("inherit")) + ); + } + + @Test + public void notInheritedFieldsAreMatchedByCustomMatchers() { + assertThat(new ChildClass(), sameBeanAs(new ChildClass()) + .with("childField", startsWith("child")) + ); + } + + @Test(expected = IllegalArgumentException.class) + public void inheritedPrivateFieldsCanNotBeMatched() { + assertThat(new ChildClass(), sameBeanAs(new ChildClass()) + .with("privateField", startsWith("foo")) + ); + } + + @SuppressWarnings("unused") + private class GrandParentClass { + private String privateField = "not inherited"; + String packageProtectedField = "inherited"; + } + + @SuppressWarnings("unused") + private class ParentClass extends GrandParentClass { + protected String protectedField = "inherited"; + public String publicField = "inherited"; + } + + @SuppressWarnings("unused") + private class ChildClass extends ParentClass { + private String childField = "child field"; + } +}