| /* |
| * 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.dm.runtime; |
| |
| import java.lang.reflect.Array; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * This class represents the parsed data found from meta-inf dependencymanager descriptors. |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class JSONMetaData implements MetaData, Cloneable |
| { |
| /** |
| * The parsed Dependency or Service metadata. The map value is either a String, a String[], |
| * or a Dictionary, whose values are String or String[]. |
| */ |
| private HashMap<String, Object> m_metadata = new HashMap<String, Object>(); |
| |
| /** |
| * 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 input the JSON string that corresponds to a dependency manager descriptor entry line. |
| * @throws Exception |
| */ |
| @SuppressWarnings("unchecked") |
| public JSONMetaData(String input) throws Exception |
| { |
| JsonReader jreader = new JsonReader(input); |
| Map<String, Object> m = jreader.getParsed(); |
| |
| for (Map.Entry<String, Object> e : m.entrySet()) |
| { |
| String key = e.getKey(); |
| Object value = e.getValue(); |
| if (value instanceof String) |
| { |
| m_metadata.put(key, value); |
| } else if (value instanceof List) |
| { |
| // String array |
| m_metadata.put(key, decodeStringArray((List<?>) value)); |
| } else if (value instanceof Map) |
| { |
| // properties |
| m_metadata.put(key, parseProperties((Map<String, Object>) value)); |
| } |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Hashtable<String, Object> parseProperties(Map<String, Object> properties) throws Exception { |
| Hashtable<String, Object> parsedProps = new Hashtable<String, Object>(); |
| |
| for (Map.Entry<String, Object> entry : properties.entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| |
| if (value instanceof String) |
| { |
| // This property type is a simple string |
| parsedProps.put(key, value); |
| } |
| else if (value instanceof List) |
| { |
| // This property type is a simple string array |
| parsedProps.put(key, decodeStringArray((List<?>) value)); |
| } |
| else if (value instanceof Map) |
| { |
| // This property type is a typed value, encoded as a JSONObject with two keys: "type"/"value" |
| Map<String, Object> json = ((Map<String, Object>) value); |
| String type = json.get("type").toString(); |
| Object typeValue = json.get("value"); |
| |
| if (type == null) |
| { |
| throw new Exception("missing type attribute in json metadata for key " + key); |
| } |
| if (typeValue == null) |
| { |
| throw new Exception("missing type value attribute in json metadata for key " + key); |
| } |
| |
| Class<?> typeClass; |
| try |
| { |
| typeClass = Class.forName(type); |
| } |
| catch (ClassNotFoundException e) |
| { |
| throw new Exception("invalid type attribute (" + type + ") in json metadata for key " |
| + key); |
| } |
| |
| if (typeValue instanceof List) |
| { |
| parsedProps.put(key, toPrimitiveTypeArray(typeClass, (List<?>) typeValue)); |
| } |
| else |
| { |
| parsedProps.put(key, toPrimitiveType(typeClass, typeValue.toString())); |
| } |
| } |
| } |
| |
| return parsedProps; |
| } |
| |
| private Object toPrimitiveType(Class<?> type, String value) throws Exception { |
| 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)) |
| { |
| try { |
| return Character.valueOf((char) Integer.parseInt(value)); |
| } catch (NumberFormatException e) { |
| return value.charAt(0); |
| } |
| } |
| else if (type.equals(Boolean.class)) |
| { |
| return Boolean.valueOf(value); |
| } |
| else if (type.equals(Short.class)) |
| { |
| return Short.valueOf(value); |
| } |
| else |
| { |
| throw new Exception("invalid type (" + type + ") attribute in json metadata"); |
| } |
| } |
| |
| private Object toPrimitiveTypeArray(Class<?> type, List<?> array) throws Exception { |
| int len = array.size(); |
| Object result = Array.newInstance(type, len); |
| |
| if (type.equals(String.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, array.get(i).toString()); |
| } |
| } |
| else if (type.equals(Long.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Long.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Double.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Double.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Float.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Float.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Integer.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Integer.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Byte.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Byte.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Character.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| try { |
| Array.set(result, i, Character.valueOf((char) Integer.parseInt(array.get(i).toString()))); |
| } catch (NumberFormatException e) { |
| Array.set(result, i, Character.valueOf((char) array.get(i).toString().charAt(0))); |
| } |
| } |
| } |
| else if (type.equals(Boolean.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Boolean.valueOf(array.get(i).toString())); |
| } |
| } |
| else if (type.equals(Short.class)) |
| { |
| for (int i = 0; i < len; i ++) { |
| Array.set(result, i, Short.valueOf(array.get(i).toString())); |
| } |
| } |
| else |
| { |
| throw new Exception("invalid type (" + type + ") attribute in json metadata"); |
| } |
| return result; |
| } |
| |
| /** |
| * Close this class instance to another one. |
| */ |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object clone() throws CloneNotSupportedException |
| { |
| JSONMetaData clone = (JSONMetaData) super.clone(); |
| clone.m_metadata = (HashMap<String, Object>) m_metadata.clone(); |
| return clone; |
| } |
| |
| public String getString(Params key) |
| { |
| Object value = m_metadata.get(key.toString()); |
| if (value == null) |
| { |
| throw new IllegalArgumentException("Parameter " + key + " not found"); |
| } |
| return value.toString(); |
| } |
| |
| public String getString(Params key, String def) |
| { |
| try |
| { |
| return getString(key); |
| } |
| catch (IllegalArgumentException e) |
| { |
| return def; |
| } |
| } |
| |
| public int getInt(Params key) |
| { |
| Object value = m_metadata.get(key.toString()); |
| if (value != null) |
| { |
| try |
| { |
| if (value instanceof Integer) { |
| return ((Integer) value).intValue(); |
| } |
| return Integer.parseInt(value.toString()); |
| } |
| catch (NumberFormatException e) |
| { |
| throw new IllegalArgumentException("parameter " + key |
| + " is not an int value: " |
| + value); |
| } |
| } |
| else |
| { |
| throw new IllegalArgumentException("missing " + key |
| + " parameter from annotation"); |
| } |
| } |
| |
| public int getInt(Params key, int def) |
| { |
| Object value = m_metadata.get(key.toString()); |
| if (value != null) |
| { |
| try |
| { |
| if (value instanceof Integer) { |
| return ((Integer) value).intValue(); |
| } |
| return Integer.parseInt(value.toString()); |
| } |
| catch (NumberFormatException e) |
| { |
| throw new IllegalArgumentException("parameter " + key |
| + " is not an int value: " |
| + value); |
| } |
| } |
| else |
| { |
| return def; |
| } |
| } |
| |
| public long getLong(Params key) |
| { |
| Object value = m_metadata.get(key.toString()); |
| if (value != null) |
| { |
| try |
| { |
| if (value instanceof Long) { |
| return ((Long) value).longValue(); |
| } |
| return Long.parseLong(value.toString()); |
| } |
| catch (NumberFormatException e) |
| { |
| throw new IllegalArgumentException("parameter " + key |
| + " is not a long value: " |
| + value); |
| } |
| } |
| else |
| { |
| throw new IllegalArgumentException("missing " + key |
| + " parameter from annotation"); |
| } |
| } |
| |
| public long getLong(Params key, long def) |
| { |
| Object value = m_metadata.get(key.toString()); |
| if (value != null) |
| { |
| try |
| { |
| if (value instanceof Long) { |
| return (Long) value; |
| } |
| return Long.parseLong(value.toString()); |
| } |
| catch (NumberFormatException e) |
| { |
| throw new IllegalArgumentException("parameter " + key |
| + " is not a long value: " |
| + value); |
| } |
| } |
| else |
| { |
| return def; |
| } |
| } |
| |
| public String[] getStrings(Params key) |
| { |
| Object array = m_metadata.get(key.toString()); |
| if (array == null) |
| { |
| throw new IllegalArgumentException("Parameter " + key + " not found"); |
| } |
| |
| if (!(array instanceof String[])) |
| { |
| throw new IllegalArgumentException("Parameter " + key + " is not a String[] (" + array.getClass() |
| + ")"); |
| } |
| return (String[]) array; |
| } |
| |
| public String[] getStrings(Params key, String[] def) |
| { |
| try |
| { |
| return getStrings(key); |
| } |
| catch (IllegalArgumentException t) |
| { |
| return def; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Dictionary<String, Object> getDictionary(Params key, |
| Dictionary<String, Object> def) |
| { |
| Object dictionary = m_metadata.get(key.toString()); |
| if (dictionary == null) |
| { |
| return def; |
| } |
| |
| if (!(dictionary instanceof Dictionary<?, ?>)) |
| { |
| throw new IllegalArgumentException("Parameter " + key + " is not a Dictionary (" |
| + dictionary.getClass() + ")"); |
| } |
| |
| return (Dictionary<String, Object>) dictionary; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return m_metadata.toString(); |
| } |
| |
| public void setDictionary(Params key, Dictionary<String, Object> dictionary) |
| { |
| m_metadata.put(key.toString(), dictionary); |
| } |
| |
| public void setString(Params key, String value) |
| { |
| m_metadata.put(key.toString(), value); |
| } |
| |
| public void setStrings(Params key, String[] values) |
| { |
| m_metadata.put(key.toString(), values); |
| } |
| |
| /** |
| * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings). |
| */ |
| private String[] decodeStringArray(List<?> array) |
| { |
| return array.stream().map(x -> x.toString()).toArray(String[]::new); |
| } |
| } |