Fix [Issue-14]; parent classes of mix-in annotations were being ignored for methods, fields
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 177281b..cc4001a 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -13,6 +13,9 @@
(reported by Steven S)
* [Issue-11] JsonParser.getValueAsLong() returning int, not long
(reported by Daniel L)
+ * [Issue-14]: Annotations were not included from parent classes of
+ mix-in classes
+ (reported by @guillaup)
* [JACKSON-829] Custom serializers not working for List<String> properties,
@JsonSerialize(contentUsing)
(reported by James R)
diff --git a/src/mapper/java/org/codehaus/jackson/map/introspect/AnnotatedClass.java b/src/mapper/java/org/codehaus/jackson/map/introspect/AnnotatedClass.java
index 837d9ab..3e36020 100644
--- a/src/mapper/java/org/codehaus/jackson/map/introspect/AnnotatedClass.java
+++ b/src/mapper/java/org/codehaus/jackson/map/introspect/AnnotatedClass.java
@@ -416,7 +416,7 @@
if (_mixInResolver != null) {
Class<?> mixin = _mixInResolver.findMixInClassFor(Object.class);
if (mixin != null) {
- _addMethodMixIns(methodFilter, _memberMethods, mixin, mixins);
+ _addMethodMixIns(_class, methodFilter, _memberMethods, mixin, mixins);
}
}
@@ -610,7 +610,7 @@
{
// first, mixIns, since they have higher priority then class methods
if (mixInCls != null) {
- _addMethodMixIns(methodFilter, methods, mixInCls, mixIns);
+ _addMethodMixIns(cls, methodFilter, methods, mixInCls, mixIns);
}
if (cls == null) { // just so caller need not check when passing super-class
@@ -650,26 +650,32 @@
}
}
- protected void _addMethodMixIns(MethodFilter methodFilter, AnnotatedMethodMap methods,
+ protected void _addMethodMixIns(Class<?> targetClass,
+ MethodFilter methodFilter, AnnotatedMethodMap methods,
Class<?> mixInCls, AnnotatedMethodMap mixIns)
{
- for (Method m : mixInCls.getDeclaredMethods()) {
- if (!_isIncludableMethod(m, methodFilter)) {
- continue;
- }
- AnnotatedMethod am = methods.find(m);
- /* Do we already have a method to augment (from sub-class
- * that will mask this mixIn)? If so, add if visible
- * without masking (no such annotation)
- */
- if (am != null) {
- _addMixUnders(m, am);
- /* Otherwise will have precedence, but must wait
- * until we find the real method (mixIn methods are
- * just placeholder, can't be called)
+ List<Class<?>> parents = new ArrayList<Class<?>>();
+ parents.add(mixInCls);
+ ClassUtil.findSuperTypes(mixInCls, targetClass, parents);
+ for (Class<?> mixin : parents) {
+ for (Method m : mixin.getDeclaredMethods()) {
+ if (!_isIncludableMethod(m, methodFilter)) {
+ continue;
+ }
+ AnnotatedMethod am = methods.find(m);
+ /* Do we already have a method to augment (from sub-class
+ * that will mask this mixIn)? If so, add if visible
+ * without masking (no such annotation)
*/
- } else {
- mixIns.add(_constructMethod(m));
+ if (am != null) {
+ _addMixUnders(m, am);
+ /* Otherwise will have precedence, but must wait
+ * until we find the real method (mixIn methods are
+ * just placeholder, can't be called)
+ */
+ } else {
+ mixIns.add(_constructMethod(m));
+ }
}
}
}
@@ -710,7 +716,7 @@
if (_mixInResolver != null) {
Class<?> mixin = _mixInResolver.findMixInClassFor(c);
if (mixin != null) {
- _addFieldMixIns(mixin, fields);
+ _addFieldMixIns(parent, mixin, fields);
}
}
}
@@ -721,22 +727,28 @@
* into already collected actual fields (from introspected classes and their
* super-classes)
*/
- protected void _addFieldMixIns(Class<?> mixin, Map<String,AnnotatedField> fields)
+ protected void _addFieldMixIns(Class<?> targetClass, Class<?> mixInCls,
+ Map<String,AnnotatedField> fields)
{
- for (Field mixinField : mixin.getDeclaredFields()) {
- /* there are some dummy things (static, synthetic); better
- * ignore
- */
- if (!_isIncludableField(mixinField)) {
- continue;
- }
- String name = mixinField.getName();
- // anything to mask? (if not, quietly ignore)
- AnnotatedField maskedField = fields.get(name);
- if (maskedField != null) {
- for (Annotation a : mixinField.getDeclaredAnnotations()) {
- if (_annotationIntrospector.isHandled(a)) {
- maskedField.addOrOverride(a);
+ List<Class<?>> parents = new ArrayList<Class<?>>();
+ parents.add(mixInCls);
+ ClassUtil.findSuperTypes(mixInCls, targetClass, parents);
+ for (Class<?> mixin : parents) {
+ for (Field mixinField : mixin.getDeclaredFields()) {
+ /* there are some dummy things (static, synthetic); better
+ * ignore
+ */
+ if (!_isIncludableField(mixinField)) {
+ continue;
+ }
+ String name = mixinField.getName();
+ // anything to mask? (if not, quietly ignore)
+ AnnotatedField maskedField = fields.get(name);
+ if (maskedField != null) {
+ for (Annotation a : mixinField.getDeclaredAnnotations()) {
+ if (_annotationIntrospector.isHandled(a)) {
+ maskedField.addOrOverride(a);
+ }
}
}
}
diff --git a/src/test/org/codehaus/jackson/map/mixins/TestMixinInheritance.java b/src/test/org/codehaus/jackson/map/mixins/TestMixinInheritance.java
index 206f225..28c967f 100644
--- a/src/test/org/codehaus/jackson/map/mixins/TestMixinInheritance.java
+++ b/src/test/org/codehaus/jackson/map/mixins/TestMixinInheritance.java
@@ -25,7 +25,30 @@
public int ido;
}
- public void testMixinInheritance() throws IOException
+ static class Beano2 {
+ public int getIdo() { return 13; }
+ public String getNameo() { return "Bill"; }
+ }
+
+ static abstract class BeanoMixinSuper2 extends Beano2 {
+ @Override
+ @JsonProperty("name")
+ public abstract String getNameo();
+ }
+
+ static abstract class BeanoMixinSub2 extends BeanoMixinSuper2 {
+ @Override
+ @JsonProperty("id")
+ public abstract int getIdo();
+ }
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ public void testMixinFieldInheritance() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(Beano.class, BeanoMixinSub.class);
@@ -35,4 +58,15 @@
assertTrue(result.containsKey("id"));
assertTrue(result.containsKey("name"));
}
+
+ public void testMixinMethodInheritance() throws IOException
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.getSerializationConfig().addMixInAnnotations(Beano2.class, BeanoMixinSub2.class);
+ Map<String,Object> result;
+ result = writeAndMap(mapper, new Beano2());
+ assertEquals(2, result.size());
+ assertTrue(result.containsKey("id"));
+ assertTrue(result.containsKey("name"));
+ }
}