Skip to content

Commit ceb7e2d

Browse files
authored
Merge pull request #111 from bazaarvoice/patch-reabstracted-method
MrBean: Correct handling of re-abstracted methods
2 parents ed8b9e2 + 90d902a commit ceb7e2d

File tree

2 files changed

+108
-8
lines changed

2 files changed

+108
-8
lines changed

mrbean/src/main/java/com/fasterxml/jackson/module/mrbean/BeanBuilder.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ protected boolean hasConcreteOverride(Method m0, JavaType implementedType)
189189
// 22-Sep-2020: [modules-base#109]: getMethod returns the most-specific method
190190
// implementation, for public methods only (which is any method in an interface)
191191
Method effectiveMethod = implementedType.getRawClass().getMethod(name, argTypes);
192-
if (BeanUtil.isConcrete(effectiveMethod)) {
193-
return true;
194-
}
192+
193+
// we've found the method, so we can simply check whether it is concrete
194+
return BeanUtil.isConcrete(effectiveMethod);
195195
} catch (NoSuchMethodException e) {
196196
// method must be non-public, fallback to using getDeclaredMethod
197197
}
@@ -202,9 +202,9 @@ protected boolean hasConcreteOverride(Method m0, JavaType implementedType)
202202
// be better here?
203203
try {
204204
Method effectiveMethod = curr.getRawClass().getDeclaredMethod(name, argTypes);
205-
if (BeanUtil.isConcrete(effectiveMethod)) {
206-
return true;
207-
}
205+
206+
// we've found the method, so we can simply check whether it is concrete
207+
return BeanUtil.isConcrete(effectiveMethod);
208208
} catch (NoSuchMethodException e) {
209209
// method must exist on a superclass, continue searching...
210210
}

mrbean/src/test/java/com/fasterxml/jackson/module/mrbean/TestAbstractClassesWithOverrides.java

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.fasterxml.jackson.module.mrbean;
22

3+
import com.fasterxml.jackson.databind.JsonMappingException;
34
import com.fasterxml.jackson.databind.ObjectMapper;
45

56
public class TestAbstractClassesWithOverrides
@@ -42,6 +43,30 @@ public abstract static class CoffeeBean extends Bean {
4243

4344
public abstract static class PeruvianCoffeeBean extends CoffeeBean {}
4445

46+
/*
47+
* Test classes where some concrete method has been re-"abstract"-ed
48+
*/
49+
50+
public abstract static class CoffeeBeanWithVariableFoo extends CoffeeBean
51+
{
52+
@Override public abstract String getFoo();
53+
}
54+
55+
public abstract static class StringlessCoffeeBean extends CoffeeBean
56+
{
57+
@Override public abstract String toString();
58+
}
59+
60+
public abstract static class UnroastableCoffeeBean extends CoffeeBean
61+
{
62+
@Override public abstract String roast(int temperature);
63+
}
64+
65+
public abstract static class CoffeeBeanLackingPublicMethod extends CoffeeBean
66+
{
67+
@Override protected abstract Object protectedAbstractMethod();
68+
}
69+
4570

4671
/*
4772
/**********************************************************
@@ -51,8 +76,7 @@ public abstract static class PeruvianCoffeeBean extends CoffeeBean {}
5176

5277
public void testOverrides() throws Exception
5378
{
54-
ObjectMapper mapper = new ObjectMapper();
55-
mapper.registerModule(new MrBeanModule());
79+
ObjectMapper mapper = newMrBeanMapper();
5680

5781
Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", CoffeeBean.class);
5882
verifyBean(bean);
@@ -61,6 +85,82 @@ public void testOverrides() throws Exception
6185
verifyBean(bean2);
6286
}
6387

88+
public void testReAbstractedMethods() throws Exception
89+
{
90+
AbstractTypeMaterializer mat = new AbstractTypeMaterializer();
91+
// ensure that we will only get deferred error methods
92+
mat.disable(AbstractTypeMaterializer.Feature.FAIL_ON_UNMATERIALIZED_METHOD);
93+
ObjectMapper mapper = new ObjectMapper()
94+
.registerModule(new MrBeanModule(mat));
95+
96+
verifyReAbstractedProperty(mapper);
97+
98+
Bean bean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", StringlessCoffeeBean.class);
99+
verifyBean(bean);
100+
try {
101+
assertNotNull(bean.toString());
102+
fail("Should not pass");
103+
} catch (UnsupportedOperationException e) {
104+
verifyException(e, "Unimplemented method 'toString'");
105+
}
106+
107+
Bean unroastableBean = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", UnroastableCoffeeBean.class);
108+
try {
109+
unroastableBean.roast(123);
110+
fail("Should not pass");
111+
} catch (UnsupportedOperationException e) {
112+
verifyException(e, "Unimplemented method 'roast'");
113+
}
114+
115+
Bean beanLackingNonPublicMethod = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", CoffeeBeanLackingPublicMethod.class);
116+
try {
117+
beanLackingNonPublicMethod.customMethod();
118+
fail("Should not pass");
119+
} catch (UnsupportedOperationException e) {
120+
verifyException(e, "Unimplemented method 'protectedAbstractMethod'");
121+
}
122+
}
123+
124+
// Ensures that the re-abstracted method will read "foo" from the JSON, regardless of the FAIL_ON_UNMATERIALIZED_METHOD setting
125+
private void verifyReAbstractedProperty(ObjectMapper mapper) throws com.fasterxml.jackson.core.JsonProcessingException {
126+
Bean beanWithNoFoo = mapper.readValue("{ \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", CoffeeBeanWithVariableFoo.class);
127+
assertNull(beanWithNoFoo.getFoo());
128+
Bean beanWithOtherFoo = mapper.readValue("{ \"foo\": \"Another Foo!\", \"x\" : \"abc\", \"y\" : 13, \"z\" : \"def\" }", CoffeeBeanWithVariableFoo.class);
129+
assertEquals("Another Foo!", beanWithOtherFoo.getFoo());
130+
}
131+
132+
public void testEagerFailureOnReAbstractedMethods() throws Exception
133+
{
134+
AbstractTypeMaterializer mat = new AbstractTypeMaterializer();
135+
// ensure that we will get eager failure on abstract methods
136+
mat.enable(AbstractTypeMaterializer.Feature.FAIL_ON_UNMATERIALIZED_METHOD);
137+
ObjectMapper mapper = new ObjectMapper()
138+
.registerModule(new MrBeanModule(mat));
139+
140+
verifyReAbstractedProperty(mapper);
141+
142+
try {
143+
mapper.readValue("{}", StringlessCoffeeBean.class);
144+
fail("Should not pass");
145+
} catch (JsonMappingException e) {
146+
verifyException(e, "Unrecognized abstract method 'toString'");
147+
}
148+
149+
try {
150+
mapper.readValue("{}", UnroastableCoffeeBean.class);
151+
fail("Should not pass");
152+
} catch (JsonMappingException e) {
153+
verifyException(e, "Unrecognized abstract method 'roast'");
154+
}
155+
156+
try {
157+
mapper.readValue("{}", CoffeeBeanLackingPublicMethod.class);
158+
fail("Should not pass");
159+
} catch (JsonMappingException e) {
160+
verifyException(e, "Unrecognized abstract method 'protectedAbstractMethod'");
161+
}
162+
}
163+
64164
private void verifyBean(Bean bean) {
65165
assertNotNull(bean);
66166
assertEquals("abc", bean.getX());

0 commit comments

Comments
 (0)