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 ef9fc08  JOHNZON-309 more tolerance of adapters (string)
ef9fc08 is described below

commit ef9fc08a3d4846534449eb37833470837d808da7
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Fri Mar 27 10:41:51 2020 +0100

    JOHNZON-309 more tolerance of adapters (string)
---
 .../java/org/apache/johnzon/jsonb/AdapterTest.java | 37 ++++++++++++++++++++++
 .../java/org/apache/johnzon/mapper/Mapper.java     |  8 ++++-
 .../org/apache/johnzon/mapper/MapperConfig.java    | 32 ++++++++++++++++++-
 .../apache/johnzon/mapper/MappingParserImpl.java   | 34 ++++++++++++++++++--
 .../apache/johnzon/mapper/internal/AdapterKey.java | 18 +++++++++++
 5 files changed, 125 insertions(+), 4 deletions(-)

diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
index dca4576..9a69bb7 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
@@ -29,6 +29,8 @@ import javax.json.bind.JsonbConfig;
 import javax.json.bind.adapter.JsonbAdapter;
 import javax.json.bind.annotation.JsonbTypeAdapter;
 import javax.json.bind.config.PropertyOrderStrategy;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -41,6 +43,41 @@ import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 public class AdapterTest {
+    public static class PathAdapter implements JsonbAdapter<Path, JsonString> {
+        @Override
+        public JsonString adaptToJson(final Path path) {
+            return Json.createValue(path.toString());
+        }
+
+        @Override
+        public Path adaptFromJson(final JsonString jsonString) {
+            return Paths.get(jsonString.getString());
+        }
+    }
+
+    public static class PathWrapper {
+        public Path path;
+    }
+
+    @Test
+    public void testSerialize() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create(new 
JsonbConfig().withAdapters(new PathAdapter()))) {
+            final PathWrapper wrapper = new PathWrapper();
+            wrapper.path = Paths.get("/example/file.txt");
+            assertEquals("{\"path\":\"/example/file.txt\"}", 
jsonb.toJson(wrapper));
+            assertEquals("\"/example/file.txt\"", jsonb.toJson(wrapper.path));
+        }
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create(new 
JsonbConfig().withAdapters(new PathAdapter()))) {
+            final Path expected = Paths.get("/example/file.txt");
+            assertEquals(expected, 
jsonb.fromJson("{\"path\":\"/example/file.txt\"}", PathWrapper.class).path);
+            assertEquals(expected, jsonb.fromJson("\"/example/file.txt\"", 
Path.class));
+        }
+    }
+
     @Test
     public void adapt() throws Exception {
         try (final Jsonb jsonb = JsonbBuilder.create(new 
JsonbConfig().withAdapters(new BarAdapter()))) {
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 3616bf7..391c60f 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -44,6 +44,7 @@ import javax.json.JsonBuilderFactory;
 import javax.json.JsonObject;
 import javax.json.JsonReader;
 import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
 import javax.json.JsonStructure;
 import javax.json.JsonValue;
 import javax.json.spi.JsonProvider;
@@ -195,8 +196,13 @@ public class Mapper implements Closeable {
             return;
         }
 
+        final Adapter adapter = config.findAdapter(object.getClass());
+        if (adapter != null && TypeAwareAdapter.class.isInstance(adapter) && 
TypeAwareAdapter.class.cast(adapter).getTo() == JsonString.class) {
+            writeObject(adapter.from(object), stream);
+            return;
+        }
         try (final JsonGenerator generator = 
generatorFactory.createGenerator(stream(stream))) {
-            writeObjectWithGenerator(object, generator);
+            writeObjectWithGenerator(adapter == null ? object : 
adapter.from(object), generator);
         }
     }
 
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
index d3ef464..3046376 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -26,14 +26,18 @@ import org.apache.johnzon.mapper.internal.ConverterAdapter;
 import javax.json.JsonValue;
 import java.lang.reflect.Type;
 import java.nio.charset.Charset;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
+import static java.util.stream.Collectors.toList;
+
 /**
  * Contains internal configuration for all the mapper stuff.
  * It needs to be immutable and 100% runtime oriented.
@@ -86,6 +90,9 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig 
implements Cloneable {
     private final Map<Class<?>, ObjectConverter.Writer<?>> 
objectConverterWriterCache;
     private final Map<Class<?>, ObjectConverter.Reader<?>> 
objectConverterReaderCache;
 
+    private final Collection<Type> noParserAdapterTypes = new 
ConcurrentHashMap<Type, Boolean>().keySet(true);
+    private final Collection<Type> noGeneratorAdapterTypes = new 
ConcurrentHashMap<Type, Boolean>().keySet(true);
+
     //disable checkstyle for 10+ parameters
     //CHECKSTYLE:OFF
     public MapperConfig(final ConcurrentMap<AdapterKey, Adapter<?, ?>> 
adapters,
@@ -149,6 +156,14 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig 
implements Cloneable {
         this.deduplicateObjects = deduplicateObjects;
     }
 
+    public Collection<Type> getNoParserAdapterTypes() {
+        return noParserAdapterTypes;
+    }
+
+    public Collection<Type> getNoGeneratorAdapterTypes() {
+        return noGeneratorAdapterTypes;
+    }
+
     public Function<String, Class<?>> getTypeLoader() {
         return typeLoader;
     }
@@ -186,7 +201,11 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig 
implements Cloneable {
     }
 
     public Adapter findAdapter(final Type aClass) {
-        final Adapter<?, ?> converter = adapters.get(new AdapterKey(aClass, 
String.class));
+        if (getNoGeneratorAdapterTypes().contains(aClass)) { // avoid to 
create a key for nothing
+            return null;
+        }
+
+        final Adapter<?, ?> converter = adapters.get(new AdapterKey(aClass, 
String.class, true));
         if (converter != null) {
             return converter;
         }
@@ -204,6 +223,17 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig 
implements Cloneable {
                 return enumConverter;
             }
         }
+        final List<AdapterKey> matched = adapters.keySet().stream()
+                .filter(k -> k.isAssignableFrom(aClass))
+                .collect(toList());
+        if (matched.size() == 1) {
+            final Adapter<?, ?> adapter = 
adapters.get(matched.iterator().next());
+            if (TypeAwareAdapter.class.isInstance(adapter)) {
+                adapters.put(new AdapterKey(aClass, 
TypeAwareAdapter.class.cast(adapter).getTo()), adapter);
+            }
+            return adapter;
+        }
+        getNoGeneratorAdapterTypes().add(aClass);
         return null;
     }
 
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 0ec10bc..26d3e12 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
@@ -77,6 +77,7 @@ import java.util.stream.LongStream;
 import java.util.stream.Stream;
 
 import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.toList;
 import static javax.json.JsonValue.ValueType.ARRAY;
 import static javax.json.JsonValue.ValueType.FALSE;
 import static javax.json.JsonValue.ValueType.NULL;
@@ -164,6 +165,20 @@ public class MappingParserImpl implements MappingParser {
             if (classMapping != null && classMapping.adapter != null) {
                 return (T) 
classMapping.adapter.to(JsonString.class.cast(jsonValue).getString());
             }
+
+            final Adapter adapter = findAdapter(targetType);
+            if (adapter != null && TypeAwareAdapter.class.isInstance(adapter)) 
{
+                final TypeAwareAdapter typeAwareAdapter = 
TypeAwareAdapter.class.cast(adapter);
+                if (typeAwareAdapter.getTo() == String.class) {
+                    return (T) 
adapter.to(JsonString.class.cast(jsonValue).getString());
+                }
+                if (typeAwareAdapter.getTo() == JsonString.class) {
+                    return (T) adapter.to(JsonString.class.cast(jsonValue));
+                }
+                if (typeAwareAdapter.getTo() == CharSequence.class) {
+                    return (T) 
adapter.to(JsonString.class.cast(jsonValue).getChars());
+                }
+            }
         }
         if (JsonNumber.class.isInstance(jsonValue)) {
             final JsonNumber number = JsonNumber.class.cast(jsonValue);
@@ -235,6 +250,7 @@ public class MappingParserImpl implements MappingParser {
         if (FALSE == valueType && (Boolean.class == targetType || 
boolean.class == targetType || Object.class == targetType)) {
             return (T) Boolean.FALSE;
         }
+
         throw new IllegalArgumentException("Unsupported " + jsonValue + " for 
type " + targetType);
     }
 
@@ -1093,10 +1109,13 @@ public class MappingParserImpl implements MappingParser 
{
     }
 
     /**
-     * @deprecated see MapperConfig
+     * @deprecated see MapperConfig - it is acually reversed so maybe not 
deprecated after all?
      */
     private Adapter findAdapter(final Type aClass) {
-        final Adapter<?, ?> converter = config.getAdapters().get(new 
AdapterKey(aClass, String.class));
+        if (config.getNoParserAdapterTypes().contains(aClass)) {
+            return null;
+        }
+        final Adapter<?, ?> converter = config.getAdapters().get(new 
AdapterKey(aClass, String.class, true));
         if (converter != null) {
             return converter;
         }
@@ -1108,6 +1127,17 @@ public class MappingParserImpl implements MappingParser {
                 return enumConverter;
             }
         }
+        final List<AdapterKey> matched = config.getAdapters().keySet().stream()
+                .filter(k -> k.isAssignableFrom(aClass))
+                .collect(toList());
+        if (matched.size() == 1) {
+            final Adapter<?, ?> adapter = 
config.getAdapters().get(matched.iterator().next());
+            if (TypeAwareAdapter.class.isInstance(adapter)) {
+                config.getAdapters().put(new AdapterKey(aClass, 
TypeAwareAdapter.class.cast(adapter).getTo()), adapter);
+            }
+            return adapter;
+        }
+        config.getNoParserAdapterTypes().add(aClass);
         return null;
     }
 
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/AdapterKey.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/AdapterKey.java
index 9494ca2..27085c7 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/AdapterKey.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/AdapterKey.java
@@ -24,10 +24,20 @@ public class AdapterKey {
     private final Type from;
     private final Type to;
     private final int hash;
+    private Class valueAsClass;
+    private Class<?> keyAsClass;
 
     public AdapterKey(final Type from, final Type to) {
+        this(from, to, false);
+    }
+
+    public AdapterKey(final Type from, final Type to, final boolean lookup) {
         this.from = from;
         this.to = to;
+        if (!lookup) {
+            this.keyAsClass = Class.class.isInstance(from) ? 
Class.class.cast(from) : null;
+            this.valueAsClass = Class.class.isInstance(to) ? 
Class.class.cast(to) : null;
+        }
 
         int result = from.hashCode();
         result = 31 * result + to.hashCode();
@@ -56,6 +66,14 @@ public class AdapterKey {
 
     }
 
+    public boolean isAssignableFrom(final Type type) {
+        return keyAsClass != null && Class.class.isInstance(type) && 
keyAsClass.isAssignableFrom(Class.class.cast(type));
+    }
+
+    public boolean isAssignableTo(final Type type) {
+        return valueAsClass != null && Class.class.isInstance(type) && 
valueAsClass.isAssignableFrom(Class.class.cast(type));
+    }
+
     @Override
     public int hashCode() {
         return hash;

Reply via email to