diff --git a/mrbean/pom.xml b/mrbean/pom.xml
index 91b754a3..e365c8b0 100644
--- a/mrbean/pom.xml
+++ b/mrbean/pom.xml
@@ -51,6 +51,16 @@ ${project.groupId}.mrbean.*;version=${project.version}
replacer
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+ 1.8
+
+
+
org.apache.maven.plugins
diff --git a/mrbean/src/main/java/com/fasterxml/jackson/module/mrbean/BeanBuilder.java b/mrbean/src/main/java/com/fasterxml/jackson/module/mrbean/BeanBuilder.java
index e19282f5..038ccfc1 100644
--- a/mrbean/src/main/java/com/fasterxml/jackson/module/mrbean/BeanBuilder.java
+++ b/mrbean/src/main/java/com/fasterxml/jackson/module/mrbean/BeanBuilder.java
@@ -181,16 +181,28 @@ protected boolean hasConcreteOverride(Method m0, JavaType implementedType)
{
final String name = m0.getName();
final Class>[] argTypes = m0.getParameterTypes();
+ try {
+ // getMethod returns the most-specific method implementation, for public methods only (which is any method in an interface)
+ Method effectiveMethod = implementedType.getRawClass().getMethod(name, argTypes);
+ if (BeanUtil.isConcrete(effectiveMethod)) {
+ return true;
+ }
+ } catch (NoSuchMethodException e) {
+ // method must be non-public, fallback to using getDeclaredMethod
+ }
+
for (JavaType curr = implementedType; (curr != null) && !curr.isJavaLangObject();
curr = curr.getSuperClass()) {
// 29-Nov-2015, tatu: Avoiding exceptions would be good, so would linear scan
// be better here?
try {
Method effectiveMethod = curr.getRawClass().getDeclaredMethod(name, argTypes);
- if (effectiveMethod != null && BeanUtil.isConcrete(effectiveMethod)) {
+ if (BeanUtil.isConcrete(effectiveMethod)) {
return true;
}
- } catch (NoSuchMethodException e) { }
+ } catch (NoSuchMethodException e) {
+ // method must exist on a superclass, continue searching...
+ }
}
return false;
}
@@ -245,7 +257,7 @@ protected final static boolean returnsBoolean(Method m)
Class> rt = m.getReturnType();
return (rt == Boolean.class || rt == Boolean.TYPE);
}
-
+
/*
/**********************************************************
/* Internal methods, bytecode generation
@@ -354,7 +366,7 @@ protected void createUnimplementedMethod(ClassWriter cw, String internalClassNam
/* Internal methods, other
/**********************************************************
*/
-
+
protected String decap(String name) {
char c = name.charAt(0);
if (name.length() > 1
diff --git a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClasses.java b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClasses.java
index 9d4ffda5..1307a412 100644
--- a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClasses.java
+++ b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClasses.java
@@ -16,11 +16,15 @@ public abstract static class Bean
int y;
protected Bean() { }
-
+
public abstract String getX();
public String getFoo() { return "Foo!"; }
public void setY(int value) { y = value; }
+
+ // also verify non-public methods
+ protected abstract String getZ();
+ private String customMethod() { return "Private methods rock!"; }
}
/*
@@ -32,10 +36,12 @@ protected Bean() { }
public void testSimpleInteface() throws Exception
{
ObjectMapper mapper = newMrBeanMapper();
- Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13 }", Bean.class);
+ Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", Bean.class);
assertNotNull(bean);
assertEquals("abc", bean.getX());
assertEquals(13, bean.y);
assertEquals("Foo!", bean.getFoo());
+ assertEquals("def", bean.getZ());
+ assertEquals("Private methods rock!", bean.customMethod());
}
}
diff --git a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClassesWithOverrides.java b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClassesWithOverrides.java
index a3938c0a..82deaac4 100644
--- a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClassesWithOverrides.java
+++ b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClassesWithOverrides.java
@@ -16,19 +16,28 @@ public abstract static class Bean
int y;
protected Bean() { }
-
+
public abstract String getX();
public abstract String roast(int temperature);
public String getFoo() { return "Foo!"; }
public void setY(int value) { y = value; }
+
+ // also verify non-public methods
+ protected abstract String getZ();
+ private Object customMethod() { return protectedAbstractMethod(); }
+ protected abstract Object protectedAbstractMethod();
}
public abstract static class CoffeeBean extends Bean {
@Override public String roast(int temperature) {
return "The coffee beans are roasting at " + temperature + " degrees now, yummy";
}
+
+ @Override protected Object protectedAbstractMethod() {
+ return "Private methods invoking protected abstract methods is the bomb!";
+ }
}
public abstract static class PeruvianCoffeeBean extends CoffeeBean {}
@@ -45,10 +54,10 @@ public void testOverrides() throws Exception
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
- Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13 }", CoffeeBean.class);
+ Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", CoffeeBean.class);
verifyBean(bean);
- Bean bean2 = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13 }", PeruvianCoffeeBean.class);
+ Bean bean2 = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", PeruvianCoffeeBean.class);
verifyBean(bean2);
}
@@ -57,6 +66,8 @@ private void verifyBean(Bean bean) {
assertEquals("abc", bean.getX());
assertEquals(13, bean.y);
assertEquals("Foo!", bean.getFoo());
+ assertEquals("def", bean.getZ());
assertEquals("The coffee beans are roasting at 123 degrees now, yummy", bean.roast(123));
+ assertEquals("Private methods invoking protected abstract methods is the bomb!", bean.customMethod());
}
}
diff --git a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestSimpleMaterializedInterfaces.java b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestSimpleMaterializedInterfaces.java
index f4d252a9..7ecd2884 100644
--- a/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestSimpleMaterializedInterfaces.java
+++ b/mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestSimpleMaterializedInterfaces.java
@@ -26,13 +26,13 @@ public interface BeanWithY extends Bean
{
public int getY();
}
-
+
public interface PartialBean {
public boolean isOk();
// and then non-getter/setter one:
public int foobar();
}
-
+
public interface BeanHolder {
public Bean getBean();
}
@@ -53,7 +53,21 @@ public interface ArrayBean {
interface NonPublicBean {
public abstract int getX();
}
-
+
+ public interface OtherInterface {
+ public boolean anyValuePresent();
+ }
+
+ public interface BeanWithDefaultForOtherInterface extends Bean, OtherInterface {
+ public default boolean anyValuePresent() {
+ return getX() > 0 || getA() != null;
+ }
+ }
+
+ public interface BeanWithInheritedDefault extends BeanWithDefaultForOtherInterface {
+ // in this interface, anyValuePresent() is an inherited (rather than declared) concrete method
+ }
+
/*
/**********************************************************
/* Unit tests, low level
@@ -149,8 +163,8 @@ public void testBeanHolder() throws Exception
assertNotNull(bean);
assertEquals("b", bean.getA());
assertEquals(-4, bean.getX());
- }
-
+ }
+
public void testArrayInterface() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
@@ -172,7 +186,27 @@ public void testSubInterface() throws Exception
assertEquals(1, bean.getX());
assertEquals(2, bean.getY());
}
-
+
+ public void testDefaultMethodInInterface() throws Exception
+ {
+ ObjectMapper mapper = newMrBeanMapper();
+ BeanWithDefaultForOtherInterface bean = mapper.readValue("{\"a\":\"value\",\"x\":123 }", BeanWithDefaultForOtherInterface.class);
+ assertNotNull(bean);
+ assertEquals("value", bean.getA());
+ assertEquals(123, bean.getX());
+ assertTrue(bean.anyValuePresent());
+ }
+
+ public void testInheritedDefaultMethodInInterface() throws Exception
+ {
+ ObjectMapper mapper = newMrBeanMapper();
+ BeanWithInheritedDefault bean = mapper.readValue("{\"a\":\"value\",\"x\":123 }", BeanWithInheritedDefault.class);
+ assertNotNull(bean);
+ assertEquals("value", bean.getA());
+ assertEquals(123, bean.getX());
+ assertTrue(bean.anyValuePresent());
+ }
+
/*
/**********************************************************
/* Unit tests, higher level, error handling
@@ -212,6 +246,6 @@ public void testNonPublic() throws Exception
} catch (JsonMappingException e) {
verifyException(e, "is not public");
}
- }
-
+ }
+
}