/* * Copyright 2015 by Thomas Lottermann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package notaql.datamodel; import notaql.evaluation.values.UnresolvedValue; import java.util.*; import java.util.stream.Collectors; /** * This simply represents a BSON-like object * * @author Thomas Lottermann */ public class ObjectValue implements ComplexValue<String> { private static final long serialVersionUID = 7479453524811023147L; private ComplexValue<?> parent; private LinkedHashMap<Step<String>, Value> map = new LinkedHashMap<>(); public ObjectValue() { this(null); } public ObjectValue(ComplexValue<?> parent) { this.parent = parent; } /** * Adds the value to the object and sets the parent as this * * @param key * @param value * @return */ public Value put(Step<String> key, Value value) { value.setParent(this); return map.put(key, value); } /** * Adds the values in the complex value (e.g. ObjectValue) to the object and sets the parent as this * * @param v * @return */ public void putAll(ComplexValue<String> v) { final Map<Step<String>, Value> m = v.toMap(); m.values().stream().forEach(a -> a.setParent(this)); map.putAll(m); } /** * Adds the values in the map to the object and sets the parent as this * * @param m * @return */ public void putAll(Map<Step<String>, Value> m) { m.values().stream().forEach(v -> v.setParent(this)); map.putAll(m); } /** * Deletes a value from the object * * Note: this leaves references to this object untouched * * @param key * @return */ public Value removeKey(Step<String> key) { return map.remove(key); } /** * Denotes if this object contains the provided key * @param key * @return */ public boolean containsKey(Step<String> key) { return map.containsKey(key); } /** * Provides all keys * @return */ public Set<Step<String>> keySet() { return map.keySet(); } @Override public Value get(Step<String> key) { return map.get(key); } @Override public Map<Step<String>, Value> toMap() { return map; } @Override public int size() { return map.size(); } @Override public SplitValue<Integer> split(Step<String> key, List<Value> splits) { final Value value = map.get(key); if(!(value instanceof AtomValue)) throw new IllegalArgumentException("The selected value is no atom."); final SplitValue<Integer> splitted = new SplitAtomValue<>((AtomValue<?>)value, splits); put(key, splitted); return splitted; } @Override public Step<String> getStep(Value child) { final Optional<Map.Entry<Step<String>, Value>> entry = map.entrySet().stream().filter(e -> e.getValue() == child).findAny(); if(entry.isPresent()) return entry.get().getKey(); return null; } @Override public ComplexValue<?> getParent() { return parent; } @Override public void setParent(ComplexValue<?> parent) { this.parent = parent; } @Override public boolean isRoot() { return parent == null; } @Override public Value deepCopy() { final ObjectValue copy = new ObjectValue(); this.toMap().entrySet().stream().forEach(e -> copy.put(e.getKey(), e.getValue().deepCopy())); return copy; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("{\n"); builder.append( map.entrySet().stream() .map(e -> e.getKey() + ": " + (e.getValue() != null ? e.getValue() : "NULL")) .collect(Collectors.joining(",\n")) ); builder.append("\n}"); return builder.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ObjectValue that = (ObjectValue) o; if (!reduceMap(map).equals(reduceMap(that.map))) return false; return true; } @Override public int hashCode() { return map != null ? reduceMap(map).hashCode() : 0; } private Map<Step<String>, Value> reduceMap(Map<Step<String>, Value> map) { return map.entrySet().stream() .filter(e -> !(e.getValue() instanceof UnresolvedValue)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } /** * Since the entry hashes are ordered it should ignore order * @return */ @Override public String groupKey() { final Map<Step<String>, Value> reduceMap = reduceMap(map); final String mapHash = reduceMap.entrySet().stream() .map(e -> GroupHelper.digest(e.getKey().getStep() + e.getValue().groupKey())) .sorted() .reduce("", (a, b) -> GroupHelper.digest(a + b)); return GroupHelper.digest(getClass().getCanonicalName() + ":" + mapHash); } }