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;
+ }
+ }
+}