Skip to content

Commit 3ec2537

Browse files
committed
keep bundle annotations and prevent simple cycles
I don't see any downsides and this makes it easier to manipulate custom annotations, introspectors, serializers, and other parts that interact with annotated objects. The cycle detection is a bonus.
1 parent e88a87c commit 3ec2537

File tree

4 files changed

+73
-18
lines changed

4 files changed

+73
-18
lines changed

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -908,13 +908,13 @@ private void _addAnnotationsIfNotPresent(AnnotationMap result, Annotation[] anns
908908
if (anns != null) {
909909
List<Annotation[]> bundles = null;
910910
for (Annotation ann : anns) { // first: direct annotations
911-
if (_isAnnotationBundle(ann)) {
911+
// note: we will NOT filter out non-Jackson anns any more
912+
boolean wasNotPresent = result.addIfNotPresent(ann);
913+
if (wasNotPresent && _isAnnotationBundle(ann)) {
912914
if (bundles == null) {
913915
bundles = new LinkedList<Annotation[]>();
914916
}
915917
bundles.add(ann.annotationType().getDeclaredAnnotations());
916-
} else { // note: we will NOT filter out non-Jackson anns any more
917-
result.addIfNotPresent(ann);
918918
}
919919
}
920920
if (bundles != null) { // and secondarily handle bundles, if any found: precedence important
@@ -930,13 +930,13 @@ private void _addAnnotationsIfNotPresent(AnnotatedMember target, Annotation[] an
930930
if (anns != null) {
931931
List<Annotation[]> bundles = null;
932932
for (Annotation ann : anns) { // first: direct annotations
933-
if (_isAnnotationBundle(ann)) {
933+
// note: we will NOT filter out non-Jackson anns any more
934+
boolean wasNotPresent = target.addIfNotPresent(ann);
935+
if (wasNotPresent && _isAnnotationBundle(ann)) {
934936
if (bundles == null) {
935937
bundles = new LinkedList<Annotation[]>();
936938
}
937939
bundles.add(ann.annotationType().getDeclaredAnnotations());
938-
} else { // note: we will NOT filter out non-Jackson anns any more
939-
target.addIfNotPresent(ann);
940940
}
941941
}
942942
if (bundles != null) { // and secondarily handle bundles, if any found: precedence important

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ protected AnnotationMap getAllAnnotations() {
5151
* annotation masking or overriding an annotation 'real' constructor
5252
* has.
5353
*/
54-
public final void addOrOverride(Annotation a) {
55-
_annotations.add(a);
54+
public final boolean addOrOverride(Annotation a) {
55+
return _annotations.add(a);
5656
}
5757

5858
/**
5959
* Method called to augment annotations, by adding specified
6060
* annotation if and only if it is not yet present in the
6161
* annotation map we have.
6262
*/
63-
public final void addIfNotPresent(Annotation a) {
64-
_annotations.addIfNotPresent(a);
63+
public final boolean addIfNotPresent(Annotation a) {
64+
return _annotations.addIfNotPresent(a);
6565
}
6666

6767
/**

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@ public int size() {
7171
* Method called to add specified annotation in the Map, but
7272
* only if it didn't yet exist.
7373
*/
74-
public void addIfNotPresent(Annotation ann)
74+
public boolean addIfNotPresent(Annotation ann)
7575
{
7676
if (_annotations == null || !_annotations.containsKey(ann.annotationType())) {
7777
_add(ann);
78+
return true;
7879
}
80+
return false;
7981
}
8082

8183
/**
8284
* Method called to add specified annotation in the Map.
8385
*/
84-
public void add(Annotation ann) {
85-
_add(ann);
86+
public boolean add(Annotation ann) {
87+
return _add(ann);
8688
}
8789

8890
@Override
@@ -99,11 +101,12 @@ public String toString() {
99101
/**********************************************************
100102
*/
101103

102-
protected final void _add(Annotation ann) {
104+
protected final boolean _add(Annotation ann) {
103105
if (_annotations == null) {
104106
_annotations = new HashMap<Class<? extends Annotation>,Annotation>();
105107
}
106-
_annotations.put(ann.annotationType(), ann);
108+
Annotation previous = _annotations.put(ann.annotationType(), ann);
109+
return (previous != null) && previous.equals(ann);
107110
}
108111
}
109112

src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
import java.lang.annotation.Retention;
44
import java.lang.annotation.RetentionPolicy;
55

6-
import com.fasterxml.jackson.annotation.*;
6+
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
7+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
78
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
9+
import com.fasterxml.jackson.annotation.JsonIgnore;
10+
import com.fasterxml.jackson.annotation.JsonProperty;
811
import com.fasterxml.jackson.databind.ObjectMapper;
12+
import com.fasterxml.jackson.databind.PropertyName;
913

1014
/* Tests mostly for [JACKSON-754]: ability to create "annotation bundles"
1115
*/
@@ -51,15 +55,63 @@ public class NoAutoDetect {
5155
public class Bean92 {
5256
@Bundle92
5357
protected String id = "abc";
54-
}
58+
}
59+
60+
@HolderB
61+
@JacksonAnnotationsInside
62+
@Retention(RetentionPolicy.RUNTIME)
63+
static @interface HolderA {}
64+
65+
@HolderA
66+
@JacksonAnnotationsInside
67+
@Retention(RetentionPolicy.RUNTIME)
68+
static @interface HolderB {}
69+
70+
static class HolderHolder {
71+
@HolderA
72+
@InformativeHolder
73+
public int unimportant = 42;
74+
}
75+
76+
@JsonProperty
77+
@JacksonAnnotationsInside
78+
@Retention(RetentionPolicy.RUNTIME)
79+
static @interface InformativeHolder {
80+
// doesn't really contribute to the test, but would be impossible without this feature
81+
boolean important() default true;
82+
}
83+
84+
static class BundleAnnotationIntrospector extends JacksonAnnotationIntrospector {
85+
@Override
86+
public PropertyName findNameForSerialization(Annotated a)
87+
{
88+
InformativeHolder informativeHolder = a.getAnnotation(InformativeHolder.class);
89+
if ((informativeHolder != null) && informativeHolder.important()) {
90+
return new PropertyName("important");
91+
}
92+
return super.findNameForSerialization(a);
93+
}
94+
}
95+
5596
/*
5697
/**********************************************************
5798
/* Test methods
5899
/**********************************************************
59100
*/
60101

61102
private final ObjectMapper MAPPER = new ObjectMapper();
62-
103+
104+
public void testKeepAnnotationBundle() throws Exception
105+
{
106+
MAPPER.setAnnotationIntrospector(new BundleAnnotationIntrospector());
107+
assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new HolderHolder()));
108+
}
109+
110+
public void testRecursiveBundles() throws Exception
111+
{
112+
assertEquals("{\"unimportant\":42}", MAPPER.writeValueAsString(new HolderHolder()));
113+
}
114+
63115
public void testBundledIgnore() throws Exception
64116
{
65117
assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));

0 commit comments

Comments
 (0)