From e62efc1b5f38187840a98506e1b3b9e5bc548e16 Mon Sep 17 00:00:00 2001 From: David Leangen Date: Fri, 20 Jan 2017 12:00:48 -0800 Subject: [PATCH] Updated Converter implementation to include sourceAsDTO() and targetAsDTO() Signed-off-by: David Leangen --- .../felix/converter/impl/ConvertingImpl.java | 51 ++++++++-- .../felix/converter/impl/ConverterTest.java | 92 ++++++++++++++++++- .../apache/felix/converter/impl/MyDTO8.java | 32 +++++++ 3 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 converter/converter/src/test/java/org/apache/felix/converter/impl/MyDTO8.java diff --git a/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java b/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java index dfe2d8a3123..32bc2ab57f1 100644 --- a/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java +++ b/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java @@ -70,6 +70,10 @@ public class ConvertingImpl implements Converting, InternalConverting { private List keys = new ArrayList<>(); private volatile Object root; private volatile boolean sourceAsJavaBean = false; + @SuppressWarnings( "unused" ) + private volatile boolean targetAsJavaBean = false; + private volatile boolean sourceAsDTO = false; + private volatile boolean targetAsDTO = false; ConvertingImpl(InternalConverter c, Object obj) { converter = c; @@ -84,13 +88,17 @@ public Converting sourceAs(Class cls) { @Override public Converting sourceAsBean() { + // To avoid ambiguity, reset any instruction to sourceAsDTO + sourceAsDTO = false; sourceAsJavaBean = true; return this; } @Override public Converting sourceAsDTO() { - // TODO Implement + // To avoid ambiguity, reset any instruction to sourceAsJavaBean + sourceAsJavaBean = false; + sourceAsDTO = true; return this; } @@ -102,13 +110,17 @@ public Converting targetAs(Class cls) { @Override public Converting targetAsBean() { - // TODO not yet implemented + // To avoid ambiguity, reset any instruction to targetAsDTO + targetAsDTO = false; + targetAsJavaBean = true; return this; } @Override public Converting targetAsDTO() { - // TODO Implement + // To avoid ambiguity, reset any instruction to targetAsJavaBean + targetAsJavaBean = false; + targetAsDTO = true; return this; } @@ -165,6 +177,7 @@ public T to(TypeReference ref) { return (T) to(ref.getType()); } + @SuppressWarnings( "unchecked" ) @Override public Object to(Type type) { Class cls = null; @@ -189,6 +202,11 @@ public Object to(Type type) { sourceClass = sourceAsClass != null ? sourceAsClass : object.getClass(); + // Temporary - to remove next commit!! + // This is just to catch any old code that may still be using {source|target}As(DTO.class) + if(DTO.class.equals(sourceClass) || DTO.class.equals(targetAsClass)) + throw new RuntimeException("To update!!"); + if (!isCopyRequiredType(targetAsClass) && targetAsClass.isAssignableFrom(sourceClass)) { return object; } @@ -201,7 +219,7 @@ public Object to(Type type) { return convertToArray(); } else if (Collection.class.isAssignableFrom(targetAsClass)) { return convertToCollection(); - } else if (isDTOType(targetAsClass)) { + } else if (isDTOType(targetAsClass) || ((sourceAsDTO || targetAsDTO) && DTO.class.isAssignableFrom(targetActualClass))) { return convertToDTO(); } else if (isMapType(targetAsClass)) { return convertToMapType(); @@ -291,16 +309,19 @@ private T convertToCollection() { private T convertToDTO() { Map m = mapView(object, sourceClass, converter); + Class cls = targetAsClass; + if (targetAsDTO) + cls = targetActualClass; try { T dto = (T) targetActualClass.newInstance(); for (Map.Entry entry : (Set) m.entrySet()) { Field f = null; try { - f = targetAsClass.getDeclaredField(mangleName(entry.getKey().toString())); + f = cls.getDeclaredField(mangleName(entry.getKey().toString())); } catch (NoSuchFieldException e) { try { - f = targetAsClass.getField(mangleName(entry.getKey().toString())); + f = cls.getField(mangleName(entry.getKey().toString())); } catch (NoSuchFieldException e1) { // There is not field with this name } @@ -308,7 +329,11 @@ private T convertToDTO() { if (f != null) { Object val = entry.getValue(); - f.set(dto, converter.convert(val).to(f.getType())); + if (sourceAsDTO && DTO.class.isAssignableFrom(f.getType())) + val = converter.convert(val).sourceAsDTO().to(f.getType()); + else + val = converter.convert(val).to(f.getType()); + f.set(dto, val); } } @@ -354,7 +379,11 @@ private Map convertToMap() { if (isCopyRequiredType(cls)) { cls = getConstructableType(cls); } - value = converter.convert(value).key(ka).to(cls); + if (sourceAsDTO && DTO.class.isAssignableFrom(cls)) + // sourceAsDTO or sourceAsClass??? + value = converter.convert(value).key(ka).sourceAsDTO().to(cls); + else + value = converter.convert(value).key(ka).to(cls); } } instance.put(key, value); @@ -845,11 +874,11 @@ private static void handleInterfaceMethod(Object obj, Method md, Set inv } private Map mapView(Object obj, Class sourceCls, InternalConverter converter) { - if (Map.class.isAssignableFrom(sourceCls)) + if (Map.class.isAssignableFrom(sourceCls) || (DTO.class.isAssignableFrom(sourceCls) && obj instanceof Map)) return (Map) obj; else if (Dictionary.class.isAssignableFrom(sourceCls)) return null; // TODO - else if (isDTOType(sourceCls)) + else if (isDTOType(sourceCls) || sourceAsDTO) return createMapFromDTO(obj, converter); else { if (sourceAsJavaBean) { @@ -862,6 +891,8 @@ else if (isDTOType(sourceCls)) } private static boolean isCopyRequiredType(Class cls) { + if (cls.isEnum()) + return false; return Map.class.isAssignableFrom(cls) || Collection.class.isAssignableFrom(cls) || DTO.class.isAssignableFrom(cls) || diff --git a/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java b/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java index 32321e953d3..30bf05075d8 100644 --- a/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java +++ b/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java @@ -52,6 +52,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.osgi.dto.DTO; import org.osgi.util.converter.ConversionException; import org.osgi.util.converter.Converter; import org.osgi.util.converter.ConverterBuilder; @@ -462,6 +463,72 @@ public void testDTO2Map() { assertEquals(Alpha.A, e.get("alpha")); } + @Test + public void testDTO2Map2() { + MyEmbeddedDTO embedded = new MyEmbeddedDTO(); + embedded.marco = "hohoho"; + embedded.polo = Long.MAX_VALUE; + embedded.alpha = Alpha.A; + + MyDTO dto = new MyDTO(); + dto.ping = "lalala"; + dto.pong = Long.MIN_VALUE; + dto.count = Count.ONE; + dto.embedded = embedded; + + @SuppressWarnings("rawtypes") + Map m = converter.convert(dto).sourceAsDTO().to(Map.class); + assertEquals(4, m.size()); + assertEquals("lalala", m.get("ping")); + assertEquals(Long.MIN_VALUE, m.get("pong")); + assertEquals(Count.ONE, m.get("count")); + assertNotNull(m.get("embedded")); + @SuppressWarnings("rawtypes") + Map e = (Map)m.get("embedded"); + assertEquals("hohoho", e.get("marco")); + assertEquals(Long.MAX_VALUE, e.get("polo")); + assertEquals(Alpha.A, e.get("alpha")); + } + + @Test + public void testDTO2Map3() { + MyEmbeddedDTO embedded2 = new MyEmbeddedDTO(); + embedded2.marco = "hohoho"; + embedded2.polo = Long.MAX_VALUE; + embedded2.alpha = Alpha.A; + + MyDTOWithMethods embedded = new MyDTOWithMethods(); + embedded.ping = "lalala"; + embedded.pong = Long.MIN_VALUE; + embedded.count = Count.ONE; + embedded.embedded = embedded2; + + MyDTO8 dto = new MyDTO8(); + dto.ping = "lalala"; + dto.pong = Long.MIN_VALUE; + dto.count = MyDTO8.Count.ONE; + dto.embedded = embedded; + + @SuppressWarnings("rawtypes") + Map m = converter.convert(dto).sourceAsDTO().to(Map.class); + assertEquals(4, m.size()); + assertEquals("lalala", m.get("ping")); + assertEquals(Long.MIN_VALUE, m.get("pong")); + assertEquals(MyDTO8.Count.ONE, m.get("count")); + assertNotNull(m.get("embedded")); + assertTrue(m.get( "embedded" ) instanceof MyDTOWithMethods); + MyDTOWithMethods e = (MyDTOWithMethods)m.get("embedded"); + assertEquals("lalala", e.ping); + assertEquals(Long.MIN_VALUE, e.pong); + assertEquals(Count.ONE, e.count); + assertNotNull(e.embedded); + assertTrue(e.embedded instanceof MyEmbeddedDTO); + MyEmbeddedDTO e2 = (MyEmbeddedDTO)e.embedded; + assertEquals("hohoho", e2.marco); + assertEquals(Long.MAX_VALUE, e2.polo); + assertEquals(Alpha.A, e2.alpha); + } + @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void testDTOFieldShadowing() { MySubDTO dto = new MySubDTO(); @@ -603,7 +670,7 @@ public void testConversionPriority() { } @Test - public void testConvertAs1() { + public void testConvertAsInterface() { MyBean mb = new MyBean(); mb.intfVal = 17; mb.beanVal = "Hello"; @@ -613,7 +680,7 @@ public void testConvertAs1() { } @Test - public void testConvertAs2() { + public void testConvertAsBean() { MyBean mb = new MyBean(); mb.intfVal = 17; mb.beanVal = "Hello"; @@ -622,6 +689,14 @@ public void testConvertAs2() { converter.convert(mb).sourceAsBean().to(Map.class)); } + @Test + public void testConvertAsDTO() { + MyClass3 mc3 = new MyClass3(17); + + assertEquals(17, + converter.convert(mc3).sourceAsDTO().to(Map.class).get("value")); + } + @Test public void testDTONameMangling() { Map m = new HashMap<>(); @@ -680,4 +755,17 @@ public String getValue() { return beanVal; } } + + static class MyClass3 { + public int value; + public String string = "String"; + + public MyClass3( int value ) { + this.value = value; + } + + public int value() { + return value; + } + } } diff --git a/converter/converter/src/test/java/org/apache/felix/converter/impl/MyDTO8.java b/converter/converter/src/test/java/org/apache/felix/converter/impl/MyDTO8.java new file mode 100644 index 00000000000..bc94dc1e4e2 --- /dev/null +++ b/converter/converter/src/test/java/org/apache/felix/converter/impl/MyDTO8.java @@ -0,0 +1,32 @@ +/* + * 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.felix.converter.impl; + +import org.osgi.dto.DTO; + +public class MyDTO8 extends DTO { + public enum Count { ONE, TWO, THREE } + + public Count count; + + public String ping; + + public long pong; + + public MyDTOWithMethods embedded; +} +