package com.winsun.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;


@Slf4j
public class XmlParser {

	private final String SPCODE_XPATH = "/params/spcode";
	private final String PARAMS_XPATH = "/params/*";
	Document doc;

	public XmlParser() {

	}

	public XmlParser(String xmlString) throws DocumentException {
		loadXml(xmlString);
	}

	
	/**
	 * 设置bean属性值
	 *
	 * @param obj
	 * @param nodes
	 * @throws Exception
	 */
	public void setValues(Object obj, List<Node> nodes) throws Exception {
		try {
			for (Node node : nodes) {
				Method[] methods = obj.getClass().getMethods();
				for (Method method : methods) {
					Class<?>[] paramTypes = method.getParameterTypes();
					if (method.getName().toLowerCase(Locale.ENGLISH).equals(
							"set" + node.getName())) {
						if (StringUtils.isNotEmpty(node.getStringValue().trim())){
							if (paramTypes[0].getCanonicalName().endsWith("Long")) {
								method.invoke(obj, new Long(node.getStringValue()));
							} else if (paramTypes[0].getCanonicalName().endsWith(
							"Double")) {
								method.invoke(obj,
										new Double(node.getStringValue()));
							} else {
								method.invoke(obj, node.getStringValue());
							}
						}

					}
				}
			}
		} catch (RuntimeException e) {
			log.error("设置bean属性值失败。", e);
			throw e;
		}
	}

	/**
	 * 根据xml串和xpath获取spcode
	 * 
	 * @param xmlString
	 * @return
	 * @throws Exception
	 */
	public String getSpCode(String xmlString) throws Exception {
		return getSingleValue(xmlString, SPCODE_XPATH);
	}

	/**
	 * 获取单个属性的值
	 * 
	 * @param xmlString
	 * @param xpath
	 * @return
	 * @throws Exception
	 */
	public String getSingleValue(String xmlString, String xpath)
			throws Exception {
		try {
			if ((xmlString == null || xmlString.trim().equals(""))
					&& doc == null) {
				return null;
			}
			load(xmlString);
			Node node = doc.selectSingleNode(xpath);
			if (node != null)
				return node.getStringValue();
			else {
				log.info("xml中没有指定的属性信息。" + xpath);
				return null;
			}
		} catch (RuntimeException e) {
			log.error("获取指定属性值失败。", e);
			throw e;
		}
	}

	/**
	 * 获取单个属性的值
	 * 
	 * @param xpath
	 * @return
	 */
	public String getSingleValue(String xpath) throws Exception {
		return getSingleValue(null, xpath);
	}

	/**
	 * 根据输入的xpath，找出所有属性值
	 * 
	 * @param xmlString
	 * @param xpath
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<String> getMultiValue(String xmlString, String xpath)
			throws Exception {
		try {
			load(xmlString);
			List<Node> nodes = doc.selectNodes(xpath);
			List<String> results = new ArrayList<String>(0);
			if (nodes != null && nodes.size() > 0) {
				for (Node node : nodes) {
					results.add(node.getStringValue());
				}
				return results;
			} else {
				log.info("找不到对应的节点." + xpath);
				return null;
			}
		} catch (DocumentException e) {
			log.error("获取属性值失败。", e);
			throw e;
		}
	}

	public List<String> getMultiValue(String xpath) throws Exception {
		return getMultiValue(null, xpath);
	}

	/**
	 * 根据输入的参数名称，从xml串获取参数值
	 * 
	 * @param xmlString
	 * @param paramNames 参数名称集
	 * @return
	 */
	public List<String> getParams(String xmlString, String[] paramNames)
			throws Exception {
		reload(xmlString);
		return getParams(paramNames, PARAMS_XPATH);
	}

	public List<String> getParams(String[] paramNames) throws Exception {
		return getParams(null, paramNames);
	}

	/**
	 * 根据输入的参数名称，从xml串获取值,需要xpath
	 * 
	 * @param xmlString
	 * @param paramNames
	 * @param xpath
	 * @return
	 * @throws Exception
	 */
	public List<String> getParams(String xmlString, String[] paramNames,
			String xpath) throws Exception {
		try {
			List<Node> nodes = getNodes(xmlString, xpath);
			List<String> rtnObj = new ArrayList<String>(0);
			if(nodes == null) return null;
			Outer: for (String name : paramNames) {
				for (Node node : nodes) {
					if (node.getName().equals(name)) {
						rtnObj.add(node.getStringValue());
						continue Outer;
					}
				}
				log.info("传入XML参数中未找到 " + name + " 字段");
				throw new Exception("传入XML参数中未找到 " + name + " 字段");
			}
			return rtnObj;
		} catch (DocumentException e) {
			log.error("从xml串解析参数列表失败。", e);
			throw e;
		}
	}

