package com.brightgenerous.orm;
import static com.brightgenerous.orm.ICloneable.Utils.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.brightgenerous.commons.EqualsUtils;
import com.brightgenerous.commons.HashCodeUtils;
import com.brightgenerous.commons.ToStringUtils;
import com.brightgenerous.lang.Args;
abstract class AbstractUpdatedCallbackableMap<K extends Serializable, V extends EmptyChackable & Serializable>
implements Map<K, V>, Clearable, ICloneable<AbstractUpdatedCallbackableMap<K, V>>,
Serializable {
private static final long serialVersionUID = -74948305336935190L;
private final UpdatedCallback callback;
private Map<K, V> map = createMap();
protected AbstractUpdatedCallbackableMap(UpdatedCallback callback) {
this.callback = callback;
}
private Map<K, V> createMap() {
return new HashMap<>();
}
protected abstract ConditionContext getContext();
protected abstract K convertKey(Object key);
protected abstract V createValue(UpdatedCallback uc);
protected UpdatedCallback getCallback() {
return callback;
}
@Override
public void clear() {
clean();
if (!map.isEmpty()) {
map.clear();
callbackUpdated();
}
}
protected void clean() {
Set<K> keys = new HashSet<>();
for (Entry<K, V> entry : map.entrySet()) {
V value = entry.getValue();
if ((value == null) || value.isEmpty()) {
keys.add(entry.getKey());
}
}
for (K key : keys) {
remove(key);
}
}
@Override
public boolean containsKey(Object key) {
V ret = map.get(convertKey(key));
return (ret != null) && !ret.isEmpty();
}
@Override
public boolean containsValue(Object value) {
clean();
return map.containsValue(value);
}
@Override
public Set<Entry<K, V>> entrySet() {
clean();
return Collections.unmodifiableSet(map.entrySet());
}
@Override
public V get(Object key) {
K k = convertKey(key);
if (k == null) {
throw new IllegalStateException(String.format(
"The converted key must not be null. converted key => %s, key => %s", k, key));
}
V ret = map.get(k);
if (ret == null) {
ret = createValue(callback);
map.put(k, ret);
}
return ret;
}
@Override
public boolean isEmpty() {
clean();
return map.isEmpty();
}
@Override
public Set<K> keySet() {
clean();
return Collections.unmodifiableSet(map.keySet());
}
@Override
public V put(K key, V value) {
Args.notNull(key, "key");
Args.notNull(value, "value");
V ret = null;
if (map.containsKey(key)) {
ret = map.get(key);
if (ret != value) {
map.put(key, value);
callbackUpdated();
}
} else {
map.put(key, value);
callbackUpdated();
}
if ((ret != null) && ret.isEmpty()) {
ret = null;
}
return ret;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@Override
public V remove(Object key) {
K k = convertKey(key);
V ret = null;
if (k != null) {
if (map.containsKey(k)) {
ret = map.remove(k);
if ((ret != null) && !ret.isEmpty()) {
callbackUpdated();
}
}
}
if ((ret != null) && ret.isEmpty()) {
key = null;
}
return ret;
}
@Override
public int size() {
clean();
return map.size();
}
@Override
public Collection<V> values() {
clean();
return Collections.unmodifiableCollection(map.values());
}
private void callbackUpdated() {
if (callback != null) {
callback.updated();
}
}
@Override
public AbstractUpdatedCallbackableMap<K, V> clone() {
clean();
AbstractUpdatedCallbackableMap<K, V> ret;
try {
ret = (AbstractUpdatedCallbackableMap<K, V>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e.getMessage());
}
ret.map = createMap();
for (Entry<K, V> entry : map.entrySet()) {
ret.map.put(getIfClone(entry.getKey()), getIfClone(entry.getValue()));
}
return ret;
}
@Override
public int hashCode() {
if (HashCodeUtils.resolved()) {
return HashCodeUtils.hashCodeAlt(null, this);
}
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (EqualsUtils.resolved()) {
return EqualsUtils.equalsAlt(null, this, obj);
}
return super.equals(obj);
}
@Override
public String toString() {
if (ToStringUtils.resolved()) {
return ToStringUtils.toStringAlt(this);
}
return super.toString();
}
}