blob: 8e25032f861dcc070bf10c507b5d473c646fb247 [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.aries.rsa.discovery.endpoint;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXBElement;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Node;
import org.osgi.xmlns.rsa.v1_0.ArrayType;
import org.osgi.xmlns.rsa.v1_0.ObjectFactory;
import org.osgi.xmlns.rsa.v1_0.PropertyType;
import org.osgi.xmlns.rsa.v1_0.ValueType;
import org.osgi.xmlns.rsa.v1_0.XmlType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class PropertiesMapper {
private static final Logger LOG = LoggerFactory.getLogger(PropertiesMapper.class);
public Map<String, Object> toProps(List<PropertyType> properties) {
Map<String, Object> map = new HashMap<>();
for (PropertyType prop : properties) {
map.put(prop.getName(), getValue(prop));
}
return map;
}
private Object getValue(PropertyType prop) {
Object value = null;
String type = getTypeName(prop);
Object content = getFirstNonText(prop.getContent());
if (content instanceof JAXBElement<?>) {
JAXBElement<?> el = (JAXBElement<?>)content;
if (el.getDeclaredType() == ArrayType.class) {
String elName = el.getName().getLocalPart();
ArrayType inValue = (ArrayType)el.getValue();
if ("array".equals(elName)) {
value = getArray(inValue, type);
} else if ("set".equals(elName)) {
value = handleCollection(inValue, new HashSet<>(), type);
} else if ("list".equals(elName)) {
value = handleCollection(inValue, new ArrayList<>(), type);
}
} else if (el.getDeclaredType() == XmlType.class) {
value = readXML((XmlType)el.getValue(), type);
}
} else {
if (prop.getValue() != null) {
value = instantiate(type, prop.getValue());
} else {
if (prop.getContent().size() > 0) {
value = instantiate(type, prop.getContent().get(0).toString());
}
}
}
return value;
}
private Object getFirstNonText(List<Serializable> contentList) {
for (Object content : contentList) {
if (content instanceof JAXBElement<?>) {
return content;
}
}
return null;
}
private static String getTypeName(PropertyType prop) {
String type = prop.getValueType();
return type == null ? "String" : type;
}
private Object getArray(ArrayType arrayEl, String type) {
List<ValueType> values = arrayEl.getValue();
Class<?> cls = null;
if ("long".equals(type)) {
cls = long.class;
} else if ("double".equals(type)) {
cls = double.class;
} else if ("float".equals(type)) {
cls = float.class;
} else if ("int".equals(type)) {
cls = int.class;
} else if ("byte".equals(type)) {
cls = byte.class;
} else if ("boolean".equals(type)) {
cls = boolean.class;
} else if ("short".equals(type)) {
cls = short.class;
}
try {
if (cls == null) {
cls = ClassLoader.getSystemClassLoader().loadClass("java.lang." + type);
}
Object array = Array.newInstance(cls, values.size());
for (int i = 0; i < values.size(); i++) {
Object val = getValue(values.get(i), type);
Array.set(array, i, val);
}
return array;
} catch (Exception e) {
LOG.warn("Could not create array for Endpoint Description", e);
return null;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Collection handleCollection(ArrayType el, Collection value, String type) {
List<ValueType> values = el.getValue();
for (ValueType val : values) {
Object obj = getValue(val, type);
value.add(obj);
}
return value;
}
private Object getValue(ValueType value, String type) {
if (value.getContent().size() == 1 && value.getContent().get(0) instanceof String) {
return handleValue((String)value.getContent().get(0), type);
}
JAXBElement<?> valueContent = (JAXBElement<?>)getFirstNonText(value.getContent());
if (valueContent.getDeclaredType() == XmlType.class) {
return readXML((XmlType)valueContent.getValue(), type);
}
return "";
}
private String readXML(XmlType el, String type) {
if (el == null) {
return null;
}
if (!"String".equals(type)) {
LOG.warn("Embedded XML must be of type String, found: " + type);
return null;
}
Node xmlContent = (Node)el.getAny();
xmlContent.normalize();
try {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
StringWriter buffer = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(xmlContent), new StreamResult(buffer));
return buffer.toString();
} catch (Exception e) {
return "";
}
}
private static Object handleValue(String val, String type) {
return instantiate(type, val);
}
private static Object instantiate(String type, String value) {
if ("String".equals(type)) {
return value;
}
value = value.trim();
String boxedType = null;
if ("long".equals(type)) {
boxedType = "Long";
} else if ("double".equals(type)) {
boxedType = "Double";
} else if ("float".equals(type)) {
boxedType = "Float";
} else if ("int".equals(type)) {
boxedType = "Integer";
} else if ("byte".equals(type)) {
boxedType = "Byte";
} else if ("char".equals(type)) {
boxedType = "Character";
} else if ("boolean".equals(type)) {
boxedType = "Boolean";
} else if ("short".equals(type)) {
boxedType = "Short";
}
if (boxedType == null) {
boxedType = type;
}
String javaType = "java.lang." + boxedType;
try {
if ("Character".equals(boxedType)) {
return value.charAt(0);
} else {
Class<?> cls = ClassLoader.getSystemClassLoader().loadClass(javaType);
Constructor<?> ctor = cls.getConstructor(String.class);
return ctor.newInstance(value);
}
} catch (Exception e) {
LOG.warn("Could not create Endpoint Property of type " + type + " and value " + value);
return null;
}
}
public List<PropertyType> fromProps(Map<String, Object> m) {
List<PropertyType> props = new ArrayList<PropertyType>();
for (Map.Entry<String, Object> entry : m.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
PropertyType propEl = new PropertyType();
propEl.setName(key);
ObjectFactory factory = new ObjectFactory();
if (val.getClass().isArray()) {
ArrayType arrayEl = new ArrayType();
propEl.getContent().add(factory.createArray(arrayEl));
for (Object o : normalizeArray(val)) {
setValueType(propEl, o);
ValueType valueType = new ValueType();
valueType.getContent().add(o.toString());
arrayEl.getValue().add(valueType);
}
} else if (val instanceof List) {
ArrayType listEl = new ArrayType();
propEl.getContent().add(factory.createList(listEl));
handleCollectionValue((Collection<?>) val, propEl, listEl);
} else if (val instanceof Set) {
ArrayType setEl = new ArrayType();
propEl.getContent().add(factory.createSet(setEl));
handleCollectionValue((Collection<?>) val, propEl, setEl);
} else if (val instanceof String
|| val instanceof Character
|| val instanceof Boolean
|| val instanceof Byte) {
setValueType(propEl, val);
propEl.setValue(val.toString());
} else if (val instanceof Long
|| val instanceof Double
|| val instanceof Float
|| val instanceof Integer
|| val instanceof Short) {
// various numbers.. maybe "val instanceof Number"?
setValueType(propEl, val);
propEl.setValue(val.toString());
} else {
// Don't add this property as the value type is not supported
continue;
}
props.add(propEl);
}
return props;
}
private static Object[] normalizeArray(Object val) {
List<Object> l = new ArrayList<>();
if (val instanceof int[]) {
int[] ia = (int[]) val;
for (int i : ia) {
l.add(i);
}
} else if (val instanceof long[]) {
long[] la = (long[]) val;
for (long i : la) {
l.add(i);
}
} else if (val instanceof float[]) {
float[] fa = (float[]) val;
for (float f : fa) {
l.add(f);
}
} else if (val instanceof byte[]) {
byte[] ba = (byte[]) val;
for (byte b : ba) {
l.add(b);
}
} else if (val instanceof boolean[]) {
boolean[] ba = (boolean[]) val;
for (boolean b : ba) {
l.add(b);
}
} else if (val instanceof short[]) {
short[] sa = (short[]) val;
for (short s : sa) {
l.add(s);
}
} else if (val instanceof char[]) {
char[] ca = (char[]) val;
for (char c : ca) {
l.add(c);
}
} else {
return (Object[]) val;
}
return l.toArray();
}
private static void handleCollectionValue(Collection<?> val, PropertyType propEl, ArrayType listEl) {
for (Object o : val) {
setValueType(propEl, o);
ValueType valueType = new ValueType();
valueType.getContent().add(o.toString());
listEl.getValue().add(valueType);
}
}
private static void setValueType(PropertyType propEl, Object val) {
if (val instanceof String) {
return;
}
String dataType = val.getClass().getName();
if (dataType.startsWith("java.lang.")) {
dataType = dataType.substring("java.lang.".length());
}
propEl.setValueType(dataType);
}
}