You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rs...@apache.org on 2016/04/18 10:18:10 UTC

[2/7] incubator-johnzon git commit: JOHNZON-72 implemented ObjectConverter lookup

JOHNZON-72 implemented ObjectConverter lookup

of course this is not done yet but a good starting point


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/c67a73d3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/c67a73d3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/c67a73d3

Branch: refs/heads/master
Commit: c67a73d3ae619e868e1ee4d8dc3049d5d2828f25
Parents: d797b9c
Author: Reinhard Sandtner <rs...@apache.org>
Authored: Thu Apr 14 14:35:29 2016 +0200
Committer: Reinhard Sandtner <rs...@apache.org>
Committed: Thu Apr 14 14:35:29 2016 +0200

----------------------------------------------------------------------
 .../org/apache/johnzon/mapper/MapperConfig.java | 107 +++++++++++
 .../apache/johnzon/mapper/MapperConfigTest.java | 180 +++++++++++++++++++
 2 files changed, 287 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/c67a73d3/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
----------------------------------------------------------------------
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 08bd77d..8e161cc 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
@@ -23,9 +23,11 @@ import org.apache.johnzon.mapper.converter.EnumConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 
+import javax.json.JsonObject;
 import java.lang.reflect.Type;
 import java.nio.charset.Charset;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 
@@ -34,6 +36,19 @@ import java.util.concurrent.ConcurrentMap;
  * It needs to be immutable and 100% runtime oriented.
  */
 class MapperConfig implements Cloneable {
+
+    private static final ObjectConverter NO_CONVERTER = new ObjectConverter() {
+        @Override
+        public void writeJson(Object instance, MappingGenerator jsonbGenerator) {
+            // just a dummy
+        }
+
+        @Override
+        public Object fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) {
+            return null;
+        }
+    };
+
     private final int version;
     private final boolean close;
     private final boolean skipNull;
