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


The following commit(s) were added to refs/heads/master by this push:
     new 43eff6c  JOHNZON-314 support @JohnzonAny on a field
43eff6c is described below

commit 43eff6cb93efef0e04e75dc2e8a3f710a9265991
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Sun May 31 18:47:16 2020 +0200

    JOHNZON-314 support @JohnzonAny on a field
---
 .../org/apache/johnzon/jsonb/JsonbAccessMode.java  |  5 ++
 .../org/apache/johnzon/jsonb/AnySupportTest.java   | 94 ++++++++++++++++++++++
 .../java/org/apache/johnzon/mapper/JohnzonAny.java |  3 +-
 .../apache/johnzon/mapper/MappingParserImpl.java   | 15 +++-
 .../java/org/apache/johnzon/mapper/Mappings.java   | 12 ++-
 .../apache/johnzon/mapper/access/AccessMode.java   |  2 +
 .../johnzon/mapper/access/BaseAccessMode.java      | 23 +++++-
 7 files changed, 149 insertions(+), 5 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 3ab3fd0..1130f67 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
@@ -728,6 +728,11 @@ public class JsonbAccessMode implements AccessMode, 
Closeable {
     }
 
     @Override
+    public Field findAnyField(final Class<?> clazz) {
+        return partialDelegate.findAnyField(clazz);
+    }
+
+    @Override
     public void afterParsed(final Class<?> clazz) {
         parsingCache.remove(clazz);
         partialDelegate.afterParsed(clazz);
diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AnySupportTest.java 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AnySupportTest.java
new file mode 100644
index 0000000..1cf040f
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AnySupportTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.jsonb;
+
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.apache.johnzon.mapper.JohnzonAny;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Objects;
+
+import static java.util.Collections.singletonMap;
+import static org.junit.Assert.assertEquals;
+
+public class AnySupportTest {
+    @Rule
+    public final JsonbRule rule = new JsonbRule();
+
+    @Test
+    public void roundTrip() {
+        final Foo foo = rule.fromJson("{\"a\":\"b\"}", Foo.class);
+        assertEquals(singletonMap("a", "b"), foo.values);
+        assertEquals("{\"a\":\"b\"}", rule.toJson(foo));
+    }
+
+    @Test
+    public void subObject() {
+        final Bar object = rule.fromJson("{\"a\":{\"b\":\"c\"}}", Bar.class);
+        final Foo foo = new Foo();
+        foo.values = singletonMap("b", "c");
+        assertEquals(singletonMap("a", foo), object.values);
+        assertEquals("{\"a\":{\"b\":\"c\"}}", rule.toJson(object));
+    }
+
+    public static class Foo {
+        @JohnzonAny
+        private Map<String, String> values;
+
+        public Map<String, String> getValues() {
+            return values;
+        }
+
+        public void setValues(final Map<String, String> values) {
+            this.values = values;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            Foo foo = (Foo) o;
+            return values.equals(foo.values);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(values);
+        }
+    }
+
+    public static class Bar {
+        @JohnzonAny
+        private Map<String, Foo> values;
+
+        public Map<String, Foo> getValues() {
+            return values;
+        }
+
+        public void setValues(final Map<String, Foo> values) {
+            this.values = values;
+        }
+    }
+}
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonAny.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonAny.java
index 16338e3..f33a583 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonAny.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonAny.java
@@ -22,10 +22,11 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 @Retention(RUNTIME)
-@Target({METHOD, ANNOTATION_TYPE})
+@Target({METHOD, ANNOTATION_TYPE, FIELD})
 public @interface JohnzonAny {
 }
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 26d3e12..1b7a3a9 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
@@ -78,6 +78,7 @@ import java.util.stream.Stream;
 
 import static java.util.Arrays.asList;
 import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
 import static javax.json.JsonValue.ValueType.ARRAY;
 import static javax.json.JsonValue.ValueType.FALSE;
 import static javax.json.JsonValue.ValueType.NULL;
@@ -424,7 +425,9 @@ public class MappingParserImpl implements MappingParser {
                 final String key = entry.getKey();
                 if (!classMapping.setters.containsKey(key)) {
                     try {
-                        classMapping.anySetter.invoke(t, key, toValue(null, 
entry.getValue(), null, null, Object.class, null,
+                        classMapping.anySetter.invoke(t, key,
+                                toValue(null, entry.getValue(), null, null,
+                                        
classMapping.anySetter.getGenericParameterTypes()[1], null,
                                 isDeduplicateObjects ? new 
JsonPointerTracker(jsonPointer, entry.getKey()) : null, type));
                     } catch (final IllegalAccessException e) {
                         throw new IllegalStateException(e);
@@ -433,6 +436,16 @@ public class MappingParserImpl implements MappingParser {
                     }
                 }
             }
+        } else if (classMapping.anyField != null) {
+            final Type tRef = type;
+            try {
+                classMapping.anyField.set(t, object.entrySet().stream()
+                    .collect(toMap(Map.Entry::getKey, e -> toValue(null, 
e.getValue(), null, null,
+                            
ParameterizedType.class.cast(classMapping.anyField.getGenericType()).getActualTypeArguments()[1],
 null,
+                            isDeduplicateObjects ? new 
JsonPointerTracker(jsonPointer, e.getKey()) : null, tRef))));
+            } catch (final IllegalAccessException e) {
+                throw new IllegalStateException(e);
+            }
         }
         if (classMapping.mapAdder != null) {
             object.entrySet().stream()
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 6991c9d..0d8adbb 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -25,6 +25,7 @@ import static 
org.apache.johnzon.mapper.reflection.Generics.resolve;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
+import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
@@ -53,6 +54,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.access.FieldAccessMode;
 import org.apache.johnzon.mapper.access.MethodAccessMode;
 import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
 import org.apache.johnzon.mapper.converter.EnumConverter;
@@ -72,6 +74,7 @@ public class Mappings {
         public final ObjectConverter.Writer writer;
         public final Getter anyGetter;
         public final Method anySetter;
+        public final Field anyField;
         public final Method mapAdder;
         public final Class<?> mapAdderType;
 
@@ -83,7 +86,7 @@ public class Mappings {
                                final Map<String, Getter> getters, final 
Map<String, Setter> setters,
                                final Adapter<?, ?> adapter,
                                final ObjectConverter.Reader<?> reader, final 
ObjectConverter.Writer<?> writer,
-                               final Getter anyGetter, final Method anySetter,
+                               final Getter anyGetter, final Method anySetter, 
final Field anyField,
                                final Method mapAdder) {
             this.clazz = clazz;
             this.factory = factory;
@@ -94,6 +97,7 @@ public class Mappings {
             this.reader = reader;
             this.anyGetter = anyGetter;
             this.anySetter = anySetter;
+            this.anyField = anyField;
             this.mapAdder = mapAdder;
             this.mapAdderType = mapAdder == null ? null : 
mapAdder.getParameterTypes()[1];
         }
@@ -479,6 +483,7 @@ public class Mappings {
         }
 
         final Method anyGetter = accessMode.findAnyGetter(clazz);
+        final Field anyField = accessMode.findAnyField(clazz);
         final ClassMapping mapping = new ClassMapping(
                 clazz, accessMode.findFactory(clazz), getters, setters,
                 accessMode.findAdapter(clazz),
@@ -486,8 +491,11 @@ public class Mappings {
                 accessMode.findWriter(clazz),
                 anyGetter != null ? new Getter(
                         new MethodAccessMode.MethodReader(anyGetter, 
anyGetter.getReturnType()),
-                        false,false, false, false, true, null, null, -1, null) 
: null,
+                        false,false, false, false, true, null, null, -1, null) 
:
+                        (anyField != null ? new Getter(new 
FieldAccessMode.FieldReader(anyField, anyField.getGenericType()),
+                        false,false, false, false, true, null, null, -1, null) 
: null),
                 accessMode.findAnySetter(clazz),
+                anyField,
                 Map.class.isAssignableFrom(clazz) ? 
accessMode.findMapAdder(clazz) : null);
 
         accessMode.afterParsed(clazz);
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
index 4f12fa2..dc7a462 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
@@ -20,6 +20,7 @@ package org.apache.johnzon.mapper.access;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -113,6 +114,7 @@ public interface AccessMode {
     Adapter<?, ?> findAdapter(Class<?> clazz);
     Method findAnyGetter(Class<?> clazz);
     Method findAnySetter(Class<?> clazz);
+    Field findAnyField(Class<?> clazz);
 
     default Method findMapAdder(final Class<?> clazz) {
         return MapHelper.find((name, type, param) -> type.getMethod("add" + 
name, String.class, param), clazz);
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
index 1a5931e..edbc379 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
@@ -30,15 +30,18 @@ import java.beans.ConstructorProperties;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
@@ -302,7 +305,7 @@ public abstract class BaseAccessMode implements AccessMode {
         for (final Method current : clazz.getMethods()) {
             if (current.getAnnotation(JohnzonAny.class) != null) {
                 final Class<?>[] parameterTypes = current.getParameterTypes();
-                if (parameterTypes.length == 2 && parameterTypes[0] == 
String.class && parameterTypes[1] == Object.class) {
+                if (parameterTypes.length == 2 && parameterTypes[0] == 
String.class) {
                     if (m != null) {
                         throw new IllegalArgumentException("Ambiguous 
@JohnzonAny on " + m + " and " + current);
                     }
@@ -313,6 +316,24 @@ public abstract class BaseAccessMode implements AccessMode 
{
         return m;
     }
 
+    @Override
+    public Field findAnyField(final Class<?> clazz) {
+        if (clazz.isInterface() || clazz.isEnum()) {
+            return null;
+        }
+        Class<?> current = clazz;
+        final Set<Class<?>> visited = new HashSet<>();
+        while (current != null && current != Object.class && 
visited.add(current)) {
+            for (final Field f : current.getDeclaredFields()) {
+                if (f.isAnnotationPresent(JohnzonAny.class)) { // todo: 
validation? waiting for jsonb standard behavior
+                    return f;
+                }
+            }
+            current = clazz.getSuperclass();
+        }
+        return null;
+    }
+
     private <T> Map<String, T> sanitize(final Class<?> type, final Map<String, 
T> delegate) {
         for (final String field : fieldFilteringStrategy.select(type)) {
             delegate.remove(field);

Reply via email to