package nckd.jimin.jyyy.fi.common;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.*;

import java.io.Writer;
import java.util.List;

/**
 * xml 转Json 工具类
 *
 */
public class XmlUtils {
    /**
     * String 转 org.dom4j.Document
     *
     * @param xml
     * @return
     * @throws DocumentException
     */
    private static Document strToDocument(String xml) throws DocumentException {
        return DocumentHelper.parseText(xml);
    }

    /**
     * xml 转  com.alibaba.fastjson.JSONObject
     *
     * @param xml
     * @return
     * @throws DocumentException
     */
    public static JSONObject documentToJSONObject(String xml) {
        JSONObject jsonObject = null;
        try {
            jsonObject = elementToJSONObject(strToDocument(xml).getRootElement());
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            return jsonObject;
        }
    }

    /**
     * org.dom4j.Element 转  com.alibaba.fastjson.JSONObject
     *
     * @param node
     * @return
     */
    public static JSONObject elementToJSONObject(Element node) {
        JSONObject result = new JSONObject();
        // 当前节点的名称、文本内容和属性
        List<Attribute> listAttr = node.attributes();// 当前节点的所有属性的list
        for (Attribute attr : listAttr) {// 遍历当前节点的所有属性
            result.put(attr.getName(), attr.getValue());
        }
        // 递归遍历当前节点所有的子节点
        List<Element> listElement = node.elements();// 所有一级子节点的list
        if (!listElement.isEmpty()) {
            for (Element e : listElement) {// 遍历所有一级子节点
                if (e.attributes().isEmpty() && e.elements().isEmpty()) {// 判断一级节点是否有属性和子节点
                    result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待
                } else {
                    if (!result.containsKey(e.getName())) {// 判断父节点是否存在该一级节点名称的属性
                        result.put(e.getName(), new JSONArray());// 没有则创建
                    }
                    ((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中
                }
            }
        }
        return result;
    }

    /**
     * 创建XStream
     */
    private static XStream createXstream() {
        XStream xstream = new XStream(new MyXppDriver(false));
        xstream.autodetectAnnotations(true);
        return xstream;
    }

    /**
     * 支持注解转化XML
     */
    public static String toXML(Object obj, Class<?> cls) {
        if (obj == null) {
            return null;
        }
        XStream xstream = createXstream();
        xstream.processAnnotations(cls);
        return getDefaultXMLHeader() + xstream.toXML(obj);
    }

    /**
     * Object 转化 XML
     */
    public static String toXML(Object obj) {
        if (obj == null) {
            return null;
        }
        XStream xstream = createXstream();
        return getDefaultXMLHeader() + xstream.toXML(obj);
    }

    /**
     * XML转化为JAVA对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2Obj(String xml, Class<?> cls) {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        XStream xstream = createXstream();
        if (cls != null) {
            xstream.processAnnotations(cls);
        }
        return (T) xstream.fromXML(xml);
    }

    /**
     * XML转化为JAVA对象
     */
    public static <T> T xml2Obj(String xml) {
        return xml2Obj(xml, null);
    }

    private static String getDefaultXMLHeader() {
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
    }

    /**
     * @author lixining
     * @version $Id: XmlUtils.java, v 0.1 2015年8月18日 上午9:46:57 lixining Exp $
     * @description XppDriver
     */
    public static class MyXppDriver extends XppDriver {
        boolean useCDATA = false;

        MyXppDriver(boolean useCDATA) {
            super(new XmlFriendlyNameCoder("__", "_"));
            this.useCDATA = useCDATA;
        }

        @Override
        public HierarchicalStreamWriter createWriter(Writer out) {
            if (!useCDATA) {
                return super.createWriter(out);
            }
            return new PrettyPrintWriter(out) {
                boolean cdata = true;

                @Override
                public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
                    super.startNode(name, clazz);
                }

                @Override
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write(cDATA(text));
                    } else {
                        writer.write(text);
                    }
                }

                private String cDATA(String text) {
                    return "<![CDATA[" + text + "]]>";
                }
            };
        }
    }
}
