blob: b202f71ee8c5d8abb72aa4819e8b9343e1d1df7a [file] [log] [blame]
/*
* 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))
{
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 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 ++) {
Array.set(result, i, Character.valueOf((char) Integer.parseInt(array.get(i).toString())));
}
}
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);
}
}