package cn.mutils.core.xml;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilderFactory;
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 javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import cn.mutils.core.beans.BeanField;
import cn.mutils.core.reflect.ReflectUtil;
/**
* XML serializer of framework
*/
@SuppressWarnings({"rawtypes", "unchecked", "CaughtExceptionImmediatelyRethrown", "unused"})
public class XmlUtil {
/**
* XML node attribute tag for entity property name
*/
public static final String TAG_NAME = "name";
/**
* XML node attribute tag for entity property of string
*/
public static final String TAG_STRING = "string";
/**
* XML node attribute tag for entity property of integer
*/
public static final String TAG_INT = "int";
/**
* XML node attribute tag for entity property of long
*/
public static final String TAG_LONG = "long";
/**
* XML node attribute tag for entity property of double
*/
public static final String TAG_DOUBLE = "double";
/**
* XML node attribute tag for entity property of boolean
*/
public static final String TAG_BOOL = "bool";
/**
* XML node attribute tag for entity property of enumeration
*/
public static final String TAG_ENUM = "enum";
/**
* XML node tag for entity property null
*/
public static final String TAG_NULL = "null";
public static Document parse(String file) throws Exception {
return parse(new File(file));
}
public static Document parse(File file) throws Exception {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
}
public static Document parse(InputStream is) throws Exception {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
}
public static void save(Document doc, OutputStream os) throws Exception {
TransformerFactory.newInstance().newTransformer().transform(new DOMSource(doc), new StreamResult(os));
}
public static void save(Document doc, String file) throws Exception {
save(doc, new File(file));
}
public static void save(Document doc, File file) throws Exception {
TransformerFactory.newInstance().newTransformer().transform(new DOMSource(doc), new StreamResult(file));
}
protected static Node getChildNode(Node node, String childAttrName) {
NodeList nodes = node.getChildNodes();
if (nodes == null) {
return null;
}
for (int i = nodes.getLength() - 1; i >= 0; i--) {
Node sub = nodes.item(i);
if (sub.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
String attr = getAttr(sub, TAG_NAME);
if (attr == null) {
continue;
}
if (attr.equals(childAttrName)) {
return sub;
}
}
return null;
}
public static String getAttr(Node node, String name) {
Node attr = node.getAttributes().getNamedItem(name);
return attr == null ? null : attr.getNodeValue();
}
public static void setAttr(Node node, String name, String value) {
NamedNodeMap attrs = node.getAttributes();
Node attr = attrs.getNamedItem(name);
if (attr == null) {
Document doc = node.getOwnerDocument();
attr = doc.createAttribute(name);
attrs.setNamedItem(attr);
}
attr.setNodeValue(value);
}
public static NodeList selectNodes(Node node, String xPath) throws Exception {
return (NodeList) XPathFactory.newInstance().newXPath().compile(xPath).evaluate(node, XPathConstants.NODESET);
}
public static Node newNode(Document doc, BeanField field) {
Node node = doc.createElement(field.getRawType().getSimpleName());
XmlUtil.setAttr(node, XmlUtil.TAG_NAME, field.getName());
return node;
}
public static Document newDoc() throws Exception {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
}
public static Document newDoc(String xml) throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(xml.getBytes("UTF-8"));
try {
return parse(bis);
} catch (Exception e) {
throw e;
} finally {
bis.close();
}
}
public static String toString(Node node) throws Exception {
return toString(node, true);
}
public static String toString(Node node, boolean indent) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
if (indent) {
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
}
t.transform(new DOMSource(node), new StreamResult(bos));
return bos.toString("UTF-8");
} catch (Exception e) {
throw e;
} finally {
bos.close();
}
}
public static <T> T convertFromNode(Node node, Class<T> targetClass) throws Exception {
return convertFromNode(node, targetClass, null);
}
protected static <T> T convertFromNode(Node node, Class<T> targetClass, Type genericType) throws Exception {
if (TAG_NULL.equals(node.getNodeName())) {
return null;
}
if (targetClass == String.class) {
return (T) node.getTextContent();
} else if (targetClass == Integer.TYPE || targetClass == Integer.class) {
return (T) Integer.valueOf(node.getTextContent());
} else if (targetClass == Long.TYPE || targetClass == Long.class) {
return (T) Long.valueOf(node.getTextContent());
} else if (targetClass == Double.TYPE || targetClass == Double.class) {
return (T) Double.valueOf(node.getTextContent());
} else if (targetClass == Boolean.TYPE || targetClass == Boolean.class) {
return (T) Boolean.valueOf(node.getTextContent());
} else if (Enum.class.isAssignableFrom(targetClass)) {
return (T) ReflectUtil.valueOfEnum(targetClass, node.getTextContent());
}
return convertFromNode(node, targetClass.newInstance(), genericType);
}
protected static <T> T convertFromNode(Node node, T target, Type genericType) throws Exception {
if (TAG_NULL.equals(node.getNodeName())) {
return null;
}
if (target instanceof String) {
return (T) node.getTextContent();
} else if (target instanceof Integer) {
return (T) Integer.valueOf(node.getTextContent());
} else if (target instanceof Long) {
return (T) Long.valueOf(node.getTextContent());
} else if (target instanceof Double) {
return (T) Double.valueOf(node.getTextContent());
} else if (target instanceof Boolean) {
return (T) Boolean.valueOf(node.getTextContent());
} else if (target instanceof Enum) {
return (T) ReflectUtil.valueOfEnum(target.getClass(), node.getTextContent());
} else if (target instanceof Collection) {
return (T) convertCollectionFromNode(node, (Collection<?>) target, genericType);
} else if (target instanceof Map) {
return (T) convertMapFromNode(node, (Map<?, ?>) target, genericType);
} else if (target instanceof IXmlItem) {
return (T) ((IXmlItem) target).fromXml(node, null);
}
for (BeanField f : BeanField.getFields(target.getClass())) {
Node sub = getChildNode(node, f.getName());
if (sub == null) {
continue;
}
Class<?> fClass = f.getRawType(genericType);
f.set(target, IXmlItem.class.isAssignableFrom(fClass) ? (((IXmlItem) fClass.newInstance()).fromXml(sub, f))
: convertFromNode(sub, fClass, f.getGenericType(genericType)));
}
return target;
}
protected static <T extends Collection> T convertCollectionFromNode(Node node, T target, Type genericType)
throws Exception {
Class<?> targetClass = target.getClass();
Class<?> eClass = ReflectUtil.getCollectionElementRawType(targetClass, genericType);
Type eType = ReflectUtil.getCollectionElementGenericType(targetClass, genericType);
NodeList childNodes = node.getChildNodes();
for (int i = 0, size = childNodes.getLength(); i < size; i++) {
Node sub = childNodes.item(i);
if (sub.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
target.add(convertFromNode(sub, eClass, eType));
}
return target;
}
protected static <T extends Map> T convertMapFromNode(Node node, T target, Type genericType) throws Exception {
Class<?> targetClass = target.getClass();
Class<?> keyClass = ReflectUtil.getMapKeyRawType(targetClass, genericType);
if (!String.class.isAssignableFrom(keyClass)) {
throw new Exception();
}
Class<?> vClass = ReflectUtil.getMapValueRawType(targetClass, genericType);
Type vType = ReflectUtil.getMapValueGenericType(targetClass, genericType);
NodeList childNodes = node.getChildNodes();
for (int i = childNodes.getLength() - 1; i >= 0; i--) {
Node sub = childNodes.item(i);
if (sub.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
target.put(getAttr(sub, TAG_NAME), convertFromNode(sub, vClass, vType));
}
return target;
}
public static <T> T convertFromDoc(Document doc, Class<T> targetClass) throws Exception {
return convertFromNode(doc.getDocumentElement(), targetClass, null);
}
public static <T> T convert(String str, Class<T> targetClass) throws Exception {
if (str == null) {
return null;
}
return convertFromNode(newDoc(str).getDocumentElement(), targetClass, null);
}
public static <T> T convert(String str, T target) throws Exception {
return convertFromNode(newDoc(str).getDocumentElement(), target, null);
}
public static <T> String convert(T target) throws Exception {
return toString(convertToDoc(target), false);
}
public static <T> Document convertToDoc(T target) throws Exception {
Document doc = newDoc();
doc.appendChild(convertToNode(target, doc));
return doc;
}
protected static <T> Node convertToNode(T target, Document doc) throws Exception {
if (target == null) {
return doc.createElement(TAG_NULL);
} else if (target instanceof String) {
Node node = doc.createElement(TAG_STRING);
node.setTextContent((String) target);
return node;
} else if (target instanceof Integer) {
Node node = doc.createElement(TAG_INT);
node.setTextContent(target.toString());
return node;
} else if (target instanceof Long) {
Node node = doc.createElement(TAG_LONG);
node.setTextContent(target.toString());
return node;
} else if (target instanceof Double) {
Node node = doc.createElement(TAG_DOUBLE);
node.setTextContent(target.toString());
return node;
} else if (target instanceof Boolean) {
Node node = doc.createElement(TAG_BOOL);
node.setTextContent(target.toString());
return node;
} else if (target instanceof Enum) {
Node node = doc.createElement(TAG_ENUM);
node.setTextContent(target.toString());
return node;
} else if (target instanceof Collection) {
return convertCollectionToNode(target, doc);
} else if (target instanceof Map) {
return convertMapToNode(target, doc);
} else if (target instanceof IXmlItem) {
return ((IXmlItem) target).toXml(doc, null);
}
Class<?> targetClass = target.getClass();
Node node = doc.createElement(targetClass.getSimpleName());
for (BeanField f : BeanField.getFields(targetClass)) {
Object v = f.get(target);
if (v == null) {
continue;
}
Node subNode = (v instanceof IXmlItem) ? (((IXmlItem) v).toXml(doc, f)) : convertToNode(v, doc);
setAttr(subNode, TAG_NAME, f.getName());
node.appendChild(subNode);
}
return node;
}
protected static <T> Node convertCollectionToNode(T target, Document doc) throws Exception {
Node node = doc.createElement(target.getClass().getSimpleName());
for (Object element : (Collection<?>) target) {
node.appendChild(convertToNode(element, doc));
}
return node;
}
protected static <T> Node convertMapToNode(T target, Document doc) throws Exception {
Node node = doc.createElement(target.getClass().getSimpleName());
for (Entry<?, ?> entry : ((Map<?, ?>) target).entrySet()) {
Object k = entry.getKey();
if (!(k instanceof String)) {
break;
}
Object v = entry.getValue();
if (v == null) {
continue;
}
Node subNode = convertToNode(v, doc);
setAttr(subNode, TAG_NAME, (String) k);
node.appendChild(subNode);
}
return node;
}
}