/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.model;
import java.util.*;
/**
* Abstract base for map that tracks changes
*/
public abstract class AbstractChangeTrackingMap<K> extends HashMap<String, K> {
private HashMap<String, K> _remove;
private HashSet<String> _changed; // added or modified
/**
* Default constructor
*/
public AbstractChangeTrackingMap() {
}
/**
* Constructs a new map with the same mappings as source
*
* @param source
*/
public AbstractChangeTrackingMap(Map<String, K> source) {
super(source);
}
/**
* Get removed key set
*
* @return removed keys
*/
public Set<String> getRemovedKeySet() {
if (_remove == null) {
return null;
}
return Collections.unmodifiableSet(_remove.keySet());
}
/**
* Get modified keys set
*
* @return modified keys set
*/
public Set<String> getChangedKeySet() {
if (_changed == null) {
return null;
}
return Collections.unmodifiableSet(_changed);
}
/**
* 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() {
_changed = new HashSet<String>();
_changed.addAll(this.keySet());
}
/**
* Incrementally adds entry into the hash map
*
* @param key
* @param value
* @return Previous value for the key, if one exists
*/
@Override
public K put(String key, K value) {
if (_changed == null) {
_changed = new HashSet<String>();
}
if (_remove != null) {
_remove.remove(key);
}
_changed.add(key);
return super.put(key, value);
}
/**
* Adds entry into hashmap, not tracked as a change
*
* @param key
* @param value
*/
public void putNoTrack(String key, byte[] value) {
super.put(key, valFromByte(value));
}
/**
* Removes entry from map without tracking
*
* @param key
*/
public void removeNoTrack(String key) {
super.remove(key);
}
/**
* Incrementally add multiple key, value pairs into the Map
*
* @param add HashMap to add key, value pairs from
*/
@Override
public void putAll(Map<? extends String, ? extends K> add) {
for (Map.Entry<? extends String, ? extends K> entry : add.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
/**
* Incrementally remove a key from the map
*
* @param key
*/
public void remove(String key) {
// check if need to remove from changed
if (_changed != null && _changed.contains(key)) {
_changed.remove(key);
}
K old = super.remove(key);
if (_remove == null) {
_remove = new HashMap<String, K>();
}
_remove.put(key, old);
}
public K getRemovedValue(String key) {
return _remove.get(key);
}
/**
* Removes all entries in the map.
*/
@Override
public void clear() {
Set<String> keys = super.keySet();
if (keys != null && _changed != null) {
_changed.removeAll(keys);
}
if (_remove == null) {
_remove = new HashMap<String, K>();
}
_remove.putAll(this);
super.clear();
}
/**
* replace current entries with the ones passed in
*
* @param newEntries
*/
public void replace(Map<String, K> newEntries) {
if ((newEntries == null) || (newEntries.isEmpty())) {
clear();
return;
}
Set<String> keys = super.keySet();
List<String> removedKeys = new ArrayList();
for (String key : keys) {
if (!newEntries.containsKey(key)) {
removedKeys.add(key);
}
}
for (String key : removedKeys) {
remove(key);
}
Set<Map.Entry<String, K>> entries = newEntries.entrySet();
for (Map.Entry<String, K> entry : entries) {
put(entry.getKey(), entry.getValue());
}
}
/**
* Deserializes string value into K
*
* @param value
* @return
*/
public abstract K valFromByte(byte[] value);
/**
* Serializes K into string
*
* @param value
* @return
*/
public abstract byte[] valToByte(K value);
}