All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twelvemonkeys.util.AbstractDecoratedMap Maven / Gradle / Ivy

/*
 * 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 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 pContents) { if (pBacking == null) { throw new IllegalArgumentException("backing == null"); } Entry[] entries = null; if (pBacking == pContents) { // NOTE: Special treatment to avoid ClassCastExceptions Set> 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 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 implementaton. */ 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(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy