/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.model;
import java.util.*;
import com.emc.storageos.db.client.util.DbClientCallbackEvent;
/**
* Abstract base for set that tracks changes
*/
public abstract class AbstractChangeTrackingSet<K> extends HashSet<K> {
private HashSet<K> _remove;
private HashSet<K> _added;
DbClientCallbackEvent _cb;
/**
* Default constructor
*/
public AbstractChangeTrackingSet() {
}
/**
* Constructs a new set with the same set as source
*
* @param source
*/
public AbstractChangeTrackingSet(Collection<K> source) {
super(source);
}
public void setCallback(DbClientCallbackEvent cb) {
_cb = cb;
}
public DbClientCallbackEvent getCallback() {
return _cb;
}
/**
* Get removed value set
*
* @return
*/
public Set<K> getRemovedSet() {
if (_remove == null) {
return null;
}
return Collections.unmodifiableSet(_remove);
}
/**
* Get modified value set
*
* @return
*/
public Set<K> getAddedSet() {
if (_added == null) {
return null;
}
return Collections.unmodifiableSet(_added);
}
/**
* Marks all entries as added new, for a complete db field value overwrite used from inside migration engine, not
* recommended for use otherwise
*/
public void markAllForOverwrite() {
_added = new HashSet<K>();
_added.addAll(this);
}
/**
* To incrementally add value to set, call this and follow up with DbClient.persist
*
* @param val
* String to be added, if doesn't already exists
* @return true if value is not already there, hence added now
*/
@Override
public boolean add(K val) {
if (contains(val)) {
return false;
}
if (_added == null) {
_added = new HashSet<K>();
}
_added.add(val);
if (_remove != null) {
_remove.remove(val);
}
super.add(val);
if (_cb != null) {
_cb.call();
}
return true;
}
/**
* Adds entry into set, not tracked as a change
*
* @param val
* value to add into the set
*/
public void addNoTrack(String val) {
super.add(valFromString(val));
}
/**
* Removes entry from set without tracking
*
* @param val
*/
public void removeNoTrack(String val) {
super.remove(valFromString(val));
}
/**
* Add set of values, calls add for each element in the input set
*
* @param add
* Set of strings to be added to the Set
*/
public void addAll(HashSet<K> add) {
Iterator<K> it = add.iterator();
while (it.hasNext()) {
add(it.next());
}
if (_cb != null) {
_cb.call();
}
}
/**
* To incrementally remove a value from set, call this and follow up with DbClient.persist
*
* @param val
* String to be removed, if exists
*/
@Override
@SuppressWarnings("unchecked")
public boolean remove(Object val) {
// check if removing from newly added
if (_added != null) {
_added.remove(val);
}
boolean ret = super.remove(val);
if (_remove == null) {
_remove = new HashSet<K>();
}
_remove.add((K) val);
if (_cb != null) {
_cb.call();
}
return ret;
}
/**
* Remove set of values, calls remove for each element in the input set.
*
* @param remove
* Set of strings to be removed from the Set
*
* @return true if an the set was changed, false otherwise.
*/
public boolean removeAll(HashSet<K> remove) {
boolean ret = false;
Iterator<K> it = remove.iterator();
while (it.hasNext()) {
boolean removed = remove(it.next());
if (!ret) {
ret = removed;
}
}
if (_cb != null) {
_cb.call();
}
return ret;
}
/**
* Removes all entries in the set.
*/
@Override
public void clear() {
if (_added != null) {
_added.removeAll(this);
}
if (_remove == null) {
_remove = new HashSet<K>();
}
_remove.addAll(this);
super.clear();
if (_cb != null) {
_cb.call();
}
}
/**
* replace current entries with the ones passed in
*
* @param newEntries
*/
public void replace(Set<K> newEntries) {
if (newEntries == null || (newEntries.isEmpty())) {
clear();
return;
}
HashSet<K> toRemove = new HashSet();
for (K entry : this) {
if (!newEntries.contains(entry)) {
toRemove.add(entry);
}
}
removeAll(toRemove);
for (K entry : newEntries) {
add(entry);
}
if (_cb != null) {
_cb.call();
}
}
/**
* Deserializes string value into K
*
* @param value
* @return
*/
public abstract K valFromString(String value);
/**
* Serializes K into string
*
* @param value
* @return
*/
public abstract String valToString(K value);
}