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

nl.hsac.fitnesse.fixture.util.MapHelper Maven / Gradle / Ivy

package nl.hsac.fitnesse.fixture.util;

import jdk.nashorn.api.scripting.ScriptObjectMirror;
import nl.hsac.fitnesse.fixture.slim.SlimFixtureException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MapHelper {
    private static final Pattern LIST_INDEX_PATTERN = Pattern.compile("(\\S+)\\[(\\d+)\\]");
    protected HtmlCleaner htmlCleaner = new HtmlCleaner();

    /**
     * Gets value from map.
     * @param map map to get value from.
     * @param name name of (possibly nested) property to get value from.
     * @return value found, if it could be found, null otherwise.
     */
    public Object getValue(Map map, String name) {
        String cleanName = htmlCleaner.cleanupValue(name);
        return getValueImpl(map, cleanName, true);
    }

    protected Object getValueImpl(Map map, String name, boolean throwIfNoList) {
        Object value = null;
        if (map.containsKey(name)) {
            value = map.get(name);
        } else {
            String[] parts = name.split("\\.", 2);
            if (parts.length > 1) {
                Object nested = getValueImpl(map, parts[0], throwIfNoList);
                if (nested instanceof Map) {
                    Map nestedMap = (Map) nested;
                    value = getValueImpl(nestedMap, parts[1], throwIfNoList);
                }
            } else if (isListName(name)) {
                value = getListValue(map, name);
            } else if (isListIndexExpr(name)) {
                value = getIndexedListValue(map, name, throwIfNoList);
            }
        }
        return value;
    }

    /**
     * Stores value in map.
     * @param value value to be passed.
     * @param name name to use this value for.
     * @param map map to store value in.
     */
    public void setValueForIn(Object value, String name, Map map) {
        if (isListName(name)) {
            String valueStr = null;
            if (value != null) {
                valueStr = value.toString();
            }
            setValuesForIn(valueStr, stripListIndicator(name), map);
        } else {
            if (name.endsWith("\\[]")) {
                name = name.replace("\\[]", "[]");
            }
            String cleanName = htmlCleaner.cleanupValue(name);
            Object cleanValue = getCleanValue(value);
            if (map.containsKey(cleanName)) {
                // overwrite current value
                map.put(cleanName, cleanValue);
            } else {
                int firstDot = cleanName.indexOf(".");
                if (firstDot > -1) {
                    String key = cleanName.substring(0, firstDot);
                    Object nested = getValueImpl(map, key, false);
                    if (nested == null) {
                        nested = new LinkedHashMap();
                        if (isListIndexExpr(key)) {
                            setIndexedListValue(map, key, nested);
                        } else {
                            map.put(key, nested);
                        }
                    }
                    if (nested instanceof Map) {
                        Map nestedMap = (Map) nested;
                        String lastPart = cleanName.substring(firstDot + 1);
                        setValueForIn(cleanValue, lastPart, nestedMap);
                    } else {
                        throw new SlimFixtureException(false, key + " is not a map, but " + nested.getClass());
                    }
                } else if (isListIndexExpr(name)) {
                    setIndexedListValue(map, cleanName, cleanValue);
                } else {
                    map.put(cleanName, cleanValue);
                }
            }
        }
    }

    /**
     * Adds a value to the end of a list.
     * @param value value to be passed.
     * @param name name to use this value for.
     * @param map map to store value in.
     */
    public void addValueToIn(Object value, String name, Map map) {
        Object val = getValue(map, name);
         if (val instanceof Collection) {
             Object cleanValue = getCleanValue(value);
             ((Collection) val).add(cleanValue);
         } else if (val == null) {
             setValueForIn(value, name + "[0]", map);
         } else {
            throw new SlimFixtureException(false, "name is not a list but: " + val.getClass().getSimpleName());
         }
    }

    /**
     * Adds all values in the otherMap to map.
     * @param otherMap to obtain values from.
     * @param map map to store value in.
     */
    public void copyValuesFromTo(Map otherMap, Map map) {
        map.putAll(otherMap);
    }

    /**
     * Stores list of values in map.
     * @param values comma separated list of values.
     * @param name name to use this list for.
     * @param map map to store values in.
     */
    public void setValuesForIn(String values, String name, Map map) {
        String cleanName = htmlCleaner.cleanupValue(name);
        String[] valueArrays = values.split("\\s*,\\s*");
        List valueObjects = new ArrayList(valueArrays.length);
        for (int i = 0; i < valueArrays.length; i++) {
            Object cleanValue = getCleanValue(valueArrays[i]);
            valueObjects.add(cleanValue);
        }
        setValueForIn(valueObjects, cleanName, map);
    }

    public boolean removeFrom(String keyToRemove, Map map) {
        String cleanKey = htmlCleaner.cleanupValue(keyToRemove);
        boolean result = false;
        if (map.containsKey(cleanKey)) {
            map.remove(cleanKey);
            return true;
        } else {
            int firstDot = cleanKey.indexOf(".");
            if (firstDot > -1) {
                String key = cleanKey.substring(0, firstDot);
                Object nested = getValueImpl(map, key, false);
                if (nested instanceof Map) {
                    Map nestedMap = (Map) nested;
                    String nestedKey = cleanKey.substring(firstDot + 1);
                    result = removeFrom(nestedKey, nestedMap);
                }
            }
        }
        return result;
    }

    /**
     * Determines whether map one's content matches two.
     * @param one map the check content of.
     * @param two other map to check.
     * @return true if both maps are equal.
     */
    public boolean contentOfEquals(Map one, Object two) {
        if (one == null) {
            return two == null;
        } else {
            return one.equals(two);
        }
    }

    /**
     * Determines size of either (Map or Collection) value in the map.
     * @param expr expression indicating which (possibly nested) value in the map to determine size of.
     * @param map map to find value in.
     * @return size of value.
     * @throws SlimFixtureException if the value found is not a Map or Collection.
     */
    public int sizeOfIn(String expr, Map map) {
        int result;
        Object val = getValue(map, expr);
        if (val instanceof Map) {
            result = ((Map) val).size();
        } else if (val instanceof Collection) {
            result = ((Collection) val).size();
        } else {
            throw new SlimFixtureException(false, expr + " is not a collection");
        }
        return result;
    }

    public Object getCleanValue(Object value) {
        Object cleanValue = value;
        if (value instanceof String) {
            cleanValue = htmlCleaner.parseValue((String) value);
            if (cleanValue instanceof String) {
                cleanValue = htmlCleaner.cleanupValue((String) cleanValue);
            }
        }
        return cleanValue;
    }

    protected Object getListValue(Map map, String name) {
        return getValue(map, stripListIndicator(name));
    }

    protected Object getIndexedListValue(Map map, String name, boolean throwIfNoList) {
        Object value = null;
        String prop = getListKeyName(name);
        Object val = getValue(map, prop);
        if (!(val instanceof List) && val instanceof ScriptObjectMirror) {
            ScriptObjectMirror mirror = (ScriptObjectMirror) val;
            if (mirror.isArray()) {
                val = mirror.to(List.class);
            }
        }
        if (val instanceof List) {
            List list = (List) val;
            int index = getListIndex(name);
            if (index < list.size()) {
                value = list.get(index);
            } else {
                value = null;
            }
        } else {
            if (val != null || throwIfNoList) {
                throw new SlimFixtureException(false, prop + " is not a list, but " + val);
            }
        }
        return value;
    }

    protected void setIndexedListValue(Map map, String name, Object value) {
        String prop = getListKeyName(name);
        Object val = getValue(map, prop);
        if (val == null) {
            val = new ArrayList();
            setValueForIn(val, prop, map);
        }
        if (val instanceof List) {
            List list = (List) val;
            int index = getListIndex(name);
            while (list.size() <= index) {
                list.add(null);
            }
            list.set(index, value);
        } else {
            throw new SlimFixtureException(false, prop + " is not a list, but " + val);
        }
    }

    protected boolean isListName(String name) {
        return name.endsWith("[]") && !name.endsWith("\\[]");
    }

    protected String stripListIndicator(String key) {
        return key.substring(0, key.length() - 2);
    }

    protected boolean isListIndexExpr(String key) {
        return getListIndexMatcher(key).matches();
    }

    protected String getListKeyName(String key) {
        Matcher matcher = getListIndexMatcher(key);
        matcher.matches();
        return matcher.group(1);
    }

    protected int getListIndex(String key) {
        Matcher matcher = getListIndexMatcher(key);
        matcher.matches();
        return Integer.parseInt(matcher.group(2));
    }

    protected Matcher getListIndexMatcher(String key) {
        return LIST_INDEX_PATTERN.matcher(key);
    }

    public void setHtmlCleaner(HtmlCleaner htmlCleaner) {
        this.htmlCleaner = htmlCleaner;
    }
}