You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2016/04/15 17:56:05 UTC

svn commit: r1739326 - in /felix/trunk/converter/src: main/java/org/apache/felix/converter/impl/ConvertingImpl.java test/java/org/apache/felix/converter/impl/ConverterMapTest.java

Author: davidb
Date: Fri Apr 15 15:56:05 2016
New Revision: 1739326

URL: http://svn.apache.org/viewvc?rev=1739326&view=rev
Log:
Felix Converter Service - Initial support for Maps.

Added:
    felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java
Modified:
    felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java

Modified: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java?rev=1739326&r1=1739325&r2=1739326&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java (original)
+++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java Fri Apr 15 15:56:05 2016
@@ -25,12 +25,15 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import org.osgi.service.converter.Converter;
 import org.osgi.service.converter.Converting;
@@ -57,6 +60,7 @@ public class ConvertingImpl implements C
         m.put(Collection.class, ArrayList.class);
         m.put(List.class, ArrayList.class);
         m.put(Set.class, LinkedHashSet.class); // preserves insertion order
+        m.put(Map.class, LinkedHashMap.class); // preserves insertion order
         interfaceImplementations = Collections.unmodifiableMap(m);
     }
 
@@ -98,8 +102,12 @@ public class ConvertingImpl implements C
 
         targetCls = primitiveToBoxed(targetCls);
 
-        if (targetCls.isAssignableFrom(object.getClass()))
-            return object;
+        if (!Map.class.isAssignableFrom(targetCls) &&
+                !Collections.class.isAssignableFrom(targetCls)) {
+            // For maps and collections we always want copies returned
+            if (targetCls.isAssignableFrom(object.getClass()))
+                return object;
+        }
 
         Object res = trySpecialCases(targetCls);
         if (res != null)
@@ -109,8 +117,9 @@ public class ConvertingImpl implements C
             return convertToArray(targetCls);
         } else if (Collection.class.isAssignableFrom(targetCls)) {
             return convertToCollection(targetCls, typeArguments);
+        } else if (isMapType(targetCls)) {
+            return convertToMapType(targetCls, typeArguments);
         }
-        // TODO maps
 
         // At this point we know that the target is a 'singular' type: not a map, collection or array
         if (object instanceof Collection) {
@@ -135,6 +144,59 @@ public class ConvertingImpl implements C
         }
     }
 
+    private Object convertToMapType(Class<?> targetCls, Type[] typeArguments) {
+        if (Map.class.isAssignableFrom(targetCls))
+            return convertToMap(targetCls, typeArguments);
+        return null;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private Map convertToMap(Class<?> targetCls, Type[] typeArguments) {
+        Map m = mapView(object);
+        if (m == null)
+            return null;
+        Class<?> targetKeyType = null, targetValueType = null;
+        if (typeArguments != null && typeArguments.length > 1 &&
+                typeArguments[0] instanceof Class && typeArguments[1] instanceof Class) {
+            targetKeyType = (Class<?>) typeArguments[0];
+            targetValueType = (Class<?>) typeArguments[1];
+        }
+
+        Class<?> ctrCls = interfaceImplementations.get(targetCls);
+        if (ctrCls == null)
+            ctrCls = targetCls;
+
+        Map instance = (Map) createMapOrCollection(ctrCls, m.size());
+        if (instance == null)
+            return null;
+
+        for (Map.Entry entry : (Set<Entry>) m.entrySet()) {
+            Object key = entry.getKey();
+            if (targetKeyType != null)
+                key = converter.convert(key).to(targetKeyType);
+            Object value = entry.getValue();
+            if (targetValueType != null)
+                value = converter.convert(value).to(targetValueType);
+            instance.put(key, value);
+        }
+
+        return instance;
+    }
+
+    private static Map<?,?> mapView(Object obj) {
+        if (obj instanceof Map)
+            return (Map<?,?>) obj;
+        return null;
+    }
+
+    private boolean isMapType(Class<?> targetCls) {
+        // All interface types that are not Collections are treated as maps
+        if (targetCls.isInterface())
+            return true;
+        else
+            return Dictionary.class.isAssignableFrom(targetCls);
+    }
+
     @SuppressWarnings("unchecked")
     private <T> T convertToArray(Class<?> targetClass) {
         Collection<?> collectionView = collectionView(object);
@@ -154,7 +216,7 @@ public class ConvertingImpl implements C
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     private <T> T convertToCollection(Class<?> targetCls, Type[] typeArguments) {
-        Collection<?> collectionView = collectionView(object);
+        Collection<?> cv = collectionView(object);
         Class<?> targetElementType = null;
         if (typeArguments != null && typeArguments.length > 0 && typeArguments[0] instanceof Class) {
             targetElementType = (Class<?>) typeArguments[0];
@@ -164,11 +226,11 @@ public class ConvertingImpl implements C
         if (ctrCls != null)
             targetCls = ctrCls;
 
-        Collection instance = createCollection(targetCls, collectionView);
+        Collection instance = (Collection) createMapOrCollection(targetCls, cv.size());
         if (instance == null)
             return null;
 
-        for (Object o : collectionView) {
+        for (Object o : cv) {
             if (targetElementType != null)
                 o = converter.convert(o).to(targetElementType);
 
@@ -178,14 +240,14 @@ public class ConvertingImpl implements C
         return (T) instance;
     }
 
-    private static Collection<?> createCollection(Class<?> targetCls, Collection<?> collectionView) {
+    private static Object createMapOrCollection(Class<?> targetCls, int initialSize) {
         try {
             Constructor<?> ctor = targetCls.getConstructor(int.class);
-            return (Collection<?>) ctor.newInstance(collectionView.size());
+            return ctor.newInstance(initialSize);
         } catch (Exception e1) {
             try {
                 Constructor<?> ctor2 = targetCls.getConstructor();
-                return (Collection<?>) ctor2.newInstance();
+                return ctor2.newInstance();
             } catch (Exception e2) {
                 e2.printStackTrace();
             }

Added: felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java?rev=1739326&view=auto
==============================================================================
--- felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java (added)
+++ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java Fri Apr 15 15:56:05 2016
@@ -0,0 +1,50 @@
+/*
+ * 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 java.util.Collections;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+
+import static org.junit.Assert.assertEquals;
+
+public class ConverterMapTest {
+    private Converter converter;
+
+    @Before
+    public void setUp() {
+        converter = new ConverterImpl();
+    }
+
+    @After
+    public void tearDown() {
+        converter = null;
+    }
+
+    @Test
+    public void testGenericMapConversion() {
+        Map<Integer, String> m1 = Collections.singletonMap(42, "987654321");
+        Map<String, Long> m2 = converter.convert(m1).to(new TypeReference<Map<String, Long>>(){});
+        assertEquals(1, m2.size());
+        assertEquals(987654321L, (long) m2.get("42"));
+    }
+}