Skip to content

Commit

Permalink
[#37] Reflect.type() on instance fails for null field
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaseder committed May 3, 2017
1 parent 2b50894 commit 846beee
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 40 deletions.
76 changes: 36 additions & 40 deletions jOOR/src/main/java/org/joor/Reflect.java
Expand Up @@ -107,7 +107,11 @@ public static Reflect on(Class<?> clazz) {
* @return A wrapped object, to be used for further reflection.
*/
public static Reflect on(Object object) {
return new Reflect(object);
return new Reflect(object == null ? Object.class : object.getClass(), object);
}

private static Reflect on(Class<?> type, Object object) {
return new Reflect(type, object);
}

/**
Expand Down Expand Up @@ -163,29 +167,26 @@ public static <T extends AccessibleObject> T accessible(T accessible) {
/* [/java-8] */

/**
* The wrapped object
* The type of the wrapped object.
*/
private final Object object;
private final Class<?> type;

/**
* A flag indicating whether the wrapped object is a {@link Class} (for
* accessing static fields and methods), or any other type of {@link Object}
* (for accessing instance fields and methods).
* The wrapped object.
*/
private final boolean isClass;
private final Object object;

// ---------------------------------------------------------------------
// Constructors
// ---------------------------------------------------------------------

private Reflect(Class<?> type) {
this.object = type;
this.isClass = true;
this(type, type);
}

private Reflect(Object object) {
private Reflect(Class<?> type, Object object) {
this.type = type;
this.object = object;
this.isClass = false;
}

// ---------------------------------------------------------------------
Expand Down Expand Up @@ -280,32 +281,32 @@ public <T> T get(String name) throws ReflectException {
public Reflect field(String name) throws ReflectException {
try {
Field field = field0(name);
return on(field.get(object));
return on(field.getType(), field.get(object));
}
catch (Exception e) {
throw new ReflectException(e);
}
}

private Field field0(String name) throws ReflectException {
Class<?> type = type();
Class<?> t = type();

// Try getting a public field
try {
return accessible(type.getField(name));
return accessible(t.getField(name));
}

// Try again, getting a non-public field
catch (NoSuchFieldException e) {
do {
try {
return accessible(type.getDeclaredField(name));
return accessible(t.getDeclaredField(name));
}
catch (NoSuchFieldException ignore) {}

type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);

throw new ReflectException(e);
}
Expand All @@ -328,21 +329,21 @@ private Field field0(String name) throws ReflectException {
*/
public Map<String, Reflect> fields() {
Map<String, Reflect> result = new LinkedHashMap<String, Reflect>();
Class<?> type = type();
Class<?> t = type();

do {
for (Field field : type.getDeclaredFields()) {
if (!isClass ^ Modifier.isStatic(field.getModifiers())) {
for (Field field : t.getDeclaredFields()) {
if (type != object ^ Modifier.isStatic(field.getModifiers())) {
String name = field.getName();

if (!result.containsKey(name))
result.put(name, field(name));
}
}

type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);

return result;
}
Expand Down Expand Up @@ -431,24 +432,24 @@ public Reflect call(String name, Object... args) throws ReflectException {
* If no exact match could be found, we let the {@code NoSuchMethodException} pass through.
*/
private Method exactMethod(String name, Class<?>[] types) throws NoSuchMethodException {
Class<?> type = type();
Class<?> t = type();

// first priority: find a public method with exact signature match in class hierarchy
try {
return type.getMethod(name, types);
return t.getMethod(name, types);
}

// second priority: find a private method with exact signature match on declaring class
catch (NoSuchMethodException e) {
do {
try {
return type.getDeclaredMethod(name, types);
return t.getDeclaredMethod(name, types);
}
catch (NoSuchMethodException ignore) {}

type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);

throw new NoSuchMethodException();
}
Expand All @@ -463,27 +464,27 @@ private Method exactMethod(String name, Class<?>[] types) throws NoSuchMethodExc
* returned, otherwise a {@code NoSuchMethodException} is thrown.
*/
private Method similarMethod(String name, Class<?>[] types) throws NoSuchMethodException {
Class<?> type = type();
Class<?> t = type();

// first priority: find a public method with a "similar" signature in class hierarchy
// similar interpreted in when primitive argument types are converted to their wrappers
for (Method method : type.getMethods()) {
for (Method method : t.getMethods()) {
if (isSimilarSignature(method, name, types)) {
return method;
}
}

// second priority: find a non-public method with a "similar" signature on declaring class
do {
for (Method method : type.getDeclaredMethods()) {
for (Method method : t.getDeclaredMethods()) {
if (isSimilarSignature(method, name, types)) {
return method;
}
}

type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);

throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + ".");
}
Expand Down Expand Up @@ -577,7 +578,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl

// Actual method name matches always come first
try {
return on(object).call(name, args).get();
return on(type, object).call(name, args).get();
}

// [#14] Emulate POJO behaviour on wrapped map objects
Expand Down Expand Up @@ -697,7 +698,7 @@ public String toString() {
*/
private static Reflect on(Constructor<?> constructor, Object... args) throws ReflectException {
try {
return on(accessible(constructor).newInstance(args));
return on(constructor.getDeclaringClass(), accessible(constructor).newInstance(args));
}
catch (Exception e) {
throw new ReflectException(e);
Expand Down Expand Up @@ -784,12 +785,7 @@ private static Class<?> forName(String name, ClassLoader classLoader) throws Ref
* @see Object#getClass()
*/
public Class<?> type() {
if (isClass) {
return (Class<?>) object;
}
else {
return object.getClass();
}
return type;
}

/**
Expand Down
26 changes: 26 additions & 0 deletions jOOR/src/test/java/org/joor/test/ReflectTest.java
Expand Up @@ -529,4 +529,30 @@ public void shouldThrowExceptionThrownByDefaultMethod(){
Reflect.on(new Object()).as(InterfaceWithDefaultMethods.class).throwIllegalArgumentException();
}
/* [/java-8] */

@Test
public void testNullStaticFieldType() {
Map<String, Reflect> fields = Reflect.on(Test1.class).fields();

assertEquals(3, fields.size());
assertEquals(int.class, fields.get("S_INT1").type());
assertEquals(Integer.valueOf(0), fields.get("S_INT1").get());
assertEquals(Integer.class, fields.get("S_INT2").type());
assertNull(fields.get("S_INT2").get());
assertEquals(Test1.class, fields.get("S_DATA").type());
assertNull(fields.get("S_DATA").get());
}

@Test
public void testNullInstanceFieldType() {
Map<String, Reflect> fields = Reflect.on(new Test1()).fields();

assertEquals(3, fields.size());
assertEquals(int.class, fields.get("I_INT1").type());
assertEquals(Integer.valueOf(0), fields.get("I_INT1").get());
assertEquals(Integer.class, fields.get("I_INT2").type());
assertNull(fields.get("I_INT2").get());
assertEquals(Test1.class, fields.get("I_DATA").type());
assertNull(fields.get("I_DATA").get());
}
}

0 comments on commit 846beee

Please sign in to comment.