mirror of https://github.com/apache/cloudstack.git
490 lines
19 KiB
Java
490 lines
19 KiB
Java
// 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 com.cloud.bridge.util;
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Modifier;
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Calendar;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
import org.apache.log4j.Logger;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
|
|
public class XSerializer {
|
|
protected final static Logger logger = Logger.getLogger(XSerializer.class);
|
|
|
|
private static Map<String, Class<?>> rootTypes = new HashMap<String, Class<?>>();
|
|
|
|
private XSerializerAdapter adapter;
|
|
private boolean flattenCollection;
|
|
private boolean omitNull;
|
|
|
|
public XSerializer(XSerializerAdapter adapter) {
|
|
this.adapter = adapter;
|
|
adapter.setSerializer(this);
|
|
|
|
// set default serialization options
|
|
flattenCollection = false;
|
|
omitNull = false;
|
|
}
|
|
|
|
public XSerializer(XSerializerAdapter adapter, boolean flattenCollection, boolean omitNull) {
|
|
this.adapter = adapter;
|
|
adapter.setSerializer(this);
|
|
|
|
this.flattenCollection = flattenCollection;
|
|
this.omitNull = omitNull;
|
|
}
|
|
|
|
public boolean getFlattenCollection() {
|
|
return flattenCollection;
|
|
}
|
|
|
|
public void setFlattenCollection(boolean value) {
|
|
flattenCollection = value;
|
|
}
|
|
|
|
public boolean flattenField(Field f) {
|
|
XFlatten flatten = f.getAnnotation(XFlatten.class);
|
|
if (flatten != null)
|
|
return flatten.value();
|
|
return flattenCollection;
|
|
}
|
|
|
|
public boolean omitNullField(Field f) {
|
|
XOmitNull omit = f.getAnnotation(XOmitNull.class);
|
|
if (omit != null)
|
|
return omit.value();
|
|
|
|
return omitNull;
|
|
}
|
|
|
|
public boolean getOmitNull() {
|
|
return omitNull;
|
|
}
|
|
|
|
public void setOmitNull(boolean value) {
|
|
omitNull = value;
|
|
}
|
|
|
|
public static void registerRootType(String elementName, Class<?> clz) {
|
|
rootTypes.put(elementName, clz);
|
|
}
|
|
|
|
public XSerializerAdapter getAdapter() {
|
|
return adapter;
|
|
}
|
|
|
|
public static Object mapElement(String elementName) {
|
|
Class<?> clz = rootTypes.get(elementName);
|
|
if (clz == null) {
|
|
logger.error("Object class is not registered for root element " + elementName);
|
|
throw new IllegalArgumentException("Object class is not registered for root element " + elementName);
|
|
}
|
|
|
|
try {
|
|
return clz.newInstance();
|
|
} catch (InstantiationException e) {
|
|
logger.error("Unable to instantiate object for root element due to InstantiationException, XML element: " + elementName);
|
|
throw new IllegalArgumentException("Unable to instantiate object for root element " + elementName);
|
|
} catch (IllegalAccessException e) {
|
|
logger.error("Unable to instantiate object for root element due to IllegalAccessException, XML element: " + elementName);
|
|
throw new IllegalArgumentException("Unable to instantiate object for root element due to IllegalAccessException, XML element: " + elementName);
|
|
}
|
|
}
|
|
|
|
public Object serializeFrom(String xmlString) {
|
|
try {
|
|
Document doc = XmlHelper.parse(xmlString);
|
|
Node node = XmlHelper.getRootNode(doc);
|
|
if (node == null) {
|
|
logger.error("Invalid XML document, no root element");
|
|
return null;
|
|
}
|
|
|
|
Object object = mapElement(node.getNodeName());
|
|
if (object == null) {
|
|
logger.error("Unable to map root element. Please remember to use XSerializer.registerRootType() to register the root object type");
|
|
return null;
|
|
}
|
|
|
|
if (object instanceof XSerializable)
|
|
((XSerializable)object).serializeFrom(this, object, node);
|
|
else
|
|
serializeFrom(object, object.getClass(), node);
|
|
|
|
return object;
|
|
} catch (IOException e) {
|
|
logger.error("Unable to parse XML input due to " + e.getMessage(), e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void serializeFrom(Object object, Class<?> clz, Node node) {
|
|
if (clz.getSuperclass() != null)
|
|
serializeFrom(object, clz.getSuperclass(), node);
|
|
|
|
Field[] fields = clz.getDeclaredFields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
Field f = fields[i];
|
|
|
|
if ((f.getModifiers() & Modifier.STATIC) == 0) {
|
|
f.setAccessible(true);
|
|
|
|
Class<?> fieldType = f.getType();
|
|
XElement elem = f.getAnnotation(XElement.class);
|
|
if (elem == null)
|
|
continue;
|
|
|
|
try {
|
|
if (fieldType.isPrimitive()) {
|
|
setPrimitiveField(f, object, XmlHelper.getChildNodeTextContent(node, elem.name()));
|
|
} else if (fieldType.getSuperclass() == Number.class) {
|
|
setNumberField(f, object, XmlHelper.getChildNodeTextContent(node, elem.name()));
|
|
} else if (fieldType == String.class) {
|
|
f.set(object, XmlHelper.getChildNodeTextContent(node, elem.name()));
|
|
} else if (fieldType == Date.class) {
|
|
setDateField(f, object, XmlHelper.getChildNodeTextContent(node, elem.name()));
|
|
} else if (fieldType == Calendar.class) {
|
|
setCalendarField(f, object, XmlHelper.getChildNodeTextContent(node, elem.name()));
|
|
} else if (fieldType.isArray()) {
|
|
if (flattenField(f))
|
|
setArrayField(f, object, node, elem.item(), elem.itemClass());
|
|
else
|
|
setArrayField(f, object, XmlHelper.getChildNode(node, elem.name()), elem.item(), elem.itemClass());
|
|
} else if (Collection.class.isAssignableFrom(fieldType)) {
|
|
if (flattenField(f))
|
|
setCollectionField(f, object, node, elem.item(), elem.itemClass());
|
|
else
|
|
setCollectionField(f, object, XmlHelper.getChildNode(node, elem.name()), elem.item(), elem.itemClass());
|
|
} else {
|
|
Node childNode = XmlHelper.getChildNode(node, elem.name());
|
|
Object fieldObject = f.get(object);
|
|
if (fieldObject == null) {
|
|
try {
|
|
fieldObject = fieldType.newInstance();
|
|
} catch (InstantiationException e) {
|
|
logger.error("Unable to instantiate " + fieldType.getName() + " object, please make sure it has public constructor");
|
|
assert (false);
|
|
}
|
|
f.set(object, fieldObject);
|
|
}
|
|
serializeFrom(fieldObject, fieldType, childNode);
|
|
}
|
|
} catch (IllegalArgumentException e) {
|
|
logger.error("Unexpected exception " + e.getMessage(), e);
|
|
} catch (IllegalAccessException e) {
|
|
logger.error("Unexpected exception " + e.getMessage(), e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setPrimitiveField(Field f, Object object, String valueContent) throws IllegalArgumentException, IllegalAccessException {
|
|
|
|
String clzName = f.getType().getName();
|
|
if (clzName.equals("boolean")) {
|
|
if (valueContent != null && valueContent.equalsIgnoreCase("true"))
|
|
f.setBoolean(object, true);
|
|
else
|
|
f.setBoolean(object, false);
|
|
} else if (clzName.equals("byte")) {
|
|
byte value = 0;
|
|
if (valueContent != null)
|
|
value = Byte.parseByte(valueContent);
|
|
f.setByte(object, value);
|
|
} else if (clzName.equals("char")) {
|
|
char value = '\0';
|
|
if (valueContent != null) {
|
|
if (valueContent.charAt(0) == '\'')
|
|
value = valueContent.charAt(1);
|
|
else
|
|
value = valueContent.charAt(0);
|
|
}
|
|
f.setChar(object, value);
|
|
} else if (clzName.equals("short")) {
|
|
short value = 0;
|
|
if (valueContent != null)
|
|
value = Short.parseShort(valueContent);
|
|
f.setShort(object, value);
|
|
} else if (clzName.equals("int")) {
|
|
int value = 0;
|
|
if (valueContent != null)
|
|
value = Integer.parseInt(valueContent);
|
|
f.setInt(object, value);
|
|
} else if (clzName.equals("long")) {
|
|
long value = 0;
|
|
if (valueContent != null)
|
|
value = Long.parseLong(valueContent);
|
|
f.setLong(object, value);
|
|
} else if (clzName.equals("float")) {
|
|
float value = 0;
|
|
if (valueContent != null)
|
|
value = Float.parseFloat(valueContent);
|
|
f.setFloat(object, value);
|
|
} else if (clzName.equals("double")) {
|
|
double value = 0;
|
|
if (valueContent != null)
|
|
value = Double.parseDouble(valueContent);
|
|
f.setDouble(object, value);
|
|
} else {
|
|
logger.error("Assertion failed at setPrimitiveFiled");
|
|
assert (false);
|
|
}
|
|
}
|
|
|
|
private void setNumberField(Field f, Object object, String valueContent) throws IllegalArgumentException, IllegalAccessException {
|
|
|
|
String clzName = f.getType().getName();
|
|
if (clzName.equals("Byte")) {
|
|
byte value = 0;
|
|
if (valueContent != null)
|
|
value = Byte.parseByte(valueContent);
|
|
f.set(object, new Byte(value));
|
|
} else if (clzName.equals("Short")) {
|
|
short value = 0;
|
|
if (valueContent != null)
|
|
value = Short.parseShort(valueContent);
|
|
f.set(object, new Short(value));
|
|
} else if (clzName.equals("Integer")) {
|
|
int value = 0;
|
|
if (valueContent != null)
|
|
value = Integer.parseInt(valueContent);
|
|
f.set(object, new Integer(value));
|
|
} else if (clzName.equals("Long")) {
|
|
long value = 0;
|
|
if (valueContent != null)
|
|
value = Long.parseLong(valueContent);
|
|
f.set(object, new Long(value));
|
|
} else if (clzName.equals("Float")) {
|
|
float value = 0;
|
|
if (valueContent != null)
|
|
value = Float.parseFloat(valueContent);
|
|
f.set(object, new Float(value));
|
|
} else if (clzName.equals("Double")) {
|
|
double value = 0;
|
|
if (valueContent != null)
|
|
value = Double.parseDouble(valueContent);
|
|
f.setDouble(object, new Double(value));
|
|
} else if (clzName.equals("AtomicInteger")) {
|
|
int value = 0;
|
|
if (valueContent != null)
|
|
value = Integer.parseInt(valueContent);
|
|
f.set(object, new AtomicInteger(value));
|
|
} else if (clzName.equals("AtomicLong")) {
|
|
long value = 0;
|
|
if (valueContent != null)
|
|
value = Long.parseLong(valueContent);
|
|
f.set(object, new AtomicLong(value));
|
|
} else if (clzName.equals("BigInteger")) {
|
|
logger.error("we don't support BigInteger for now");
|
|
assert (false);
|
|
} else if (clzName.equals("BigDecimal")) {
|
|
logger.error("we don't support BigInteger for now");
|
|
assert (false);
|
|
} else {
|
|
logger.error("Assertion failed at setPrimitiveFiled");
|
|
assert (false);
|
|
}
|
|
}
|
|
|
|
private void setDateField(Field f, Object object, String valueContent) throws IllegalArgumentException, IllegalAccessException {
|
|
|
|
if (valueContent != null) {
|
|
valueContent = valueContent.replace('T', ' ');
|
|
valueContent = valueContent.replace('.', '\0');
|
|
|
|
SimpleDateFormat df = DateHelper.getGMTDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
try {
|
|
Date value = df.parse(valueContent);
|
|
f.set(object, value);
|
|
} catch (ParseException e) {
|
|
logger.error("Unrecognized date/time format " + valueContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setCalendarField(Field f, Object object, String valueContent) throws IllegalArgumentException, IllegalAccessException {
|
|
|
|
if (valueContent != null) {
|
|
valueContent = valueContent.replace('T', ' ');
|
|
valueContent = valueContent.replace('.', '\0');
|
|
|
|
SimpleDateFormat df = DateHelper.getGMTDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
try {
|
|
Date value = df.parse(valueContent);
|
|
f.set(object, DateHelper.toCalendar(value));
|
|
} catch (ParseException e) {
|
|
logger.error("Unrecognized date/time format " + valueContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setArrayField(Field f, Object object, Node node, String itemElementName, String itemClass) throws IllegalArgumentException, IllegalAccessException {
|
|
|
|
List<Object> arrayList = new ArrayList<Object>();
|
|
|
|
Class<?> itemClz = null;
|
|
try {
|
|
itemClz = this.getClass().forName(itemClass);
|
|
} catch (ClassNotFoundException e) {
|
|
logger.error("Unable to find class " + itemClass);
|
|
return;
|
|
}
|
|
|
|
if (node != null) {
|
|
NodeList l = node.getChildNodes();
|
|
if (l != null && l.getLength() > 0) {
|
|
for (int i = 0; i < l.getLength(); i++) {
|
|
try {
|
|
Node itemNode = l.item(i);
|
|
if (itemNode.getNodeName().equals(itemElementName)) {
|
|
Object item = itemClz.newInstance();
|
|
serializeFrom(item, itemClz, l.item(i));
|
|
arrayList.add(item);
|
|
}
|
|
} catch (InstantiationException e) {
|
|
logger.error("Unable to initiate object instance for class " + itemClass + ", make sure it has public constructor");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Object arrary = Array.newInstance(f.getType().getComponentType(), arrayList.size());
|
|
arrayList.toArray((Object[])arrary);
|
|
f.set(object, arrary);
|
|
}
|
|
|
|
private void setCollectionField(Field f, Object object, Node node, String itemElementName, String itemClass) throws IllegalArgumentException, IllegalAccessException {
|
|
Object fieldObject = f.get(object);
|
|
|
|
if (fieldObject == null) {
|
|
logger.error("Please initialize collection field " + f.getName() + " in class " + object.getClass().getName() + "'s constructor");
|
|
return;
|
|
}
|
|
|
|
Class<?> itemClz = null;
|
|
try {
|
|
itemClz = this.getClass().forName(itemClass);
|
|
} catch (ClassNotFoundException e) {
|
|
logger.error("Unable to find class " + itemClass);
|
|
return;
|
|
}
|
|
|
|
NodeList l = node.getChildNodes();
|
|
if (l != null && l.getLength() > 0) {
|
|
for (int i = 0; i < l.getLength(); i++) {
|
|
try {
|
|
Node itemNode = l.item(i);
|
|
if (itemNode.getNodeName().equals(itemElementName)) {
|
|
Object item = itemClz.newInstance();
|
|
serializeFrom(item, itemClz, l.item(i));
|
|
((Collection)fieldObject).add(item);
|
|
}
|
|
} catch (InstantiationException e) {
|
|
logger.error("Unable to initiate object instance for class " + itemClass + ", make sure it has public constructor");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void serializeTo(Object obj, String startElement, String namespace, int indentLevel, PrintWriter writer) {
|
|
if (startElement != null) {
|
|
adapter.beginElement(startElement, namespace, indentLevel, writer);
|
|
indentLevel++;
|
|
}
|
|
|
|
if (obj instanceof XSerializable) {
|
|
((XSerializable)obj).serializeTo(this, indentLevel, writer);
|
|
} else {
|
|
Class<?> clz = obj.getClass();
|
|
serializeTo(obj, clz, indentLevel, writer);
|
|
}
|
|
|
|
if (startElement != null) {
|
|
indentLevel--;
|
|
adapter.endElement(startElement, indentLevel, writer);
|
|
}
|
|
}
|
|
|
|
public String serializeTo(Object obj, String startElement, String namespace, int indentLevel) {
|
|
StringWriter writer = new StringWriter();
|
|
serializeTo(obj, startElement, namespace, indentLevel, new PrintWriter(writer));
|
|
return writer.toString();
|
|
}
|
|
|
|
private void serializeTo(Object obj, Class<?> clz, int indentLevel, PrintWriter writer) {
|
|
if (clz.getSuperclass() != null)
|
|
serializeTo(obj, clz.getSuperclass(), indentLevel, writer);
|
|
|
|
Field[] fields = clz.getDeclaredFields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
Field f = fields[i];
|
|
|
|
if ((f.getModifiers() & Modifier.STATIC) == 0) {
|
|
f.setAccessible(true);
|
|
|
|
Class<?> fieldType = f.getType();
|
|
XElement elem = f.getAnnotation(XElement.class);
|
|
if (elem == null)
|
|
continue;
|
|
|
|
Object fieldValue = null;
|
|
try {
|
|
fieldValue = f.get(obj);
|
|
} catch (IllegalArgumentException e) {
|
|
logger.error("Unexpected exception " + e.getMessage(), e);
|
|
} catch (IllegalAccessException e) {
|
|
logger.error("Unexpected exception " + e.getMessage(), e);
|
|
}
|
|
|
|
adapter.writeElement(elem.name(), elem.item(), fieldValue, f, indentLevel, writer);
|
|
if (i < fields.length - 1) {
|
|
Field next = fields[i + 1];
|
|
if ((next.getModifiers() & Modifier.STATIC) == 0 && next.getAnnotation(XElement.class) != null) {
|
|
adapter.writeSeparator(indentLevel, writer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isComposite(Class<?> clz) {
|
|
if (clz.isPrimitive() || clz.getSuperclass() == Number.class || clz == String.class || clz == Date.class || clz == Calendar.class) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|