package alien4cloud.utils;

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

import org.apache.commons.lang3.StringUtils;

import alien4cloud.exception.InvalidArgumentException;
import alien4cloud.rest.utils.JsonUtil;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.Maps;

/**
 * Utility class to ease map manipulation.
 */
public final class MapUtil {
    private MapUtil() {
    }

    /**
     * This method allows to add an element to the list of elements in a map for a given key. If the list is null for the given key it will create a new list,
     * insert it in the map and add the element.
     * 
     * @param listMap A map of <Key, List<Values>>
     * @param key The key in the map.
     * @param value The element to add to the list that is mapped by the given key.
     * @param <K> The key class.
     * @param <V> The class of the list elements.
     */
    public static <K, V> void addToList(Map<K, List<V>> listMap, K key, V value) {
        List<V> list = listMap.get(key);
        if (list == null) {
            list = new ArrayList<V>();
            listMap.put(key, list);
        }
        list.add(value);
    }

    /**
     * Try to get a value following a path in a map or a list/array. For example :
     * MapUtil.get(map, "a.b.c") correspond to:
     * map.get(a).get(b).get(c)
     * MapUtil.get(list, "1.b.c.2") correspond to:
     * list[1].get(b).get(c)[2]
     *
     * @param object the map/list to search for path
     * @param path keys in the map separated by '.'
     */
    public static Object get(Object object, String path) {
        if (StringUtils.isBlank(path)) {
            throw new InvalidArgumentException("Path is empty, cannot evaluate property for object " + object);
        }
        String[] tokens = path.split("[\\.\\]\\[]");

        Object value = object;
        for (String token : tokens) {
            if (StringUtils.isEmpty(token)) {
                continue;
            }
            if (value instanceof List) {
                List<Object> nested = (List<Object>) value;
                try {
                    int index = Integer.parseInt(token);
                    value = nested.get(index);
                    if (value == null) {
                        return null;
                    }
                } catch (NumberFormatException e) {
                    return null;
                }
            } else if (value instanceof Object[]) {
                Object[] nested = (Object[]) value;
                try {
                    int index = Integer.parseInt(token);
                    value = nested[index];
                    if (value == null) {
                        return null;
                    }
                } catch (NumberFormatException e) {
                    return null;
                }
            } else if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nested = (Map<String, Object>) value;
                value = nested.get(token);
                if (value == null) {
                    return null;
                }
            } else {
                return null;
            }
        }
        return value;
    }

    /**
     * Create a new hash map and fills it from the given keys and values (keys[index] -> values[index].
     * 
     * @param keys The array of keys.
     * @param values The array of values.
     * @return A map that contains for each key element in the keys array a value from the values array at the same index.
     */
    public static <K, V> Map<K, V> newHashMap(K[] keys, V[] values) {
        Map<K, V> map = new HashMap<K, V>();
        if (keys == null || values == null || keys.length != values.length) {
            throw new IllegalArgumentException("keys and values must be non-null and have the same size.");
        }
        for (int i = 0; i < keys.length; i++) {
            map.put(keys[i], values[i]);
        }
        return map;
    }

    /**
     * Revert a map key --> value become value --> key
     * 
     * @param map the map to revert
     * @param <K> the type of the key
     * @param <V> the type of the value
     * @return reverted map
     */
    public static <K, V> Map<V, K> revert(Map<K, V> map) {
        Map<V, K> reverted = new HashMap<V, K>();
        for (Map.Entry<K, V> entry : map.entrySet()) {
            reverted.put(entry.getValue(), entry.getKey());
        }
        return reverted;
    }

    /**
     * Remove the entry with 'oldKey' and put it (if exists) using 'newKey' unless if an entry already exist for 'newKey'.
     */
    public static <K, V> void replaceKey(Map<K, V> map, K oldKey, K newKey) {
        if (map == null || map.isEmpty()) {
            return;
        }
        if (map.containsKey(newKey)) {
            return;
        }
        V o = map.remove(oldKey);
        if (o != null) {
            map.put(newKey, o);
        }
    }

    public static Map<String, String> toString(Map<String, Object> stringObjectMap) throws JsonProcessingException {
        Map<String, String> stringStringMap = Maps.newHashMap();
        for (Map.Entry<String, Object> stringObjectEntry : stringObjectMap.entrySet()) {
            if (stringObjectEntry.getValue() != null) {
                if (stringObjectEntry.getValue() instanceof String) {
                    stringStringMap.put(stringObjectEntry.getKey(), (String) stringObjectEntry.getValue());
                } else {
                    stringStringMap.put(stringObjectEntry.getKey(), JsonUtil.toString(stringObjectEntry.getValue()));
                }
            } else {
                stringStringMap.put(stringObjectEntry.getKey(), null);
            }

        }
        return stringStringMap;
    }
}
@@ -1880,16 +1880,315 @@
 %5C%5B%5D%22);%0A%0A
+        return get(object, tokens);%0A    %7D%0A%0A    /**%0A     * Try to get a value following a path of tokens in a map or a list/array.%0A     *%0A     * @param object the map/list to search for path%0A     * @param tokens keys in the map%0A     */%0A    public static Object get(Object object, String... tokens) %7B%0A
         

Split MapUtils.get(object, path) method to allow passing a String... of tokens