FELIX-4357: Support types beside String/String[] in @Property annotation.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1554079 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java
index bce5d82..2310721 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.dm.runtime;
 
+import java.lang.reflect.Array;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
@@ -41,8 +42,29 @@
     private HashMap<String, Object> m_metadata = new HashMap<String, Object>();
 
     /**
-     * Decodes SON metadata for either a Service or a Dependency descriptor entry.
-     * The JSON object contains some of the following types: a String, a String[], or a Dictionary of String/String[].
+     * Decodes Json metadata for either a Service or a Dependency descriptor entry.
+     * The JSON object has the following form:
+     * 
+     * entry            ::= String | String[] | dictionary
+     * dictionary       ::= key-value-pair*
+     * key-value-pair   ::= key value
+     * value            ::= String | String[] | value-type
+     * value-type       ::= jsonObject with value-type-info
+     * value-type-info  ::= "type"=primitive java type
+     *                      "value"=String|String[]
+     *                      
+     * Exemple:
+     * 
+     * {"string-param" : "string-value",
+     *  "string-array-param" : ["string1", "string2"],
+     *  "properties" : {
+     *      "string-param" : "string-value",
+     *      "string-array-param" : ["str1", "str2],
+     *      "long-param" : {"type":"java.lang.Long", "value":"1"}}
+     *      "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+     *  }
+     * }
+     *   
      * @param jso the JSON object that corresponds to a dependency manager descriptor entry line.
      * @throws JSONException 
      */
@@ -61,39 +83,180 @@
             }
             else if (value instanceof JSONArray)
             {
-                String[] array = decodeStringArray((JSONArray) value);
-                m_metadata.put(key, array);
+                m_metadata.put(key, decodeStringArray((JSONArray) value));
             }
             else if (value instanceof JSONObject)
             {
-                Hashtable<String, Object> h = new Hashtable<String, Object>();
-                JSONObject obj = ((JSONObject) value);
-                Iterator<String> it2 = obj.keys();
-                while (it2.hasNext())
-                {
-                    String key2 = it2.next();
-                    Object value2 = obj.get(key2);
-                    if (value2 instanceof String)
-                    {
-                        h.put(key2, value2);
-                    }
-                    else if (value2 instanceof JSONArray)
-                    {
-                        String[] array = decodeStringArray((JSONArray) value2);
-                        h.put(key2, array);
-                    }
-                    else
-                    {
-                        throw new IllegalArgumentException("Could not decode JSON metadata: key " + key +
-                                "contains an invalid dictionary key: " + key
-                                + " (the value is neither a String nor a String[]).");
-                    }
-                }
-                m_metadata.put(key, h);
+                m_metadata.put(key, parseProperties((JSONObject) value));
             }
         }
     }
 
