summaryrefslogtreecommitdiff
path: root/src/core/src/data/MVDataEntry.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/src/data/MVDataEntry.java')
-rw-r--r--src/core/src/data/MVDataEntry.java238
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;
+ }
+
+
+
+}