package com.cyksj.common.util;

import com.ctc.wstx.stax.WstxInputFactory;
import com.ctc.wstx.stax.WstxOutputFactory;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author valor.
 * @date 2019-08-02 13:52
 */
public class Xmls {

    public Xmls() { }

    private static XmlMapper xmlMapper;

    /**
     *
     * @return typeFactory
     */
    public static TypeFactory javaType() {
        return TypeFactory.defaultInstance();
    }

    public static XmlMapper parser() {
        if (null == xmlMapper) {
            synchronized (Xmls.class) {
                if (null == xmlMapper) {
                    // 基于woodstox 处理xml
                    WstxInputFactory inputFactory = new WstxInputFactory();
                    WstxOutputFactory outputFactory = new WstxOutputFactory();
                    xmlMapper = new XmlMapper(inputFactory, outputFactory);

                    // 在序列化时忽略值为 null 的属性
                    xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                    // 序列化时不包括没有@JsonView注解的属性
                    xmlMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
                    // 在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
                    xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                    // 将日期转为时间戳
                    xmlMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
                }
            }
        }
        return xmlMapper;
    }

    /**
     * format entity to xml.
     *
     * @return String 「Xml」
     */
    public static String toXml(Object o) throws JsonProcessingException {
        return parser().writeValueAsString(o);
    }

    /**
     * parse json to map.
     *
     * @return Map<String, T> 「HashMap」
     */
    public static <T> Map<String, T> toMap(String json) throws Exception {
        JavaType type = javaType().constructMapType(HashMap.class, String.class, Object.class);
        return parser().readValue(json, type);
    }

    /**
     * parse bytes to map.
     *
     * @return Map<String, T> 「HashMap」
     */
    public static <T> Map<String, T> toMap(byte[] bytes) throws Exception {
        JavaType type = javaType().constructMapType(HashMap.class, String.class, Object.class);
        return parser().readValue(bytes, type);
    }

    /**
     * format entity to map.
     *
     * @return Map<String, T> 「HashMap」
     */
    public static <T> Map<String, T> toMap(Object o) {
        JavaType type = javaType().constructMapType(HashMap.class, String.class, Object.class);
        return parser().convertValue(o, type);
    }

    /**
     * parse xml to entity.
     *
     * @return T 「entity」
     */
    public static <T> T parseObject(String xml, Class<T> clazz) throws Exception {
        return parser().readValue(xml, clazz);
    }

    /**
     * parse xml to entity.
     *
     * @return T 「entity」
     */
    public static <T> T parseObject(Object o, Class<T> clazz) {
        return parser().convertValue(o, clazz);
    }

    /**
     * parse xml to ArrayList.
     *
     * @return List<T> 「ArrayList」
     */
    public static <T> List<T> parseList(String json, Class<T> clazz) throws Exception {
        JavaType type = parser().getTypeFactory().constructCollectionType(ArrayList.class, clazz);
        return parser().readValue(json, type);
    }

    /**
     * parse xml to ArrayList.
     *
     * @return List<T> 「ArrayList」
     */
    public static <T> List<T> parseList(Object o, Class<T> clazz) {
        JavaType type = parser().getTypeFactory().constructCollectionType(ArrayList.class, clazz);
        return parser().convertValue(o, type);
    }

}
