package org.openlca.cloud.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public class ObjectMap extends HashMap<String, Object> { private static final long serialVersionUID = 8510487677972200938L; private static final Gson mapper = new Gson(); public ObjectMap() { this(new HashMap<>()); } public static ObjectMap fromJson(String json) { return new ObjectMap(toMap(json)); } public static ObjectMap fromMap(Map<String, Object> managed) { return new ObjectMap(managed); } public static ObjectMap fromObject(Object object) { if (object == null) return null; String json = mapper.toJson(object); return fromJson(json); } private ObjectMap(Map<String, Object> managed) { if (managed != null) putAll(managed); } private static Map<String, Object> toMap(String json) { if (json == null) return null; return mapper.fromJson(json, new TypeToken<Map<String, Object>>() { }.getType()); } public void removeAllBut(String... fields) { if (fields == null) return; Set<String> fieldSet = new HashSet<>(); for (String field : fields) if (field.contains(".")) throw new IllegalArgumentException("removeAllBut doesn't support complex fields"); else fieldSet.add(field); for (String key : new HashSet<>(keySet())) if (!fieldSet.contains(key)) remove(key); } @Override public boolean remove(Object key, Object value) { if (key instanceof String && value instanceof String) { remove(new String[] { key.toString(), value.toString() }); return true; } else return super.remove(key, value); } public void remove(String... fields) { if (fields == null) return; for (String field : fields) remove(field); } @Override public Object remove(Object field) { return remove(this, field != null ? field.toString() : null); } private Object remove(Map<String, Object> map, String field) { if (map == null) return null; if (field.contains(".")) { String prefix = field.substring(0, field.lastIndexOf('.')); field = field.substring(field.lastIndexOf('.') + 1); Collection<Object> allNext = getAll(map, prefix); List<Object> previous = new ArrayList<>(); for (Object next : allNext) if (next instanceof Map) previous.add(((Map<?, ?>) next).remove(field)); return previous; } else if (map == this) return super.remove(field); else return map.remove(field); } @SuppressWarnings("unchecked") private Collection<Object> toArray(Object value) { if (value == null) return Collections.emptyList(); if (value instanceof Collection) return ((Collection<Object>) value); return Collections.singletonList(value); } @Override public Object put(String field, Object value) { Map<String, Object> map = this; if (field.contains(".")) { String prefix = field.substring(0, field.lastIndexOf('.')); field = field.substring(field.lastIndexOf('.') + 1); map = get(map, prefix, true, true); } if (map == this) return super.put(field, value); else return map.put(field, value); } public void nullify(String... fields) { if (fields == null) return; for (String field : fields) put(field, null); } public void removeEmptyOrNull() { removeEmptyOrNull(this); } @SuppressWarnings("unchecked") private void removeEmptyOrNull(Map<String, Object> map) { for (String key : new HashSet<>(map.keySet())) { Object value = map.get(key); if (map.get(key) == null) map.remove(key); else if (value instanceof String && ((String) value).isEmpty()) map.remove(key); else if (value instanceof Map) removeEmptyOrNull((Map<String, Object>) value); } } @Override public Object get(Object field) { if (field == null) return null; return get(this, field.toString(), false, true); } public <T> T get(String field) { if (field == null) return null; return get(this, field, false, true); } @SuppressWarnings("unchecked") public <T> List<T> getAll(String field, Class<T> clazz) { List<T> values = new ArrayList<>(); Collection<Object> all = getAll(this, field); for (Object value : all) { if (clazz == Long.class && value instanceof Integer) value = ((Integer) value).longValue(); values.add((T) value); } return values; } @SuppressWarnings("unchecked") private Collection<Object> getAll(Map<String, Object> map, String field) { Collection<Object> all = new ArrayList<>(); if (field.contains(".")) { String prefix = field.substring(0, field.lastIndexOf('.')); field = field.substring(field.lastIndexOf('.') + 1); Collection<Object> allNext = getAll(map, prefix); for (Object next : allNext) if (next instanceof Map) all.addAll(getAll((Map<String, Object>) next, field)); } else all = toArray(map.get(field)); return all; } // boolean initialCall is to distinguish between recursive call and initial // call, createMissing only applies to recursive calls @SuppressWarnings("unchecked") private <T> T get(Map<String, Object> map, String field, boolean createMissing, boolean initialCall) { if (field.contains(".")) { String prefix = field.substring(0, field.lastIndexOf('.')); field = field.substring(field.lastIndexOf('.') + 1); map = get(map, prefix, createMissing, false); } Object value = null; if (map != null) if (map == this) value = super.get(field); else value = map.get(field); if (value == null && createMissing && !initialCall) value = new HashMap<String, Object>(); return (T) value; } public int getArrayLength(String field) { Object array = get(this, field, false, true); if (array == null) array = 0; if (array instanceof Collection) return ((Collection<?>) array).size(); return 0; } public String getString(String field) { Object value = get(field); if (value == null) return null; if (value instanceof String[]) return ((String[]) value)[0]; return value.toString(); } public long getLong(String field) { Object value = get(field); if (value == null) return 0; try { if (value instanceof String[]) return new Double(Double.parseDouble(((String[]) value)[0])).longValue(); return new Double(Double.parseDouble(value.toString())).longValue(); } catch (NumberFormatException e) { return 0; } } public double getDouble(String field) { Object value = get(field); if (value == null) return 0; try { if (value instanceof String[]) return Double.parseDouble(((String[]) value)[0]); return Double.parseDouble(value.toString()); } catch (NumberFormatException e) { return 0; } } public boolean getBoolean(String field) { Object value = get(field); if (value == null) return false; String stringValue = null; if (value instanceof String[]) stringValue = ((String[]) value)[0].toLowerCase(); else stringValue = value.toString().toLowerCase(); switch (stringValue) { case "true": return true; case "on": return true; case "yes": return true; default: return false; } } }