package de.jpaw.bonaparte.xml;

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

import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class XmlJsonAdapter extends XmlAdapter<XmlJsonAdapter.JSON, Map<String, Object>> {

    @XmlType(name="JSON", namespace="http://www.jpaw.de/schema/bonaparte.xsd")
    public static class JSON {
        public List<KVP>     kvp;  // key / value pairs
    }
    @XmlType(name="KVP", namespace="http://www.jpaw.de/schema/bonaparte.xsd")
    public static class KVP {
        public String        key;
        public Double        num;
        public List<Double>  nums;
        public String        value;
        public List<String>  values;
        public Boolean       bool;
        public List<Boolean> bools;
        public JSON          obj;
        public List<JSON>    objs;
        public Object        any;
        public List<Object>  anys;
    }

    private static Object firstNonNull(Object ... objs) {
        for (Object o : objs)
            if (o != null)
                return o;
        return null;
    }

    // utility method to unmarshal a nested list of JSON objects
    protected List<Map<String, Object>> unmarshal(List<JSON> entries) throws Exception {
        if (entries == null)
            return null;
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(entries.size());
        for (JSON e : entries)
            result.add(unmarshal(e));
        return result;
    }

    @Override
    public Map<String, Object> unmarshal(JSON entries) throws Exception {
        if (entries == null || entries.kvp == null)
            return null;
        Map<String, Object> map = new HashMap<String, Object>(2 * entries.kvp.size());
        for(KVP kvp : entries.kvp) {
            map.put(kvp.key, firstNonNull(kvp.num, kvp.nums, kvp.value, kvp.values, kvp.bool, kvp.bools, unmarshal(kvp.obj), unmarshal(kvp.objs), kvp.any, kvp.anys));
        }
        return map;
    }

    // utility method to marshal a nested list of JSON objects
    protected List<JSON> marshal(List<Map<String, Object>> maps) throws Exception {
        if (maps == null)
            return null;
        List<JSON> result = new ArrayList<JSON>(maps.size());
        for (Map<String, Object> e : maps)
            result.add(marshal(e));
        return result;
    }

    @Override
    public JSON marshal(Map<String, Object> map) throws Exception {
        if (map == null)
            return null;
        List<KVP> xml = new ArrayList<KVP>(map.size());
        for(Map.Entry<String, Object> mapEntry : map.entrySet()) {
            KVP entry = new KVP();
            entry.key = mapEntry.getKey();
            Object v  = mapEntry.getValue();
            if (v != null) {
                if (v instanceof List) {
                    List l = (List)v;
                    if (l.size() > 0) {
                        Object w = l.get(0);
                        if (w instanceof Number)
                            entry.nums = l;
                        else if (w instanceof String)
                            entry.values = l;
                        else if (w instanceof Boolean)
                            entry.bools = l;
                        else if (w instanceof Map)
                            entry.objs = marshal((List<Map<String, Object>>)l);
                        else
                            entry.any = v;  // fallback
                    } // no action if empty list - cannot determine type, leave null
                } else {
                    if (v instanceof Number)
                        entry.num = ((Number)v).doubleValue();
                    else if (v instanceof String)
                        entry.value = (String)v;
                    else if (v instanceof Boolean)
                        entry.bool = (Boolean)v;
                    else if (v instanceof Map)
                        entry.obj = marshal((Map<String, Object>)v);
                    else
                        entry.any = v;  // fallback
                }
            }
            xml.add(entry);
        }
        JSON result = new JSON();
        result.kvp = xml;
        return result;
    }
}

@@ -3076,25 +3076,46 @@
 ntry.nums = 
-l
+convertToDoubleList(l)
 ;%0A          
@@ -3659,32 +3659,178 @@
 f (v instanceof 
+Double)%0A                        entry.num = (Double)v;  // explicit check for Double to avoid unbox/box%0A                    else if (v instanceof 
 Number)%0A        
@@ -4406,16 +4406,16 @@
  = xml;%0A
-
         
@@ -4434,11 +4434,768 @@
 t;%0A    %7D
+%0A%0A    protected List%3CDouble%3E convertToDoubleList(List src) %7B%0A        final List%3CDouble%3E dst = new ArrayList%3C%3E(src.size());%0A        for (Object o: src) %7B%0A            if (o == null) %7B%0A                // should not happen, really!%0A                dst.add(null);%0A            %7D else if (o instanceof Double) %7B%0A                dst.add((Double)o);%0A            %7D else if (o instanceof Number) %7B%0A                // convert int, long, BigDecimal to Double, otherwise JAXB will squeeze in some xsi:int etc... which violates the xsd%0A                dst.add(((Number)o).doubleValue());%0A            %7D else %7B%0A                // last resort to obtain some numeric value...%0A                dst.add(new Double(o.toString()));%0A            %7D%0A        %7D%0A        return dst;%0A    %7D
 %0A%7D%0A

convert numeric values to doubles for XML export of kvp element