All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.s1.web.formats.Maps Maven / Gradle / Ivy

There is a newer version: 0.1.3
Show newest version
package org.s1.web.formats;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * Map helper. Contains useful methods
 *
 * @author Grigory Pykhov
 */
public class Maps {

    private Maps() {
    }

    /**
     * Get value from path (null is default) and cast to type
     *
     * @param type Type
     * @param data Map data
     * @param path Path
     * @param   Type
     * @return Object
     */
    public static  T get(Class type, Map data, String path) {
        return get(type, data, path, null);
    }

    /**
     * Get value from path and cast to type
     *
     * @param type Type
     * @param data Map data
     * @param path Path
     * @param def  Default
     * @param   Type
     * @return Object
     */
    public static  T get(Class type, Map data, String path, T def) {
        return Types.cast(get(data, path, def), type);
    }

    /**
     * Get value from path (null is default)
     *
     * @param data Map data
     * @param path Path
     * @param   Type
     * @return Object
     */
    public static  T get(Map data, String path) {
        return get(data, path, null);
    }

    /**
     * Get value from path
     *
     * @param data Map data
     * @param path Path
     * @param def  Default
     * @param   Type
     * @return Object
     */
    public static  T get(Map data, String path, T def) {
        Object ret = def;
        try {
            String[] parts = tokenizePath(path);
            Object o = data;
            for (int i = 0; i < parts.length; i++) {
                int[] j = getNumber(parts[i]);
                String name = getLocalName(parts[i]);
                o = ((Map) o).get(name);
                if (j != null) {
                    for (int k = 0; k < j.length; k++) {
                        o = ((List) o).get(j[k]);
                    }
                }
            }
            if (o != null)
                ret = o;
            else
                ret = def;
        } catch (Throwable e) {
        }
        return (T) ret;
    }


    /**
     * Set value to key identified by path
     *
     * @param data Map data
     * @param path Path
     * @param val  Value
     */
    public static void set(Map data, String path, Object val) {
        String[] parts = tokenizePath(path);
        Map o = data;
        for (int i = 0; i < parts.length; i++) {
            int[] j = getNumber(parts[i]);
            String name = getLocalName(parts[i]);
            if (i == parts.length - 1) {
                if (j != null) {
                    if (!o.containsKey(name)) {
                        o.put(name, new ArrayList());
                    }
                    List o1 = (List) o.get(name);

                    for (int k = 0; k < j.length; k++) {
                        if (o1.size() <= j[k]) {
                            for (int ii = 0; ii <= j[k] - o1.size(); ii++)
                                o1.add(null);
                            if (k == j.length - 1) {
                                o1.set(j[k], new HashMap());
                            } else {
                                o1.set(j[k], new ArrayList());
                            }
                        }
                        if (k == j.length - 1) {
                            o1.set(j[k], val);
                        } else {
                            o1 = (List) o1.get(j[k]);
                        }
                    }
                } else {
                    o.put(name, val);
                }
            } else {

                if (j != null) {
                    if (!o.containsKey(name)) {
                        o.put(name, new ArrayList());
                    }
                    List o1 = (List) o.get(name);

                    for (int k = 0; k < j.length; k++) {
                        if (o1.size() <= j[k]) {
                            for (int ii = o1.size(); ii <= j[k]; ii++) {
                                o1.add(null);
                            }
                            if (k == j.length - 1) {
                                o1.set(j[k], new HashMap());
                            } else {
                                o1.set(j[k], new ArrayList());
                            }
                        }
                        if (k == j.length - 1) {
                            o = (Map) o1.get(j[k]);
                        } else {
                            o1 = (List) o1.get(j[k]);
                        }
                    }
                } else {
                    if (!o.containsKey(name)) {
                        o.put(name, new HashMap());
                    }
                    o = (Map) o.get(name);
                }
            }
        }
    }

    /**
     *
     * @param args Elements
     * @param  Type
     * @return ArrayList
     */
    public static  List newArrayList(T... args) {
        List list = new ArrayList();
        for (T t : args) {
            list.add(t);
        }
        return list;
    }

    /**
     * @param args Key, Value array
     * @return HashMap
     */
    public static Map newSOHashMap(Object... args) {
        return newHashMap(args);
    }

    /**
     * @param args Key, Value array
     * @param   Key type
     * @param   Value type
     * @return HashMap
     */
    public static  Map newHashMap(Object... args) {
        Map m = new HashMap();
        for (int i = 0; i < args.length; i += 2) {
            m.put((K) args[i], i + 1 >= args.length ? null : (V) args[i + 1]);
        }
        return m;
    }

    private static String[] tokenizePath(String path) {
        String s = path;
        s = s.replace("&", "&");
        s = s.replace("\\\\", "&backslash;");
        s = s.replace("\\.", "˙");
        String[] p = s.split("\\.");
        String[] p2 = new String[p.length];
        for (int i = 0; i < p.length; i++) {
            p2[i] = p[i]
                    .replace("˙", ".")
                    .replace("&backslash;", "\\\\")
                    .replace("&", "&");
        }
        return p2;
    }

    private static int[] getNumber(String name) {
        String s = name;
        s = s.replace("&", "&");
        s = s.replace("\\\\", "&backslash;");
        s = s.replace("\\[", "&open;");
        s = s.replace("\\]", "&close;");
        if (s.indexOf("[") < s.indexOf("]")) {
            String s1 = s.substring(s.indexOf("[") + 1, s.lastIndexOf("]"));
            String[] s2 = s1.split("\\]\\[");
            int[] r = new int[s2.length];
            for (int i = 0; i < s2.length; i++) {
                r[i] = Integer.parseInt(s2[i]);
            }
            return r;
        }
        return null;
    }

    private static String getLocalName(String name) {
        String s = name;
        s = s.replace("&", "&");
        s = s.replace("\\\\", "&backslash;");
        s = s.replace("\\[", "&open;");
        s = s.replace("\\]", "&close;");
        String s1 = s;
        if (s.indexOf("[") < s.indexOf("]")) {
            s1 = s.substring(0, s.indexOf("["));
        }
        name = s1.replace("&open;", "[").replace("&close;", "]")
                .replace("&backslash;", "\\")
                .replace("&", "&");
        //name = name.replace("\\[", "[").replace("\\]", "]");
        return name;
    }

    public static  T iterate(Object o, IterateFunction closure) {
        return (T) iterateNamedObjectFromLeaf(null, "", o, closure);
    }

    private static Object iterateNamedObjectFromLeaf(String name, String path, Object o, IterateFunction closure) {
        if (o instanceof Map) {
            final Map m1 = new HashMap();
            Map m = (Map) o;
            for (Map.Entry e : m.entrySet()) {
                m1.put(e.getKey(),
                        iterateNamedObjectFromLeaf(e.getKey(), (!path.isEmpty() ? path + "." + e.getKey() : e.getKey()), e.getValue(), closure));
            }
            return closure.call(name, m1, path);
        } else if (o instanceof List) {
            List l = new ArrayList();
            for (int i = 0; i < ((List) o).size(); i++) {
                l.add(iterateNamedObjectFromLeaf(null, path + "[" + i + "]", ((List) o).get(i), closure));
            }
            return closure.call(name, l, path);
        } else {
            return closure.call(name, o, path);
        }
    }

    /**
     *
     */
    public static abstract class IterateFunction {
        public abstract Object call(String name, Object value, String path);
    }