+    private Hashtable<String, Object> parseProperties(JSONObject properties) throws JSONException {
+        Hashtable<String, Object> parsedProps = new Hashtable<String, Object>();
+        Iterator<String> it = properties.keys();
+        while (it.hasNext())
+        {
+            String key = it.next();
+            Object value = properties.get(key);
+            if (value instanceof String)
+            {
+                // This property type is a simple string
+                parsedProps.put(key, value);
+            }
+            else if (value instanceof JSONArray)
+            {
+                // This property type is a simple string array
+                parsedProps.put(key, decodeStringArray((JSONArray) value));
+            }
+            else if (value instanceof JSONObject)
+            {
+                // This property type is a typed value, encoded as a JSONObject with two keys: "type"/"value"
+                JSONObject json = ((JSONObject) value);
+                String type = json.getString("type");
+                Object typeValue = json.get("value");
+
+                if (type == null)
+                {
+                    throw new JSONException("missing type attribute in json metadata for key " + key);
+                }
+                if (typeValue == null)
+                {
+                    throw new JSONException("missing type value attribute in json metadata for key " + key);
+                }
+
+                Class<?> typeClass;
+                try
+                {
+                    typeClass = Class.forName(type);
+                }
+                catch (ClassNotFoundException e)
+                {
+                    throw new JSONException("invalid type attribute (" + type + ") in json metadata for key "
+                        + key);
+                }
+
+                if (typeValue instanceof JSONArray)
+                {
+                    parsedProps.put(key, toPrimitiveTypeArray(typeClass, (JSONArray) typeValue));
+                }
+                else
+                {
+                    parsedProps.put(key, toPrimitiveType(typeClass, typeValue.toString()));
+                }
+            }
+        }
+        return parsedProps;
+    }
+
+    private Object toPrimitiveType(Class<?> type, String value) throws JSONException {
+        if (type.equals(String.class))
+        {
+            return value;
+        }
+        else if (type.equals(Long.class))
+        {
+            return Long.parseLong(value);
+        }
+        else if (type.equals(Double.class))
+        {
+            return Double.valueOf(value);
+        }
+        else if (type.equals(Float.class))
+        {
+            return Float.valueOf(value);
+        }
+        else if (type.equals(Integer.class))
+        {
+            return Integer.valueOf(value);
+        }
+        else if (type.equals(Byte.class))
+        {
+            return Byte.valueOf(value);
+        }
+        else if (type.equals(Character.class))
+        {
+            return Character.valueOf((char) Integer.parseInt(value));
+        }
+        else if (type.equals(Boolean.class))
+        {
+            return Boolean.valueOf(value);
+        }
+        else if (type.equals(Short.class))
+        {
+            return Short.valueOf(value);
+        }
+        else
+        {
+            throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+        }
+    }
+
+    private Object toPrimitiveTypeArray(Class<?> type, JSONArray array) throws JSONException {
+        int len = array.length();
+        Object result = Array.newInstance(type, len);
+
+        if (type.equals(String.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, array.getString(i));
+            }
+        } 
+        else if (type.equals(Long.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Long.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Double.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Double.valueOf(array.getString(i)));
+            }
+        } 
+        else if (type.equals(Float.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Float.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Integer.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Integer.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Byte.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Byte.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Character.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i,  Character.valueOf((char) Integer.parseInt(array.getString(i))));
+            }
+        }
+        else if (type.equals(Boolean.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Boolean.valueOf(array.getString(i)));
+            }
+        } 
+        else if (type.equals(Short.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Short.valueOf(array.getString(i)));
+            }
+        }
+        else 
+        {
+            throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+        }   
+        return result;
+    }
+
     /**
      * Close this class instance to another one.
      */
@@ -108,12 +271,12 @@
 
     public String getString(Params key)
     {
-        String value = (String) m_metadata.get(key.toString());
+        Object value = m_metadata.get(key.toString());
         if (value == null)
         {
             throw new IllegalArgumentException("Parameter " + key + " not found");
         }
-        return value;
+        return value.toString();
     }
 
     public String getString(Params key, String def)
@@ -130,12 +293,15 @@
 
     public int getInt(Params key)
     {
-        String value = getString(key, null);
+        Object value = m_metadata.get(key.toString());
         if (value != null)
         {
             try
             {
-                return Integer.parseInt(value);
+                if (value instanceof Integer) {
+                    return ((Integer) value).intValue();
+                }
+                return Integer.parseInt(value.toString());
             }
             catch (NumberFormatException e)
             {
@@ -153,12 +319,15 @@
 
     public int getInt(Params key, int def)
     {
-        String value = getString(key, null);
+        Object value = m_metadata.get(key.toString());
         if (value != null)
         {
             try
             {
-                return Integer.parseInt(value);
+                if (value instanceof Integer) {
+                    return ((Integer) value).intValue();
+                }
+                return Integer.parseInt(value.toString());
             }
             catch (NumberFormatException e)
             {
@@ -175,12 +344,15 @@
 
     public long getLong(Params key)
     {
-        String value = getString(key, null);
+        Object value = m_metadata.get(key.toString());
         if (value != null)
         {
             try
             {
-                return Long.parseLong(value);
+                if (value instanceof Long) {
+                    return ((Long) value).longValue();
+                }
+                return Long.parseLong(value.toString());
             }
             catch (NumberFormatException e)
             {
@@ -198,12 +370,15 @@
 
     public long getLong(Params key, long def)
     {
-        String value = getString(key, null);
+        Object value = m_metadata.get(key.toString());
         if (value != null)
         {
             try
             {
-                return Long.parseLong(value);
+                if (value instanceof Long) {
+                    return (Long) value;
+                }
+                return Long.parseLong(value.toString());
             }
             catch (NumberFormatException e)
             {
@@ -285,7 +460,7 @@
     {
         m_metadata.put(key.toString(), values);
     }
-
+    
     /**
      * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings).
      */