	public List<String> getParams(String[] paramNames, String xpath)
			throws Exception {
		try {
			List<Node> nodes = getNodes(xpath);
			List<String> rtnObj = new ArrayList<String>(0);
			if(nodes == null) return null;
			Outer: for (String name : paramNames) {
				for (Node node : nodes) {
					if (node.getName().equals(name)) {
						rtnObj.add(node.getStringValue());
						continue Outer;
					}
				}
				log.info("传入XML参数中未找到 " + name + " 字段");
				//throw new Exception("传入XML参数中未找到 " + name + " 字段");
			}
			return rtnObj;
		} catch (DocumentException e) {
			log.error("从xml串解析参数列表失败。", e);
			throw e;
		}
	}

	/**
	 * 过滤xmlString，根据输入的xml和节点名字以及值过滤条件过滤掉不需要的节点。
	 * （注意，被删除掉的是输入的节点的父节点，会有非根结点的验证。）
	 * 
	 * @param xmlString
	 *            需要过滤的串
	 * @param xpath
	 *            查找的xpath串（包含查找的条件信息）
	 * @return 过滤后的xml串
	 */
	@SuppressWarnings("unchecked")
	public String filterOutput(String xmlString, String xpath) throws Exception {
		try {
			load(xmlString);
			if (doc == null)
				return null;
			List<Node> nodes = doc.selectNodes(xpath);
			for (Node node : nodes) {
				node.getParent().remove(node);
			}
			return doc.asXML();
		} catch (DocumentException e) {
			log.error("过滤xml串失败。", e);
			throw e;
		}
	}

	public void appendData(String xpath, String... xmlStrings) {

	}

	/**
	 * 根据指定的xpath获取节点集合。
	 * 
	 * @param xpath
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<Element> getELements(String xmlString, String xpath)
			throws Exception {
		try {
			load(xmlString);
			if (doc == null)
				return null;
			List<Element> nodes = doc.selectNodes(xpath);
			return nodes;
		} catch (DocumentException e) {
			log.error("获取节点集合失败。", e);
			throw e;
		}
	}

	public List<Element> getELements(String xpath) throws Exception {
		return getELements(null, xpath);
	}

	@SuppressWarnings("unchecked")
	public List<Node> getNodes(String xmlString, String xpath) throws Exception {
		try {
			load(xmlString);
			if (doc == null)
				return new ArrayList<Node>(0);
			List<Node> nodes = doc.selectNodes(xpath);
			return nodes;
		} catch (DocumentException e) {
			log.error("获取节点失败。", e);
			throw e;
		}
	}

	public List<Node> getNodes(String xpath) throws Exception {
		if(doc == null) return null;
		return doc.selectNodes(xpath);
	}

	public Node getSingleNode(String xmlString, String xpath) throws Exception {
		try {
			load(xmlString);
			if (doc == null)
				return null;
			Node node = doc.selectSingleNode(xpath);
			return node;
		} catch (RuntimeException e) {
			log.error("获取节点失败。", e);
			throw e;
		}
	}

	public Node getSingleNode(String xpath) throws Exception {
		return getSingleNode(null, xpath);
	}

	@SuppressWarnings("unchecked")
	public String addNodes(String xmlString, String xpath, String nodeName,
			String value) {
		try {
			load(xmlString);
			if (doc == null)
				return null;
			List<Node> nodes = doc.selectNodes(xpath);
			for (Node node : nodes) {
				Element e = (Element) node;
				Element c = e.addElement(nodeName);
				c.addText(value);
			}
			return doc.asXML();
		} catch (Exception e) {
			log.error("添加节点失败。", e);
			return null;
		}
	}

	public String addNodes(String xpath, String nodeName, String value) {
		return addNodes(null, xpath, nodeName, value);
	}

	private boolean validateNull(Object s) {
		if (s == null || s.toString().trim().equals("")) {
			return false;
		}
		return true;
	}

	public String getAttributeValue(String xmlString, String xpath)
			throws DocumentException {
		try {
			load(xmlString);
			if (doc == null)
				return null;
			Attribute a = (Attribute) doc.selectObject(xpath);
			return a.getValue();
		} catch (DocumentException e) {
			log.error("获取属性值失败。", e);
			throw e;
		} catch (RuntimeException e) {
			log.error("获取属性值失败。", e);
			throw e;
		}
	}

	public String getAttributeValue(String xpath) throws DocumentException {
		return getAttributeValue(null, xpath);
	}

	public String getXmlString() {
		if (doc != null)
			return doc.asXML();
		else
			return null;
	}

	private void loadXml(String xml) throws DocumentException {
		try {
			if (xml == null || xml.equals("")) {
				return;
			}
			doc = DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			log.error("创建xml解析器失败。", e);
			throw e;
		}
	}

	private void load(String xml) throws DocumentException {
		if (doc == null && xml != null && !xml.trim().equals("")) {
			loadXml(xml);
		}
	}

	/**
	 * 强行重新读入xml
	 * 
	 * @param xmlString
	 * @throws DocumentException
	 */
	public void reload(String xmlString) throws DocumentException {
		loadXml(xmlString);
	}