    /**
     * @param beanType Bean type
     * @param map      Map
     * @param       Bean Type
     * @return Bean instance
     */
    public static  T convertMapToBean(Class beanType, Map map) {
        T bean = null;
        try {
            bean = beanType.newInstance();
        } catch (Exception e) {
            throw new IllegalStateException("Cannot instantiate bean of class " + beanType, e);
        }
        BeanInfo info = null;
        try {
            info = Introspector.getBeanInfo(bean.getClass());
        } catch (IntrospectionException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }

        //setters
        for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
            Method writer = pd.getWriteMethod();
            if (writer != null && !"class".equals(pd.getName())) {
                try {
                    Object o = map.get(pd.getName());
                    //o = convertMapTypeToBeanType(pd.getPropertyType(), o);
                    o = convertMapTypeToBeanType(pd.getWriteMethod().getGenericParameterTypes()[0], o);
                    writer.invoke(bean, o);
                } catch (Exception e) {
                }
            }
        }

        //public fields
        Class cls = bean.getClass();
        while (cls != null && cls != Object.class) {
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                //if(field.isAccessible())
                try {
                    Object o = map.get(field.getName());
                    o = convertMapTypeToBeanType(field.getGenericType(), o);
                    field.set(bean, o);
                } catch (Exception e) {
                }
            }
            cls = cls.getSuperclass();
        }

        return bean;
    }

    /**
     * @param type Type
     * @param val  Source value
     * @param   Type
     * @return Value converted for bean field
     */
    public static  T convertMapTypeToBeanType(Class type, Object val) {
        return convertMapTypeToBeanType((Type)type,val);
    }

    /**
     *
     * @param type Type
     * @param val Source value
     * @param  Type
     * @return Value converted for bean field
     */
    public static  T convertMapTypeToBeanType(Type type, Object val) {
        if(type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType)type;
            Class cls = (Class)pt.getRawType();
            if (Map.class.isAssignableFrom(cls)) {
                Class tp = (Class) pt.getActualTypeArguments()[0];
                //try convert map keys to beans
                Map l = new HashMap();
                if (val instanceof Map) {
                    Map v = (Map) val;
                    for (Object k : v.keySet()) {
                        l.put(k, convertMapTypeToBeanType(tp, v.get(k)));
                    }
                }
                return (T) l;
            } else if (List.class.isAssignableFrom(cls)) {
                Class tp = (Class) pt.getActualTypeArguments()[0];
                //try convert List elements to beans
                List l = new ArrayList();
                if (val instanceof List) {
                    for (Object o : (List) val) {
                        l.add(convertMapTypeToBeanType(tp, o));
                    }
                } else if (val instanceof Set) {
                    for (Object o : (Set) val) {
                        l.add(convertMapTypeToBeanType(tp, o));
                    }
                }
                return (T) l;
            } else if (Set.class.isAssignableFrom(cls)) {
                Class tp = (Class) pt.getActualTypeArguments()[0];
                //try convert Set elements to beans
                Set l = new HashSet();
                if (val instanceof List) {
                    for (Object o : (List) val) {
                        l.add(convertMapTypeToBeanType(tp, o));
                    }
                } else if (val instanceof Set) {
                    for (Object o : (Set) val) {
                        l.add(convertMapTypeToBeanType(tp, o));
                    }
                }
                return (T) l;
            }
        }else if(type instanceof Class){
            Class cls = (Class) type;

            if (val instanceof Map && !Map.class.isAssignableFrom(cls)) {
                return (T)convertMapToBean(cls, (Map) val);
            }

            return (T)Types.cast(val,cls);
        }
        return (T)val;
    }

    /**
     * @param bean Bean
     * @return Map
     */
    public static Map convertBeanToMap(Object bean) {
        Map result = new HashMap();
        BeanInfo info = null;
        try {
            info = Introspector.getBeanInfo(bean.getClass());
        } catch (IntrospectionException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }

        //getters
        for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
            Method reader = pd.getReadMethod();
            if (reader != null && !"class".equals(pd.getName()))
                try {
                    result.put(pd.getName(), convertBeanTypeToMapType(reader.invoke(bean)));
                } catch (Exception e) {
                }
        }

        //fields
        Class cls = bean.getClass();
        while (cls != null && cls != Object.class) {
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                //if(field.isAccessible())
                try {
                    result.put(field.getName(), convertBeanTypeToMapType(field.get(bean)));
                } catch (Exception e) {
                }
            }
            cls = cls.getSuperclass();
        }
        return result;
    }

    /**
     * @param val Value from bean field
     * @param  Type
     * @return Value for Map
     */
    public static  T convertBeanTypeToMapType(Object val) {
        if (val instanceof Map) {
            Map l = (Map) val;
            Map l2 = new HashMap();
            for (Object k : l.keySet()) {
                l2.put(k, convertBeanTypeToMapType(l.get(k)));
            }
            return (T)l2;
        } else if (val instanceof List) {
            List l = (List) val;
            List l2 = new ArrayList();
            for (Object it:l) {
                l2.add(convertBeanTypeToMapType(it));
            }
            return (T)l2;
        } else if (val instanceof Set) {
            Set l = (Set)val;
            Set l2 = new HashSet();
            for (Object o : l) {
                l2.add(convertBeanTypeToMapType(o));
            }
            return (T)l2;
        } else if (val instanceof Integer || val instanceof String || val instanceof Date || val instanceof Boolean || val instanceof Long
                || val instanceof Double || val instanceof Float || val instanceof BigInteger || val instanceof BigDecimal
                || val.getClass() == int.class || val.getClass() == long.class || val.getClass() == boolean.class ||
                val.getClass() == float.class || val.getClass() == double.class) {
            return (T)val;
        } else if (val instanceof Enum) {
            return (T)val.toString();
        } else if (val instanceof Object) {
            return (T)convertBeanToMap(val);
        }
        return (T)val;
    }

    /**
     * Get difference between two maps
     *
     * @param object1 Object1
     * @param object2 Object2
     * @return List of differences
     */
    public static List diff(Map object1, Map object2) {
        if (object1 == null)
            object1 = newHashMap();
        if (object2 == null)
            object2 = newHashMap();
        object1 = Types.copy(object1);
        object2 = Types.copy(object2);
        final List diff = newArrayList();

        diffTwoMaps(diff, object1, object2, true);
        diffTwoMaps(diff, object2, object1, false);

        return diff;
    }

    private static void diffTwoMaps(final List diff, final Map m1,
                                    final Map m2, final boolean m1Old) {
        iterate(m1, new IterateFunction() {
            @Override
            public Object call(String name, Object value, String path) {
                Object o = value;
                if (o instanceof Map) {

                } else if (o instanceof List) {

                } else {
                    Object oo = null;
                    if (!Types.isNullOrEmpty(path))
                        oo = get(m2, path, null);
                    if (m1Old) {
                        if (!Types.equals(o, oo)) {
                            DiffBean res = new DiffBean(path, o, oo);
                            boolean b = false;
                            for (DiffBean db : diff) {
                                if (res.getPath().equals(db.getPath())) {
                                    b = true;
                                }
                            }
                            if (!b) {
                                diff.add(res);
                            }
                        }
                    } else {
                        if (!Types.equals(o, oo)) {
                            DiffBean res = new DiffBean(path, oo, o);
                            boolean b = false;
                            for (DiffBean db : diff) {
                                if (res.getPath().equals(db.getPath())) {
                                    b = true;
                                }
                            }
                            if (!b) {
                                diff.add(res);
                            }
                        }
                    }
                }
                return o;
            }
        });
    }

    /**
     * Map diff result bean
     */
    public static class DiffBean {
        private String path;
        private Object value1;
        private Object value2;

        public DiffBean(String path, Object value1, Object value2) {
            this.path = path;
            this.value1 = value1;
            this.value2 = value2;
        }

        public String getPath() {
            return path;
        }

        public Object getValue1() {
            return value1;
        }

        public Object getValue2() {
            return value2;
        }
    }


}