package nl.hsac.fitnesse.fixture.util; 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+)\\]"); private 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<String, Object> map, String name) { String cleanName = htmlCleaner.cleanupValue(name); return getValueImpl(map, cleanName); } protected Object getValueImpl(Map<String, Object> map, String name) { 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]); if (nested instanceof Map) { Map<String, Object> nestedMap = (Map<String, Object>) nested; value = getValueImpl(nestedMap, parts[1]); } } else if (isListName(name)) { value = getListValue(map, name); } else if (isListIndexExpr(name)) { value = getIndexedListValue(map, name); } } 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<String, Object> 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 = value; if (value instanceof String) { cleanValue = htmlCleaner.cleanupValue((String) 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 = getValue(map, key); if (nested == null) { nested = new LinkedHashMap<String, Object>(); map.put(key, nested); } if (nested instanceof Map) { Map<String, Object> nestedMap = (Map<String, Object>) 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); } } } } /** * 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<String, Object> map) { String cleanName = htmlCleaner.cleanupValue(name); String[] valueArrays = values.split("\\s*,\\s*"); List<Object> valueObjects = new ArrayList<Object>(valueArrays.length); for (int i = 0; i < valueArrays.length; i++) { String cleanValue = htmlCleaner.cleanupValue(valueArrays[i]); valueObjects.add(cleanValue); } setValueForIn(valueObjects, cleanName, map); } /** * 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<String, Object> 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<String, Object> 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; } protected Object getListValue(Map<String, Object> map, String name) { return getValue(map, stripListIndicator(name)); } protected Object getIndexedListValue(Map<String, Object> map, String name) { Object value; String prop = getListKeyName(name); Object val = getValue(map, prop); if (val instanceof List) { List list = (List) val; int index = getListIndex(name); if (index < list.size()) { value = list.get(index); } else { value = null; } } else { throw new SlimFixtureException(false, prop + " is not a list, but " + val); } return value; } protected void setIndexedListValue(Map<String, Object> map, String name, Object value) { String prop = getListKeyName(name); Object val = getValue(map, prop); if (val == null) { val = new ArrayList<Object>(); 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; } }