diff options
Diffstat (limited to 'src/core/src/data/MVDataEntry.java')
-rw-r--r-- | src/core/src/data/MVDataEntry.java | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/core/src/data/MVDataEntry.java b/src/core/src/data/MVDataEntry.java new file mode 100644 index 0000000..f92a141 --- /dev/null +++ b/src/core/src/data/MVDataEntry.java @@ -0,0 +1,238 @@ +/* + * SSSync, a Simple and Stupid Synchronizer for data with multi-valued attributes + * Copyright (C) 2014 Ludovic Pouzenc <ludovic@pouzenc.fr> + * + * This file is part of SSSync. + * + * SSSync is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SSSync is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with SSSync. If not, see <http://www.gnu.org/licenses/> + */ + +package data; + +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; + +import com.google.common.collect.HashMultimap; + +/** + * Generic Multi-Valued data type. Each object store a particular entry. + * Semantics are like in LDAP directories : an entry = a key + a set of multi-valued attributes. + * Relational data like in RDMS are more constrained : columns are fixed for an entire table. + * Null and empty string attribute value are silently discarded. + * + * @author lpouzenc + */ +public class MVDataEntry implements Comparable<MVDataEntry> { + + /** + * The key part that identify this particular entry. + */ + private final String key; + /** + * The data part of this particular entry. + */ + private HashMultimap<String,String> attrValPairs; + + // XXX : add an HashMap for meta or constraints ? + + // Constructors + + /** + * Build a fresh empty MVDataEntry. + * @param key Unique key identifying this entry + */ + public MVDataEntry(String key) { + if ( key == null ) { + throw new IllegalArgumentException("key must be non-null"); + } + this.key = key; + this.attrValPairs = HashMultimap.create(); + } + + /** + * Build a fresh empty MVDataEntry with hints about expected attr/values count. + * @param key Unique key identifying this entry + */ + public MVDataEntry(String key, int expectedAttribs, int expectedValuesPerAttrib) { + if ( key == null ) { + throw new IllegalArgumentException("key must be non-null"); + } + this.key = key; + this.attrValPairs = HashMultimap.create(expectedAttribs, expectedValuesPerAttrib); + } + + /** + * Deep copy of an existing MVDataEntry. + * @param key Unique key identifying this entry + */ + public MVDataEntry(final MVDataEntry copyFrom) { + this.key=copyFrom.key; // String is immutable, so ref copy is okay + this.attrValPairs = HashMultimap.create(copyFrom.attrValPairs); + } + + /** + * Proxy function to return all attribute/value pairs. + * One can use read a MVDataEntry without depending on non-standard HashMultimap. + * @return + */ + public Set<Entry<String, String>> getAllEntries() { + return this.attrValPairs.entries(); + } + + /** + * Proxy function to add an attribute/value pair in attrValPairs. + * One can use MVDataEntry without depending on non-standard HashMultimap. + * + * @param attr + * @param value + */ + public void put(String attr, String... values) { + for (String value: values) { + if ( value != null && !value.isEmpty() ) { + this.attrValPairs.put(attr, value); + } + } + } + + /** + * Proxy function to get all values from a particular attribute. + * One can use MVDataEntry without depending on non-standard HashMultimap. + * @param attr + * @return + */ + public Set<String> getValues(String attr) { + return this.attrValPairs.get(attr); + } + + /** + * Helper function to insert multiple values from a single string. + * + * @param attr + * @param value + * @param splitRegex + */ + public void splitAndPut(String attr, String value, String splitRegex) { + if ( value != null ) { + for (String v : value.split(splitRegex)) { + put(attr, v); + } + } + } + + /** + * Helper function to return list of changed attributes. + * Note : this don't keep track of deleted attributes. + * @param original + * @return + */ + public Set<String> getChangedAttributes(MVDataEntry original) { + HashSet<String> result = new HashSet<String>(); + + for (String attr: this.attrValPairs.keySet()) { + Set<String> thisValue = this.attrValPairs.get(attr); + Set<String> originalValue = original.attrValPairs.get(attr); + if ( ! thisValue.equals(originalValue) ) { + result.add(attr); + } + } + + return result; + } + + /** + * Augment this entry with attr/values from other entries. + * @param appendMode Select behavior on an existing attribute : append values or replace them + * @param entries Entries to merge with current entry + */ + public void mergeValues(boolean appendMode, MVDataEntry... entries) { + for(MVDataEntry entry : entries) { + if ( ! appendMode ) { + for (String attr : entry.attrValPairs.keySet()) { + this.attrValPairs.removeAll(attr); + } + } + this.attrValPairs.putAll(entry.attrValPairs); + } + } + + /** + * Check if this entry seems contains useful data. + * @return true if this entry seems contains useful data + */ + public boolean isValid() { + boolean validKey=(this.key != null && this.key.length() > 0 ); + boolean validVal=(this.attrValPairs != null && ! this.attrValPairs.isEmpty()); + + return (validKey && validVal); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + // Check for self-comparison (compare object references) + if ( this == obj ) { return true; } + // Check non-nullity and type + if ( !( obj instanceof MVDataEntry) ) { return false; } + // Cast safely + MVDataEntry other = (MVDataEntry) obj; + // Check all fields (known to be always non null) + return ( this.key.equals(other.key) && this.attrValPairs.equals(other.attrValPairs) ); + } + + /** + * Compares entries. Ordering of entries is the ordering of their keys. + * (java.lang.String default ordering : lexicographical ascending order) + */ + @Override + public int compareTo(MVDataEntry other) { + return this.key.compareTo(other.key); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "{key=" + key + ", attrValPairs=" + attrValPairs.toString() + "}"; + } + + + // Boring accessors + /** + * @return the attrValPairs + */ + public HashMultimap<String, String> getAttrValPairs() { + return attrValPairs; + } + + /** + * @param attrValPairs the attrValPairs to set + */ + public void setAttrValPairs(HashMultimap<String, String> attrValPairs) { + this.attrValPairs = attrValPairs; + } + + /** + * @return the key (guaranteed to be non-null) + */ + public String getKey() { + return key; + } + + + +} |