@@ -47,6 +62,8 @@ class MapperConfig implements Cloneable {
     private final Map<Class<?>, ObjectConverter<?>> objectConverters;
     private final Comparator<String> attributeOrder;
 
+    private final Map<Class<?>, ObjectConverter<?>> objectConverterCache;
+
     //disable checkstyle for 10+ parameters
     //CHECKSTYLE:OFF
     public MapperConfig(final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters,
@@ -70,6 +87,8 @@ class MapperConfig implements Cloneable {
         this.encoding = encoding;
         this.adapters = adapters;
         this.attributeOrder = attributeOrder;
+
+        this.objectConverterCache = new HashMap<Class<?>, ObjectConverter<?>>(objectConverters.size());
     }
 
     public Adapter findAdapter(final Type aClass) {
@@ -88,6 +107,94 @@ class MapperConfig implements Cloneable {
         return null;
     }
 
+    /**
+     * Search for an {@link ObjectConverter} for the given class.
+     *
+     * If no {@link ObjectConverter} was found for the specific class,
+     * the whole type hierarchy will be scanned for a matching {@link ObjectConverter}.
+     *
+     * In case the given class implements more than on interfaces and for at least two
+     * we have configured an {@link ObjectConverter} the {@link ObjectConverter} for the
+     * first interface we get will be taken.
+     *
+     * @param clazz the {@link Class}
+     *
+     * @return the found {@link ObjectConverter} or {@code null} if no {@link ObjectConverter} has been found
+     *
+     * @throws IllegalArgumentException if {@code clazz} is {@code null}
+     */
+    public ObjectConverter findObjectConverter(Class clazz) {
+        if (clazz == null) {
+            throw new IllegalArgumentException("clazz must not be null");
+        }
+
+        // first lets look in our cache
+        ObjectConverter<?> converter = objectConverterCache.get(clazz);
+        if (converter != null && converter != NO_CONVERTER) {
+            return converter;
+        }
+
+        // if we have found a dummy, we return null
+        if (converter == NO_CONVERTER) {
+            return null;
+        }
+
+        // we get called the first time for this class
+        // lets search...
+
+        Map<Class<?>, ObjectConverter<?>> matchingConverters = new HashMap<Class<?>, ObjectConverter<?>>();
+
+        for (Map.Entry<Class<?>, ObjectConverter<?>> entry : objectConverters.entrySet()) {
+
+            if (clazz == entry.getKey()) {
+                converter = entry.getValue();
+                break;
+            }
+
+            if (entry.getKey().isAssignableFrom(clazz)) {
+                matchingConverters.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        if (converter != null) {
+            objectConverterCache.put(clazz, converter);
+            return converter;
+        }
+
+        if (matchingConverters.isEmpty()) {
+            objectConverterCache.put(clazz, NO_CONVERTER);
+            return null;
+        }
+
+        // search the most significant
+        Class toProcess = clazz;
+        while (toProcess != Object.class && converter == null) {
+
+            converter = matchingConverters.get(toProcess);
+            if (converter != null) {
+                break;
+            }
+
+            for (Class interfaceToSearch : toProcess.getInterfaces()) {
+
+                converter = matchingConverters.get(interfaceToSearch);
+                if (converter != null) {
+                    break;
+                }
+            }
+
+            toProcess = toProcess.getSuperclass();
+        }
+
+        if (converter == null) {
+            objectConverterCache.put(clazz, NO_CONVERTER);
+        } else {
+            objectConverterCache.put(clazz, converter);
+        }
+
+        return converter;
+    }
+
     public int getVersion() {
         return version;
     }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/c67a73d3/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
new file mode 100644
index 0000000..321bf0b
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.mapper;
+
+import org.apache.johnzon.mapper.access.FieldAccessMode;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.json.JsonObject;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MapperConfigTest {
+
+    @Test
+    public void testFindObjectConverterConverterForSpecificClass() {
+
+        ObjectConverter<ClassWithoutSupertypes> theConverter = new TheConverter<ClassWithoutSupertypes>();
+
+        Map<Class<?>, ObjectConverter<?>> converterMap = new HashMap<Class<?>, ObjectConverter<?>>(1);
+        converterMap.put(ClassWithoutSupertypes.class, theConverter);
+
+        MapperConfig config = createConfig(converterMap);
+
+        ObjectConverter converter = config.findObjectConverter(ClassWithoutSupertypes.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+    }
+
+    @Test
+    public void testFindObjectConverterConverterForInterface() {
+
+        ObjectConverter<TheInterface> theConverter = new TheConverter<TheInterface>();
+
+        MapperConfig config = createConfig(Collections.<Class<?>, ObjectConverter<?>>singletonMap(TheInterface.class, theConverter));
+
+        ObjectConverter converter = config.findObjectConverter(ClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+    }
+
+    @Test
+    public void testFindObjectConverterConverterOnlyForSuperclass() {
+
+        ObjectConverter<ClassForTheInterface> theConverter = new TheConverter<ClassForTheInterface>();
+
+        MapperConfig config = createConfig(Collections.<Class<?>, ObjectConverter<?>>singletonMap(ClassForTheInterface.class, theConverter));
+
+        ObjectConverter converter = config.findObjectConverter(ExtendingClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+    }
+
+    @Test
+    public void testFindObjectConverterConverterForInterfaceAndClass() {
+
+        ObjectConverter<TheInterface> interfaceConverter = new TheConverter<TheInterface>();
+        ObjectConverter<ClassForTheInterface> theConverter = new TheConverter<ClassForTheInterface>();
+
+        Map<Class<?>, ObjectConverter<?>> converterMap = new HashMap<Class<?>, ObjectConverter<?>>(2);
+        converterMap.put(TheInterface.class, interfaceConverter);
+        converterMap.put(ClassForTheInterface.class, theConverter);
+
+        MapperConfig config = createConfig(converterMap);
+
+        ObjectConverter converter = config.findObjectConverter(ClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+
+        converter = config.findObjectConverter(ExtendingClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+    }
+
+    @Test
+    public void testFindObjectConverterConverterForMoreInterfaces() {
+
+        ObjectConverter<TheInterface> firstConverter = new TheConverter<TheInterface>();
+        ObjectConverter<TheSecondInterface> secondConverter = new TheConverter<TheSecondInterface>();
+
+        Map<Class<?>, ObjectConverter<?>> converterMap = new HashMap<Class<?>, ObjectConverter<?>>(2);
+        converterMap.put(TheInterface.class, firstConverter);
+        converterMap.put(TheSecondInterface.class, secondConverter);
+        MapperConfig config = createConfig(converterMap);
+
+        ObjectConverter converter = config.findObjectConverter(ClassWithTwoInterfaces.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(converterMap.get(ClassWithTwoInterfaces.class.getInterfaces()[0]), converter);
+    }
+
+    @Test
+    public void testFindObjectConverterConverterForInterfaceAndClassConverterSubclasses() {
+
+        TheAbstractConverter<ClassForTheInterface> theConverter = new TheAbstractConverter<ClassForTheInterface>() {};
+
+        MapperConfig config = createConfig(Collections.<Class<?>, ObjectConverter<?>>singletonMap(ClassForTheInterface.class, theConverter));
+
+        ObjectConverter converter = config.findObjectConverter(ClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+
+        converter = config.findObjectConverter(ExtendingClassForTheInterface.class);
+        Assert.assertNotNull(converter);
+        Assert.assertEquals(theConverter, converter);
+    }
+
+
+    private MapperConfig createConfig(Map<Class<?>, ObjectConverter<?>> converter) {
+        return new MapperConfig(new ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(0),
+                                converter,
+                                -1,
+                                true,
+                                true,
+                                true,
+                                false,
+                                false,
+                                false,
+                                new FieldAccessMode(true, true),
+                                Charset.forName("UTF-8"),
+                                null);
+    }
+
+
+    private static final class ClassWithoutSupertypes {}
+
+    private interface TheInterface {}
+    private static class ClassForTheInterface implements TheInterface {}
+    private static class ExtendingClassForTheInterface extends ClassForTheInterface {}
+
+    private interface TheSecondInterface {}
+    private static class ClassWithTwoInterfaces implements TheInterface, TheSecondInterface {}
+
+
+    private static class TheConverter<T> implements ObjectConverter<T>{
+        @Override
+        public void writeJson(T instance, MappingGenerator jsonbGenerator) {
+            // dummy
+        }
+
+        @Override
+        public T fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) {
+            // dummy
+            return null;
+        }
+    }
+
+    private static abstract class TheAbstractConverter<T extends TheInterface> implements ObjectConverter<T> {
+        @Override
+        public void writeJson(T instance, MappingGenerator jsonbGenerator) {
+            // dummy
+        }
+
+        @Override
+        public T fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) {
+            // dummy
+            return null;
+        }
+    }
+}