	public static void main(String[] args) throws Exception {
		String xml = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ISarftService/ProcessResponse</a:Action><ActivityId CorrelationId=\"ef4f07d1-fa4a-48ec-9cb3-73d1b02edcff\" xmlns=\"http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics\">47cd77f2-ed60-4084-9d4c-87bf3c78bc1d</ActivityId></s:Header><s:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><reply xmlns=\"http://www.crifst.ac.cn/2013/OTSAPI\"><msg>&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;&lt;OnlineTicketingServiceReply xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" Datetime=\"2014-12-02T15:38:30\" Version=\"1.0\"&gt;&lt;QuerySessionReply ErrorCode=\"\" ErrorMessage=\"\" Id=\"ID_QuerySessionReply\" Status=\"Success\"&gt;&lt;Sessions CinemaCode=\"0042\"&gt;&lt;/Sessions&gt;&lt;/QuerySessionReply&gt;&lt;/OnlineTicketingServiceReply&gt;</msg></reply></s:Body></s:Envelope>";
		XmlParser parser = new XmlParser();
		parser.reload(xml);
		
		
		Document resultDoc = null;   
		try {   
		    resultDoc = DocumentHelper.parseText(xml);   
		} catch (DocumentException e) {   
		    e.printStackTrace();   
		}   
		Element rootEle = resultDoc.getRootElement();  
		
		Element ele = rootEle.element("s:Body");
 
		
		System.out.print("end");
	}
	
	/** 
	 * 根据request将XML流解析成MAP 
	 *
	 * @return 
	 * @throws IOException 
	 * @throws DocumentException 
	 */  
	public static Map<String, String> xmlToMap(InputStream in) throws DocumentException, IOException {  
	    Map<String, String> map = new HashMap<>();  
	    SAXReader reader = new SAXReader();  
	    Document document = reader.read(in);  
	    Element root = document.getRootElement();  
	    List<Element> list = root.elements();  
	    for (Element e : list) {  
	        map.put(e.getName(), e.getText());  
	    }  
	    in.close();  
	    return map;  
	} 
	
	/** 
     * map转xml map中没有根节点的键 
     * @param map 
     * @param rootName 
     * @throws DocumentException 
     * @throws IOException 
     *//*  
    public static Document map2xml(Map<String, Object> map, String rootName) throws DocumentException, IOException  {  
        Document doc = DocumentHelper.createDocument();  
        Element root = DocumentHelper.createElement(rootName);  
        doc.add(root);  
        map2xml(map, root);  
        //System.out.println(doc.asXML());  
        //System.out.println(formatXml(doc));  
        return doc;  
    }  
      
    *//** 
     * map转xml map中含有根节点的键 
     * @param map 
     * @throws DocumentException 
     * @throws IOException 
     *//*  
    public static Document map2xml(Map<String, Object> map) throws DocumentException, IOException  {  
        Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();  
        if(entries.hasNext()){ //获取第一个键创建根节点  
            Map.Entry<String, Object> entry = entries.next();  
            Document doc = DocumentHelper.createDocument();  
            Element root = DocumentHelper.createElement(entry.getKey());  
            doc.add(root);  
            map2xml((Map)entry.getValue(), root);  
            //System.out.println(doc.asXML());  
            //System.out.println(formatXml(doc));  
            return doc;  
        }  
        return null;  
    }  
      
    *//** 
     * map转xml 
     * @param map
     * @return 
     *//*  
    private static Element map2xml(Map<String, Object> map, Element body) {  
        Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();  
        while (entries.hasNext()) {  
            Map.Entry<String, Object> entry = entries.next();  
            String key = entry.getKey();  
            Object value = entry.getValue();  
            if(key.startsWith("@")){    //属性  
                body.addAttribute(key.substring(1, key.length()), value.toString());  
            } else if(key.equals("#text")){ //有属性时的文本  
                body.setText(value.toString());  
            } else {  
                if(value instanceof java.util.List ){  
                    List list = (List)value;  
                    Object obj;  
                    for(int i=0; i<list.size(); i++){  
                        obj = list.get(i);  
                        //list里是map或String，不会存在list里直接是list的，  
                        if(obj instanceof java.util.Map){  
                            Element subElement = body.addElement(key);  
                            map2xml((Map)list.get(i), subElement);  
                        } else {  
                            body.addElement(key).setText((String)list.get(i));  
                        }  
                    }  
                } else if(value instanceof java.util.Map ){  
                    Element subElement = body.addElement(key);  
                    map2xml((Map)value, subElement);  
                } else {  
                    body.addElement(key).setText(value.toString());  
                }  
            }  
            //System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());  
        }  
        return body;  
    }  */
    
	public static String map2XmlString(Map<String, String> map) {
        String xmlResult = "";

        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (String key : map.keySet()) {
            String value = "<![CDATA[" + map.get(key) + "]]>";
            sb.append("<" + key + ">" + value + "</" + key + ">");
        }
        sb.append("</xml>");
        xmlResult = sb.toString();

        return xmlResult;
    }
	
}
