com.twelvemonkeys.util.AbstractDecoratedMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common-lang Show documentation
Show all versions of common-lang Show documentation
TwelveMonkeys Common language support classes.
The newest version!
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* AbstractDecoratedMap
*
* @author Harald Kuhr
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $
*/
// TODO: The generics in this class looks suspicious..
abstract class AbstractDecoratedMap extends AbstractMap implements Map, Serializable, Cloneable {
protected Map> entries;
protected transient volatile int modCount;
private transient volatile Set> entrySet = null;
private transient volatile Set keySet = null;
private transient volatile Collection values = null;
/**
* Creates a {@code Map} backed by a {@code HashMap}.
*/
public AbstractDecoratedMap() {
this(new HashMap>(), null);
}
/**
* Creates a {@code Map} backed by a {@code HashMap}, containing all
* key/value mappings from the given {@code Map}.
*
* This is constructor is here to comply with the reccomendations for
* "standard" constructors in the {@code Map} interface.
*
*
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public AbstractDecoratedMap(Map extends K, ? extends V> pContents) {
this(new HashMap>(), pContents);
}
/**
* Creates a {@code Map} backed by the given backing-{@code Map},
* containing all key/value mappings from the given contents-{@code Map}.
*
* NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created.
*
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*
* @throws IllegalArgumentException if {@code pBacking} is {@code null}
* or if {@code pBacking} differs from {@code pContent} and is not empty.
*/
public AbstractDecoratedMap(Map> pBacking, Map extends K, ? extends V> pContents) {
if (pBacking == null) {
throw new IllegalArgumentException("backing == null");
}
Entry extends K, ? extends V>[] entries = null;
if (pBacking == pContents) {
// NOTE: Special treatment to avoid ClassCastExceptions
Set extends Entry extends K, ? extends V>> es = pContents.entrySet();
//noinspection unchecked
entries = new Entry[es.size()];
entries = es.toArray(entries);
pContents = null;
pBacking.clear();
}
else if (!pBacking.isEmpty()) {
throw new IllegalArgumentException("backing must be empty");
}
this.entries = pBacking;
init();
if (pContents != null) {
putAll(pContents);
}
else if (entries != null) {
// Reinsert entries, this time wrapped
for (Entry extends K, ? extends V> entry : entries) {
put(entry.getKey(), entry.getValue());
}
}
}
/**
* Default implementation, does nothing.
*/
protected void init() {
}
public int size() {
return entries.size();
}
public void clear() {
entries.clear();
modCount++;
init();
}
public boolean isEmpty() {
return entries.isEmpty();
}
public boolean containsKey(Object pKey) {
return entries.containsKey(pKey);
}
/**
* Returns {@code true} if this map maps one or more keys to the
* specified pValue. More formally, returns {@code true} if and only if
* this map contains at least one mapping to a pValue {@code v} such that
* {@code (pValue==null ? v==null : pValue.equals(v))}.
*
* This implementation requires time linear in the map size for this
* operation.
*
*
* @param pValue pValue whose presence in this map is to be tested.
* @return {@code true} if this map maps one or more keys to the
* specified pValue.
*/
public boolean containsValue(Object pValue) {
for (V value : values()) {
if (value == pValue || (value != null && value.equals(pValue))) {
return true;
}
}
return false;
}
public Collection values() {
Collection values = this.values;
return values != null ? values : (this.values = new Values());
}
public Set> entrySet() {
Set> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
public Set keySet() {
Set ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
}
/**
* Returns a shallow copy of this {@code AbstractMap} instance: the keys
* and values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
protected Object clone() throws CloneNotSupportedException {
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
map.values = null;
map.entrySet = null;
map.keySet = null;
// TODO: Implement: Need to clone the backing map...
return map;
}
// Subclass overrides these to alter behavior of views' iterator() method
protected abstract Iterator newKeyIterator();
protected abstract Iterator newValueIterator();
protected abstract Iterator> newEntryIterator();
// TODO: Implement these (get/put/remove)?
public abstract V get(Object pKey);
public abstract V remove(Object pKey);
public abstract V put(K pKey, V pValue);
/*protected*/ Entry createEntry(K pKey, V pValue) {
return new BasicEntry(pKey, pValue);
}
/*protected*/ Entry getEntry(K pKey) {
return entries.get(pKey);
}
/**
* Removes the given entry from the Map.
*
* @param pEntry the entry to be removed
*
* @return the removed entry, or {@code null} if nothing was removed.
*/
protected Entry removeEntry(Entry pEntry) {
if (pEntry == null) {
return null;
}
// Find candidate entry for this key
Entry candidate = getEntry(pEntry.getKey());
if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
// Remove
remove(pEntry.getKey());
return pEntry;
}
return null;
}
protected class Values extends AbstractCollection {
public Iterator iterator() {
return newValueIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class EntrySet extends AbstractSet> {
public Iterator> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Entry))
return false;
Entry e = (Entry) o;
//noinspection SuspiciousMethodCalls
Entry candidate = entries.get(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
if (!(o instanceof Entry)) {
return false;
}
/*
// NOTE: Extra cautions is taken, to only remove the entry if it
// equals the entry in the map
Object key = ((Entry) o).getKey();
Entry entry = (Entry) entries.get(key);
// Same entry?
if (entry != null && entry.equals(o)) {
return AbstractWrappedMap.this.remove(key) != null;
}
return false;
*/
//noinspection unchecked
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class KeySet extends AbstractSet {
public Iterator iterator() {
return newKeyIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return AbstractDecoratedMap.this.remove(o) != null;
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
/**
* A simple Map.Entry implementation.
*/
static class BasicEntry implements Entry, Serializable {
K mKey;
V mValue;
BasicEntry(K pKey, V pValue) {
mKey = pKey;
mValue = pValue;
}
/**
* Default implementation does nothing.
*
* @param pMap the map that is accessed
*/
protected void recordAccess(Map pMap) {
}
/**
* Default implementation does nothing.
* @param pMap the map that is removed from
*/
protected void recordRemoval(Map pMap) {
}
public V getValue() {
return mValue;
}
public V setValue(V pValue) {
V oldValue = mValue;
mValue = pValue;
return oldValue;
}
public K getKey() {
return mKey;
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) pOther;
Object k1 = mKey;
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = mValue;
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
public int hashCode() {
return (mKey == null ? 0 : mKey.hashCode()) ^
(mValue == null ? 0 : mValue.hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
}
}