diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index fa379b104d..2a9c458d5e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -908,13 +908,13 @@ private void _addAnnotationsIfNotPresent(AnnotationMap result, Annotation[] anns if (anns != null) { List bundles = null; for (Annotation ann : anns) { // first: direct annotations - if (_isAnnotationBundle(ann)) { + // note: we will NOT filter out non-Jackson anns any more + boolean wasNotPresent = result.addIfNotPresent(ann); + if (wasNotPresent && _isAnnotationBundle(ann)) { if (bundles == null) { bundles = new LinkedList(); } bundles.add(ann.annotationType().getDeclaredAnnotations()); - } else { // note: we will NOT filter out non-Jackson anns any more - result.addIfNotPresent(ann); } } if (bundles != null) { // and secondarily handle bundles, if any found: precedence important @@ -930,13 +930,13 @@ private void _addAnnotationsIfNotPresent(AnnotatedMember target, Annotation[] an if (anns != null) { List bundles = null; for (Annotation ann : anns) { // first: direct annotations - if (_isAnnotationBundle(ann)) { + // note: we will NOT filter out non-Jackson anns any more + boolean wasNotPresent = target.addIfNotPresent(ann); + if (wasNotPresent && _isAnnotationBundle(ann)) { if (bundles == null) { bundles = new LinkedList(); } bundles.add(ann.annotationType().getDeclaredAnnotations()); - } else { // note: we will NOT filter out non-Jackson anns any more - target.addIfNotPresent(ann); } } if (bundles != null) { // and secondarily handle bundles, if any found: precedence important @@ -952,13 +952,13 @@ private void _addOrOverrideAnnotations(AnnotatedMember target, Annotation[] anns if (anns != null) { List bundles = null; for (Annotation ann : anns) { // first: direct annotations - if (_isAnnotationBundle(ann)) { + // note: we will NOT filter out non-Jackson anns any more + boolean wasModified = target.addOrOverride(ann); + if (wasModified && _isAnnotationBundle(ann)) { if (bundles == null) { bundles = new LinkedList(); } bundles.add(ann.annotationType().getDeclaredAnnotations()); - } else { // note: no filtering by jackson-annotations - target.addOrOverride(ann); } } if (bundles != null) { // and then bundles, if any: important for precedence diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java index 871a65c58d..550b26329d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java @@ -51,8 +51,8 @@ protected AnnotationMap getAllAnnotations() { * annotation masking or overriding an annotation 'real' constructor * has. */ - public final void addOrOverride(Annotation a) { - _annotations.add(a); + public final boolean addOrOverride(Annotation a) { + return _annotations.add(a); } /** @@ -60,8 +60,8 @@ public final void addOrOverride(Annotation a) { * annotation if and only if it is not yet present in the * annotation map we have. */ - public final void addIfNotPresent(Annotation a) { - _annotations.addIfNotPresent(a); + public final boolean addIfNotPresent(Annotation a) { + return _annotations.addIfNotPresent(a); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java index 1b350fe0a2..0b514ffb51 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java @@ -71,18 +71,20 @@ public int size() { * Method called to add specified annotation in the Map, but * only if it didn't yet exist. */ - public void addIfNotPresent(Annotation ann) + public boolean addIfNotPresent(Annotation ann) { if (_annotations == null || !_annotations.containsKey(ann.annotationType())) { _add(ann); + return true; } + return false; } /** * Method called to add specified annotation in the Map. */ - public void add(Annotation ann) { - _add(ann); + public boolean add(Annotation ann) { + return _add(ann); } @Override @@ -99,11 +101,12 @@ public String toString() { /********************************************************** */ - protected final void _add(Annotation ann) { + protected final boolean _add(Annotation ann) { if (_annotations == null) { _annotations = new HashMap,Annotation>(); } - _annotations.put(ann.annotationType(), ann); + Annotation previous = _annotations.put(ann.annotationType(), ann); + return (previous != null) && previous.equals(ann); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java index ddb77360da..e8d1af606e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java @@ -3,9 +3,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyName; /* Tests mostly for [JACKSON-754]: ability to create "annotation bundles" */ @@ -51,7 +55,46 @@ public class NoAutoDetect { public class Bean92 { @Bundle92 protected String id = "abc"; - } + } + + @HolderB + @JacksonAnnotationsInside + @Retention(RetentionPolicy.RUNTIME) + static @interface HolderA {} + + @HolderA + @JacksonAnnotationsInside + @Retention(RetentionPolicy.RUNTIME) + static @interface HolderB {} + + static class RecursiveHolder { + @HolderA public int unimportant = 42; + } + + @JsonProperty + @JacksonAnnotationsInside + @Retention(RetentionPolicy.RUNTIME) + static @interface InformativeHolder { + // doesn't really contribute to the test, but would be impossible without this feature + boolean important() default true; + } + + static class InformingHolder { + @InformativeHolder public int unimportant = 42; + } + + static class BundleAnnotationIntrospector extends JacksonAnnotationIntrospector { + @Override + public PropertyName findNameForSerialization(Annotated a) + { + InformativeHolder informativeHolder = a.getAnnotation(InformativeHolder.class); + if ((informativeHolder != null) && informativeHolder.important()) { + return new PropertyName("important"); + } + return super.findNameForSerialization(a); + } + } + /* /********************************************************** /* Test methods @@ -59,7 +102,18 @@ public class Bean92 { */ private final ObjectMapper MAPPER = new ObjectMapper(); - + + public void testKeepAnnotationBundle() throws Exception + { + MAPPER.setAnnotationIntrospector(new BundleAnnotationIntrospector()); + assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new InformingHolder())); + } + + public void testRecursiveBundles() throws Exception + { + assertEquals("{\"unimportant\":42}", MAPPER.writeValueAsString(new RecursiveHolder())); + } + public void testBundledIgnore() throws Exception { assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));