/*******************************************************************************
* Copyright (c) 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Jan S. Rellermeyer, IBM Research - initial API and implementation
*******************************************************************************/
package org.osgi.impl.service.rest.client;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osgi.dto.DTO;
import org.restlet.data.MediaType;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.ext.xml.DomRepresentation;
import org.restlet.representation.Representation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* A reflector for turning DTOs into JSON representations and vice versa.
*
* @author Jan S. Rellermeyer, IBM Research
*/
public final class DTOReflector {
private static final MediaType XML_BASED = MediaType.valueOf("application/*+xml");
private static final MediaType JSON_BASED = MediaType.valueOf("application/*+json");
public static <T extends DTO> T getDTO(final Class<T> clazz,
final Representation repr) throws Exception {
if (JSON_BASED.includes(repr.getMediaType())) {
final JSONObject data = new JsonRepresentation(repr).getJsonObject();
return getDTOfromJson(clazz, data, repr.getLocationRef().getPath());
} else if (XML_BASED.includes(repr.getMediaType())) {
final Document doc = new DomRepresentation(repr).getDocument();
return getDTOfromXml(clazz, (Element) doc.getFirstChild(), repr.getLocationRef().getPath());
} else {
throw new UnsupportedOperationException(repr.getMediaType().toString());
}
}
public static <T extends DTO> Collection<T> getDTOs(Class<T> clazz, Representation repr) throws Exception {
if (JSON_BASED.includes(repr.getMediaType())) {
return getDTOsFromJson(clazz, new JsonRepresentation(repr).getJsonArray());
} else if (XML_BASED.includes(repr.getMediaType())) {
System.out.println("content: " + repr.getText());
return getDTOsFromXml(clazz, new DomRepresentation(repr).getDocument());
} else {
throw new UnsupportedOperationException(repr.getMediaType().toString());
}
}
public static Map<String, String> getMap(final Representation repr) throws Exception {
if (JSON_BASED.includes(repr.getMediaType())) {
final JSONObject data = new JsonRepresentation(repr).getJsonObject();
return getMapfromJsonObject(data);
} else {
throw new UnsupportedOperationException(repr.getMediaType().toString());
}
}
public static Collection<String> getStrings(final Representation repr) throws Exception {
final Collection<String> result = new ArrayList<String>();
if (JSON_BASED.includes(repr.getMediaType())) {
final JSONArray array = new JsonRepresentation(repr).getJsonArray();
for (int i = 0; i < array.length(); i++) {
result.add(array.getString(i));
}
} else if (XML_BASED.includes(repr.getMediaType())) {
final Document doc = new DomRepresentation(repr).getDocument();
final Node rootNode = doc.getFirstChild();
final NodeList nodes = rootNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
result.add(nodes.item(i).getTextContent());
}
} else {
throw new UnsupportedOperationException(repr.getMediaType().toString());
}
return result;
}
private static <T extends DTO> T getDTOfromJson(final Class<T> clazz, final JSONObject data, final String path) throws Exception {
final Field[] fields = clazz.getFields();
final T dto = clazz.newInstance();
for (final Field field : fields) {
if ("bundle".equals(field.getName())) {
if (data.has("bundle")) {
field.set(dto, new Long(getBundleIdFromPath(data.getString("bundle"))));
} else {
field.set(dto, new Long(getBundleIdFromPath(path)));
}
} else if ("usingBundles".equals(field.getName())) {
field.set(
dto,
getBundleIdsFromPaths(data.getJSONArray("usingBundles")));
} else if (field.getType().equals(Map.class)) {
field.set(dto, getMapfromJsonObject(data.getJSONObject(field.getName())));
} else {
field.set(dto, data.get(field.getName()));
}
}
return dto;
}
private static <T extends DTO> T getDTOfromXml(final Class<T> clazz, final Element elem, final String path) throws Exception {
final Field[] fields = clazz.getFields();
final T dto = clazz.newInstance();
for (final Field field : fields) {
if ("bundle".equals(field.getName())) {
if (elem.getElementsByTagName("bundle") != null) {
field.set(dto, new Long(getBundleIdFromPath(elem.getElementsByTagName("bundle").item(0).getTextContent())));
} else {
field.set(dto, new Long(getBundleIdFromPath(path)));
}
} else if ("usingBundles".equals(field.getName())) {
final Node node = elem.getElementsByTagName("usingBundles").item(0);
final NodeList nodes = node.getChildNodes();
if (nodes.getLength() > 0) {
final long[] using = new long[nodes.getLength()];
for (int i = 0; i < nodes.getLength(); i++) {
using[i] = getBundleIdFromPath(nodes.item(i).getTextContent());
field.set(dto, using);
}
}
} else if ("id".equals(field.getName()) || "lastModified".equals(field.getName())) {
field.set(dto, Long.valueOf(elem.getElementsByTagName(field.getName()).item(0).getTextContent()));
} else if ("state".equals(field.getName())) {
field.set(dto, Integer.valueOf(elem.getElementsByTagName(field.getName()).item(0).getTextContent()));
} else if (field.getType().equals(Map.class)) {
final Node props = elem.getElementsByTagName("properties").item(0);
final Map<String, Object> properties = new HashMap<String, Object>();
final NodeList prop = props.getChildNodes();
for (int i = 0; i < prop.getLength(); i++) {
propertyFromXml(properties, prop.item(i));
}
field.set(dto, properties);
} else {
// default is string
field.set(dto, elem.getElementsByTagName(field.getName()).item(0).getTextContent());
}
}
return dto;
}
private static void propertyFromXml(final Map<String, Object> map, final Node node) {
final String name = node.getAttributes().getNamedItem("name").getTextContent();
final Node tNode = node.getAttributes().getNamedItem("type");
final String type = tNode == null ? null : tNode.getTextContent();
final Node vNode = node.getAttributes().getNamedItem("value");
final Object value;
if (vNode != null) {
value = fromXml(type, vNode.getTextContent());
} else {
final String[] vals = node.getTextContent().split("\n");
final int len = vals.length;
value = getArray(type, len);
for (int i = 0; i < len; i++) {
Array.set(value, i, fromXml(type, vals[i]));
}
}
map.put(name, value);
}
private static Object fromXml(final String type, final String value) {
if (type == null || "String".equals(type)) {
return value;
} else if ("Long".equals(type)) {
return Long.valueOf(value);
} else if ("Double".equals(type)) {
return Double.valueOf(value);
} else if ("Float".equals(type)) {
return Float.valueOf(value);
} else if ("Integer".equals(type)) {
return Integer.valueOf(value);
} else if ("Byte".equals(type)) {
return Byte.valueOf(value);
} else if ("Character".equals(type)) {
return new Character(value.trim().charAt(0));
} else if ("Boolean".equals(type)) {
return Boolean.valueOf(value);
} else if ("Short".equals(type)) {
return Short.valueOf(value);
} else {
return value;
}
}
private static Object getArray(final String type, final int len) {
if (type == null || "String".equals(type)) {
return new String[len];
} else if ("Long".equals(type)) {
return new long[len];
} else if ("Double".equals(type)) {
return new double[len];
} else if ("Float".equals(type)) {
return new float[len];
} else if ("Integer".equals(type)) {
return new int[len];
} else if ("Byte".equals(type)) {
return new byte[len];
} else if ("Character".equals(type)) {
return new char[len];
} else if ("Boolean".equals(type)) {
return new boolean[len];
} else if ("Short".equals(type)) {
return new short[len];
} else {
return new Object[len];
}
}
private static final Pattern p = Pattern.compile("\\/(\\d+)\\/*");
private static long getBundleIdFromPath(final String path) {
final Matcher m = p.matcher(path);
if (m.find()) {
final String s = m.group(1);
return Long.parseLong(s);
} else {
throw new IllegalArgumentException(path);
}
}
private static long[] getBundleIdsFromPaths(JSONArray array)
throws JSONException {
if (array.length() == 0) {
return null;
}
final long[] result = new long[array.length()];
for (int i = 0; i < array.length(); i++) {
result[i] = getBundleIdFromPath(array.getString(i));
}
return result;
}
@SuppressWarnings("unchecked")
private static <K, V> Map<K, V> getMapfromJsonObject(final JSONObject obj)
throws JSONException {
final Map<K, V> result = new HashMap<K, V>();
final String[] keys = JSONObject.getNames(obj);
for (int i = 0; i < keys.length; i++) {
final Object o = keys[i].equals("service.id") || keys[i].equals("service.bundleid") ? new Long(obj.getLong(keys[i])) : obj.get(keys[i]);
if (o instanceof JSONArray) {
result.put((K) keys[i], (V) getStringArrayFromJSONArray((JSONArray) o));
} else {
result.put((K) keys[i], (V) o);
}
}
return result;
}
private static String[] getStringArrayFromJSONArray(final JSONArray a) throws JSONException {
final String[] result = new String[a.length()];
for (int i = 0; i < result.length; i++) {
result[i] = a.getString(i);
}
return result;
}
private static <T extends DTO> Collection<T> getDTOsFromJson(final Class<T> clazz, final JSONArray array) throws Exception {
final Collection<T> result = new ArrayList<T>();
for (int i = 0; i < array.length(); i++) {
result.add(DTOReflector.getDTOfromJson(clazz, array.getJSONObject(i), null));
}
return result;
}
private static <T extends DTO> Collection<T> getDTOsFromXml(final Class<T> clazz, final Node rootNode) throws Exception {
final Collection<T> result = new ArrayList<T>();
final NodeList nodes = rootNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
result.add(DTOReflector.getDTOfromXml(clazz, (Element) nodes.item(i), null));
}
return result;
}
public static <T extends DTO> JSONObject getJson(final Class<T> clazz,
final T dto) throws Exception {
final Field[] fields = clazz.getFields();
final JSONObject obj = new JSONObject();
for (final Field field : fields) {
if (field.getName().equals("bundle")) {
obj.put("bundle", getBundlePathFromId((Long) field.get(dto)));
} else {
obj.put(field.getName(), field.get(dto));
}
}
return obj;
}
private static String getBundlePathFromId(final Long id) {
return "framework/bundle/" + id.toString();
}
}