This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/johnzon.git
commit 0775ac035f0e98d1f768418b619d47932840384d Author: Romain Manni-Bucau <[email protected]> AuthorDate: Sat Aug 10 17:20:39 2019 +0200 JOHNZON-238 JOHNZON-237 ensure to read class annotation on the superclass + respect deserialization order based on the json and not the class (spec requirement we can challenge later) --- .../org/apache/johnzon/jsonb/JsonbAccessMode.java | 38 ++-- .../apache/johnzon/jsonb/DefaultMappingTest.java | 7 +- .../java/org/apache/johnzon/jsonb/OrderTest.java | 196 +++++++-------------- .../org/apache/johnzon/jsonb/SerializerTest.java | 150 ++++++++++++++++ .../apache/johnzon/mapper/MappingParserImpl.java | 16 +- .../org/apache/johnzon/mapper/access/Meta.java | 22 ++- pom.xml | 3 +- 7 files changed, 271 insertions(+), 161 deletions(-) diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java index c08d529..87819ca 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java @@ -781,16 +781,18 @@ public class JsonbAccessMode implements AccessMode, Closeable { private ObjectConverter.Reader reader; ReaderConverters(final DecoratedType annotationHolder) { + final boolean numberType = isNumberType(annotationHolder.getType()); + final boolean dateType = isDateType(annotationHolder.getType()); final JsonbTypeDeserializer deserializer = annotationHolder.getAnnotation(JsonbTypeDeserializer.class); final JsonbTypeAdapter adapter = annotationHolder.getAnnotation(JsonbTypeAdapter.class); - JsonbDateFormat dateFormat = annotationHolder.getAnnotation(JsonbDateFormat.class); - JsonbNumberFormat numberFormat = annotationHolder.getAnnotation(JsonbNumberFormat.class); + JsonbDateFormat dateFormat = dateType ? annotationHolder.getAnnotation(JsonbDateFormat.class) : null; + JsonbNumberFormat numberFormat = numberType ? annotationHolder.getAnnotation(JsonbNumberFormat.class) : null; final JohnzonConverter johnzonConverter = annotationHolder.getAnnotation(JohnzonConverter.class); validateAnnotations(annotationHolder, adapter, dateFormat, numberFormat, johnzonConverter); - if (dateFormat == null && isDateType(annotationHolder.getType())) { + if (dateFormat == null && dateType) { dateFormat = annotationHolder.getClassOrPackageAnnotation(JsonbDateFormat.class); } - if (numberFormat == null && isNumberType(annotationHolder.getType())) { + if (numberFormat == null && numberType) { numberFormat = annotationHolder.getClassOrPackageAnnotation(JsonbNumberFormat.class); } @@ -869,23 +871,25 @@ public class JsonbAccessMode implements AccessMode, Closeable { private Adapter<?, ?> converter; private ObjectConverter.Writer writer; - WriterConverters(final DecoratedType initialReader, final Types types) { - final JsonbTypeSerializer serializer = initialReader.getAnnotation(JsonbTypeSerializer.class); - final JsonbTypeAdapter adapter = initialReader.getAnnotation(JsonbTypeAdapter.class); - JsonbDateFormat dateFormat = initialReader.getAnnotation(JsonbDateFormat.class); - JsonbNumberFormat numberFormat = initialReader.getAnnotation(JsonbNumberFormat.class); - final JohnzonConverter johnzonConverter = initialReader.getAnnotation(JohnzonConverter.class); - validateAnnotations(initialReader, adapter, dateFormat, numberFormat, johnzonConverter); - if (dateFormat == null && isDateType(initialReader.getType())) { - dateFormat = initialReader.getClassOrPackageAnnotation(JsonbDateFormat.class); + WriterConverters(final DecoratedType reader, final Types types) { + final boolean numberType = isNumberType(reader.getType()); + final boolean dateType = isDateType(reader.getType()); + final JsonbTypeSerializer serializer = reader.getAnnotation(JsonbTypeSerializer.class); + final JsonbTypeAdapter adapter = reader.getAnnotation(JsonbTypeAdapter.class); + JsonbDateFormat dateFormat = dateType ? reader.getAnnotation(JsonbDateFormat.class) : null; + JsonbNumberFormat numberFormat = numberType ? reader.getAnnotation(JsonbNumberFormat.class) : null; + final JohnzonConverter johnzonConverter = reader.getAnnotation(JohnzonConverter.class); + validateAnnotations(reader, adapter, dateFormat, numberFormat, johnzonConverter); + if (dateFormat == null && isDateType(reader.getType())) { + dateFormat = reader.getClassOrPackageAnnotation(JsonbDateFormat.class); } - if (numberFormat == null && isNumberType(initialReader.getType())) { - numberFormat = initialReader.getClassOrPackageAnnotation(JsonbNumberFormat.class); + if (numberFormat == null && isNumberType(reader.getType())) { + numberFormat = reader.getClassOrPackageAnnotation(JsonbNumberFormat.class); } converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ? - defaultConverters.get(new AdapterKey(initialReader.getType(), String.class)) : - toConverter(types, initialReader.getType(), adapter, dateFormat, numberFormat); + defaultConverters.get(new AdapterKey(reader.getType(), String.class)) : + toConverter(types, reader.getType(), adapter, dateFormat, numberFormat); if (serializer != null) { final Class<? extends JsonbSerializer> value = serializer.value(); diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java index 0d92a32..e707663 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java @@ -1067,10 +1067,11 @@ public class DefaultMappingTest { assertEquals("{\"aField\":\"aField\",\"aa\":\"aa\",\"bField\":\"bField\",\"bb\":\"bb\",\"cField\":\"cField\",\"cc\":\"cc\"}", JSONB.toJson(attributesOrderingWithInheritance)); + // important, see OrderTest#deserializationRespectsOrderToo AttributesOrderingWithCounterClass attributesOrderingWithCounterClass = JSONB.fromJson("{\"second\":\"a\",\"third\":\"b\",\"first\":\"c\"}", AttributesOrderingWithCounterClass.class); - assertEquals("a1", attributesOrderingWithCounterClass.second); - assertEquals("b2", attributesOrderingWithCounterClass.third); - assertEquals("c0", attributesOrderingWithCounterClass.first); + assertEquals("a0", attributesOrderingWithCounterClass.second); + assertEquals("b1", attributesOrderingWithCounterClass.third); + assertEquals("c2", attributesOrderingWithCounterClass.first); } public static void toJsonNullValues() { diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java index 355032d..a2630d3 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java @@ -21,21 +21,8 @@ package org.apache.johnzon.jsonb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.lang.reflect.Type; -import java.util.LinkedList; -import java.util.List; - import javax.json.bind.annotation.JsonbPropertyOrder; -import javax.json.bind.annotation.JsonbTypeDeserializer; -import javax.json.bind.annotation.JsonbTypeSerializer; -import javax.json.bind.serializer.DeserializationContext; -import javax.json.bind.serializer.JsonbDeserializer; -import javax.json.bind.serializer.JsonbSerializer; -import javax.json.bind.serializer.SerializationContext; -import javax.json.stream.JsonGenerator; -import javax.json.stream.JsonParser; - -import org.apache.johnzon.jsonb.model.Holder; + import org.apache.johnzon.jsonb.test.JsonbRule; import org.junit.Rule; import org.junit.Test; @@ -51,147 +38,96 @@ public class OrderTest { "\\{\\s*\"third\"\\s*:\\s*\"Third\"\\s*,\\s*\"fourth\"\\s*:\\s*\"Fourth\".*}")); } - @Test - public void typeSerializer() { - final HolderHolder container = new HolderHolder(); - final StringHolder instance = new StringHolder(); - instance.setInstance("Test String"); - container.setInstance(instance); - - final String json = jsonb.toJson(container); - assertTrue(json, json.matches( - "\\{\\s*\"instance\"\\s*:\\s*\\{\\s*\"instance\"\\s*:\\s*\"Test String Serialized\"\\s*}\\s*}")); - - final HolderHolder unmarshalledObject = jsonb.fromJson("{ \"instance\" : { \"instance\" : \"Test String\" } }", HolderHolder.class); - assertEquals("Test String Deserialized", unmarshalledObject.getInstance().getInstance()); + @Test // TODO: not sure it is good to respect json and not mapping, we can challenge the spec on it + public void deserializationRespectsOrderToo() { + final String json = this.jsonb.toJson(new PartialOrder() {{ setStringInstance("Test String"); }}); + assertEquals("{\"longInstance\":0,\"intInstance\":0,\"stringInstance\":\"Test String\",\"anIntInstance\":0,\"anotherIntInstance\":0,\"yetAnotherIntInstance\":0}", json); + assertTrue(json, json.contains("anotherIntInstance")); + assertTrue(json, json.contains("anIntInstance")); + assertTrue(json, json.contains("yetAnotherIntInstance")); + + final PartialOrder unmarshalledObject = jsonb.fromJson( + "{ \"anIntInstance\" : 100, \"yetAnotherIntInstance\":100, \"anotherIntInstance\": 100, " + + "\"intInstance\" : 1, \"stringInstance\" : \"Test String\", \"longInstance\" : 0 }", + PartialOrder.class); + assertEquals(3, unmarshalledObject.getIntInstance()); + assertEquals(100, unmarshalledObject.getAnotherIntInstance()); + assertEquals(100, unmarshalledObject.getYetAnotherIntInstance()); + assertEquals(100, unmarshalledObject.getAnIntInstance()); } - @Test - public void arrayTypes() { - final ArrayHolder container = new ArrayHolder(); - final StringHolder instance1 = new StringHolder(); - instance1.setInstance("Test String 1"); - final StringHolder instance2 = new StringHolder(); - instance2.setInstance("Test String 2"); - container.setInstance(new StringHolder[] { instance1, instance2 }); - - final String json = jsonb.toJson(container); - assertEquals("{\"instance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}]}", json); - - final ArrayHolder unmarshalledObject = jsonb.fromJson( - "{ \"instance\" : [ { \"instance\" : \"Test String 1\" }, { \"instance\" : \"Test String 2\" } ] }", - ArrayHolder.class); - assertEquals("Test String 1", unmarshalledObject.getInstance()[0].getInstance()); - } + @JsonbPropertyOrder({ "longInstance", "intInstance", "stringInstance" }) + public static class PartialOrder { + private int intInstance; - public static class StringHolder implements Holder<String> { - private String instance = "Test"; + private String stringInstance; - public String getInstance() { - return instance; - } + private long longInstance; - public void setInstance(final String instance) { - this.instance = instance; + private int anIntInstance; + + private int anotherIntInstance; + + private int yetAnotherIntInstance; + + public int getAnIntInstance() { + intInstance -= 10; + return anIntInstance; } - } - public static class SimpleContainerDeserializer implements JsonbDeserializer<StringHolder> { - @Override - public StringHolder deserialize(final JsonParser parser, final DeserializationContext ctx, final Type type) { - final StringHolder container = new StringHolder(); - - while (parser.hasNext()) { - final JsonParser.Event event = parser.next(); - if (event == JsonParser.Event.START_OBJECT) { - continue; - } - if (event == JsonParser.Event.END_OBJECT) { - break; - } - if (event == JsonParser.Event.KEY_NAME && "instance".equals(parser.getString())) { - container.setInstance(ctx.deserialize(String.class, parser) + " Deserialized"); - } - } + public void setAnIntInstance(int anIntInstance) { + intInstance -= 30; + this.anIntInstance = anIntInstance; + } - return container; + public int getAnotherIntInstance() { + intInstance -= 100; + return anotherIntInstance; } - } - public static class SimpleContainerSerializer implements JsonbSerializer<StringHolder> { - @Override - public void serialize(final StringHolder container, final JsonGenerator generator, - final SerializationContext ctx) { - generator.writeStartObject(); - ctx.serialize("instance", container.getInstance() + " Serialized", generator); - generator.writeEnd(); + public void setAnotherIntInstance(int anotherIntInstance) { + intInstance -= 300; + this.anotherIntInstance = anotherIntInstance; } - } - public static class HolderHolder implements Holder<StringHolder> { - @JsonbTypeSerializer(SimpleContainerSerializer.class) - @JsonbTypeDeserializer(SimpleContainerDeserializer.class) - private StringHolder instance; + public int getYetAnotherIntInstance() { + intInstance -= 1000; + return yetAnotherIntInstance; + } - @Override - public StringHolder getInstance() { - return instance; + public void setYetAnotherIntInstance(int yetAnotherIntInstance) { + intInstance -= 3000; + this.yetAnotherIntInstance = yetAnotherIntInstance; } - @Override - public void setInstance(StringHolder instance) { - this.instance = instance; + public String getStringInstance() { + return stringInstance; } - } - public static class ArrayHolder implements Holder<StringHolder[]> { - @JsonbTypeSerializer(StringArraySerializer.class) - @JsonbTypeDeserializer(StringArrayDeserializer.class) - private StringHolder[] instance; + public void setStringInstance(String stringInstance) { + this.stringInstance = stringInstance; + if (intInstance == 1) { + intInstance = 2; + } + } - @Override - public StringHolder[] getInstance() { - return instance; + public int getIntInstance() { + return intInstance; } - @Override - public void setInstance(final StringHolder[] instance) { - this.instance = instance; + public void setIntInstance(int intInstance) { + this.intInstance = intInstance; } - } - public static class StringArraySerializer implements JsonbSerializer<StringHolder[]> { - @Override - public void serialize(final StringHolder[] containers, - final JsonGenerator jsonGenerator, - final SerializationContext serializationContext) { - jsonGenerator.writeStartArray(); - for (final StringHolder container : containers) { - serializationContext.serialize(container, jsonGenerator); - } - jsonGenerator.writeEnd(); + public long getLongInstance() { + return longInstance; } - } - public static class StringArrayDeserializer implements JsonbDeserializer<StringHolder[]> { - @Override - public StringHolder[] deserialize(final JsonParser jsonParser, - final DeserializationContext deserializationContext, - final Type type) { - final List<StringHolder> containers = new LinkedList<>(); - - while (jsonParser.hasNext()) { - JsonParser.Event event = jsonParser.next(); - if (event == JsonParser.Event.START_OBJECT) { - containers.add(deserializationContext.deserialize( - new StringHolder() {}.getClass().getGenericSuperclass(), jsonParser)); - } - if (event == JsonParser.Event.END_OBJECT) { - break; - } + public void setLongInstance(long longInstance) { + this.longInstance = longInstance; + if (intInstance == 2) { + intInstance = 3; } - - return containers.toArray(new StringHolder[0]); } } diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java index 2bd6f38..7945835 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -41,9 +42,48 @@ import javax.json.bind.serializer.SerializationContext; import javax.json.stream.JsonGenerator; import javax.json.stream.JsonParser; +import org.apache.johnzon.jsonb.model.Holder; +import org.apache.johnzon.jsonb.test.JsonbRule; +import org.junit.Rule; import org.junit.Test; public class SerializerTest { + @Rule + public final JsonbRule jsonb = new JsonbRule(); + + @Test + public void typeSerializer() { + final HolderHolder container = new HolderHolder(); + final StringHolder instance = new StringHolder(); + instance.setInstance("Test String"); + container.setInstance(instance); + + final String json = jsonb.toJson(container); + assertTrue(json, json.matches( + "\\{\\s*\"instance\"\\s*:\\s*\\{\\s*\"instance\"\\s*:\\s*\"Test String Serialized\"\\s*}\\s*}")); + + final HolderHolder unmarshalledObject = jsonb.fromJson("{ \"instance\" : { \"instance\" : \"Test String\" } }", HolderHolder.class); + assertEquals("Test String Deserialized", unmarshalledObject.getInstance().getInstance()); + } + + @Test + public void arrayTypes() { + final ArrayHolder container = new ArrayHolder(); + final StringHolder instance1 = new StringHolder(); + instance1.setInstance("Test String 1"); + final StringHolder instance2 = new StringHolder(); + instance2.setInstance("Test String 2"); + container.setInstance(new StringHolder[] { instance1, instance2 }); + + final String json = jsonb.toJson(container); + assertEquals("{\"instance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}]}", json); + + final ArrayHolder unmarshalledObject = jsonb.fromJson( + "{ \"instance\" : [ { \"instance\" : \"Test String 1\" }, { \"instance\" : \"Test String 2\" } ] }", + ArrayHolder.class); + assertEquals("Test String 1", unmarshalledObject.getInstance()[0].getInstance()); + } + @Test public void roundTrip() { @@ -131,6 +171,116 @@ public class SerializerTest { jsonb.close(); } + public static class StringHolder implements Holder<String> { + private String instance = "Test"; + + public String getInstance() { + return instance; + } + + public void setInstance(final String instance) { + this.instance = instance; + } + } + + public static class SimpleContainerDeserializer implements JsonbDeserializer<StringHolder> { + @Override + public StringHolder deserialize(final JsonParser parser, final DeserializationContext ctx, final Type type) { + final StringHolder container = new StringHolder(); + + while (parser.hasNext()) { + final JsonParser.Event event = parser.next(); + if (event == JsonParser.Event.START_OBJECT) { + continue; + } + if (event == JsonParser.Event.END_OBJECT) { + break; + } + if (event == JsonParser.Event.KEY_NAME && "instance".equals(parser.getString())) { + container.setInstance(ctx.deserialize(String.class, parser) + " Deserialized"); + } + } + + return container; + } + } + + public static class SimpleContainerSerializer implements JsonbSerializer<StringHolder> { + @Override + public void serialize(final StringHolder container, final JsonGenerator generator, + final SerializationContext ctx) { + generator.writeStartObject(); + ctx.serialize("instance", container.getInstance() + " Serialized", generator); + generator.writeEnd(); + } + } + + public static class HolderHolder implements Holder<StringHolder> { + @JsonbTypeSerializer(SimpleContainerSerializer.class) + @JsonbTypeDeserializer(SimpleContainerDeserializer.class) + private StringHolder instance; + + @Override + public StringHolder getInstance() { + return instance; + } + + @Override + public void setInstance(StringHolder instance) { + this.instance = instance; + } + } + + public static class ArrayHolder implements Holder<StringHolder[]> { + @JsonbTypeSerializer(StringArraySerializer.class) + @JsonbTypeDeserializer(StringArrayDeserializer.class) + private StringHolder[] instance; + + @Override + public StringHolder[] getInstance() { + return instance; + } + + @Override + public void setInstance(final StringHolder[] instance) { + this.instance = instance; + } + } + + public static class StringArraySerializer implements JsonbSerializer<StringHolder[]> { + @Override + public void serialize(final StringHolder[] containers, + final JsonGenerator jsonGenerator, + final SerializationContext serializationContext) { + jsonGenerator.writeStartArray(); + for (final StringHolder container : containers) { + serializationContext.serialize(container, jsonGenerator); + } + jsonGenerator.writeEnd(); + } + } + + public static class StringArrayDeserializer implements JsonbDeserializer<StringHolder[]> { + @Override + public StringHolder[] deserialize(final JsonParser jsonParser, + final DeserializationContext deserializationContext, + final Type type) { + final List<StringHolder> containers = new LinkedList<>(); + + while (jsonParser.hasNext()) { + JsonParser.Event event = jsonParser.next(); + if (event == JsonParser.Event.START_OBJECT) { + containers.add(deserializationContext.deserialize( + new StringHolder() {}.getClass().getGenericSuperclass(), jsonParser)); + } + if (event == JsonParser.Event.END_OBJECT) { + break; + } + } + + return containers.toArray(new StringHolder[0]); + } + } public static class Foo { public String name; diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java index 0068880..886f29a 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java @@ -334,13 +334,17 @@ public class MappingParserImpl implements MappingParser { jsonPointers.put(jsonPointer.toString(), t); } - for (final Map.Entry<String, Mappings.Setter> setter : classMapping.setters.entrySet()) { - final JsonValue jsonValue = object.get(setter.getKey()); + for (final Map.Entry<String, JsonValue> jsonEntry : object.entrySet()) { + final Mappings.Setter value = classMapping.setters.get(jsonEntry.getKey()); + if (value == null) { + continue; + } + + final JsonValue jsonValue = jsonEntry.getValue(); final JsonValue.ValueType valueType = jsonValue != null ? jsonValue.getValueType() : null; - final Mappings.Setter value = setter.getValue(); if (JsonValue.class == value.paramType) { - setter.getValue().writer.write(t, jsonValue); + value.writer.write(t, jsonValue); continue; } if (jsonValue == null) { @@ -353,7 +357,7 @@ public class MappingParserImpl implements MappingParser { } else { Object existingInstance = null; if (config.isReadAttributeBeforeWrite()) { - final Mappings.Getter getter = classMapping.getters.get(setter.getKey()); + final Mappings.Getter getter = classMapping.getters.get(jsonEntry.getKey()); if (getter != null) { try { existingInstance = getter.reader.read(t); @@ -365,7 +369,7 @@ public class MappingParserImpl implements MappingParser { final Object convertedValue = toValue( existingInstance, jsonValue, value.converter, value.itemConverter, value.paramType, value.objectConverter, - new JsonPointerTracker(jsonPointer, setter.getKey()), inType); + new JsonPointerTracker(jsonPointer, jsonEntry.getKey()), inType); if (convertedValue != null) { setterMethod.write(t, convertedValue); } diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java index dbf8e00..97cfab1 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java @@ -27,7 +27,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; public final class Meta { @@ -81,11 +83,23 @@ public final class Meta { } public static <T extends Annotation> T getAnnotation(final Class<?> clazz, final Class<T> api) { - final T annotation = clazz.getAnnotation(api); - if (annotation != null) { - return annotation; + Class<?> current = clazz; + final Set<Class<?>> visited = new HashSet<>(); + while (current != null && current != Object.class) { + if (!visited.add(current)) { + return null; + } + final T annotation = current.getAnnotation(api); + if (annotation != null) { + return annotation; + } + final T meta = findMeta(clazz.getAnnotations(), api); + if (meta != null) { + return meta; + } + current = current.getSuperclass(); } - return findMeta(clazz.getAnnotations(), api); + return null; } public static <T extends Annotation> T getAnnotation(final Package pck, final Class<T> api) { diff --git a/pom.xml b/pom.xml index 3f26909..7b77653 100644 --- a/pom.xml +++ b/pom.xml @@ -313,9 +313,10 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>2.19.1</version> + <version>3.0.0-M3</version> <configuration> <argLine>${surefire.jvm.params}</argLine> + <trimStackTrace>false</trimStackTrace> </configuration> </plugin>
