// GenericsNote: Converted.
/*
* Copyright 2001-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openanzo.rdf.utils;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.commons.collections15.MultiMap;
import org.apache.commons.collections15.iterators.EmptyIterator;
/**
* <code>MultiHashMap</code> is the default implementation of the {@link org.apache.commons.collections15.MultiMap MultiMap} interface.
* <p/>
* A <code>MultiMap</code> is like a Map, but with slightly different semantics. Putting a value into the map will add the value to a Collection at that key.
* Getting a value will return a Collection, holding all the values put to that key.
* <p/>
* This implementation uses an <code>ArrayList</code> as the collection. The internal storage list is made available without cloning via the
* <code>get(Object)</code> and <code>entrySet()</code> methods. The implementation returns <code>null</code> when there are no values mapped to a key.
* <p/>
* For example:
*
* <pre>
* Number key = Integer.valueOf(5);
* MultiMap<Number, String> mhm = new MultiHashMap<Number, String>();
* mhm.put(key, "A");
* mhm.put(key, "B");
* mhm.put(key, "C");
* Collection<String> coll = mhm.get(key);
* </pre>
* <p/>
* <code>list</code> will be a list containing "A", "B", "C".
*
* @param <K>
* key type
* @param <V>
* value type
*
* @author Christopher Berry
* @author James Strachan
* @author Steve Downey
* @author Stephen Colebourne
* @author Julien Buret
* @author Matt Hall, John Watkinson, Serhiy Yevtushenko
* @version $Revision: 1.2 $ $Date: 2006/06/08 15:19:55 $
* @since Commons Collections 2.0
*/
public class MultiTreeMap<K extends Comparable<?>, V extends Comparable<?>> implements MultiMap<K, V>, Serializable, Cloneable {
// backed values collection
private transient Collection<V> values = null;
// compatibility with commons-collection releases 2.0/2.1
private static final long serialVersionUID = 1943563828307035349L;
private Map<K, Collection<V>> internalMap;
/**
* Constructor.
*/
public MultiTreeMap() {
internalMap = new TreeMap<K, Collection<V>>();
}
/**
* Constructor that copies the input map creating an independent copy.
* <p/>
* The values are not cloned.
* <p/>
*
* @param mapToCopy
* a Map to copy
*/
public MultiTreeMap(Map<K, V> mapToCopy) {
// be careful of JDK 1.3 vs 1.4 differences
this();
putAll(mapToCopy);
}
/**
* Constructor that copies the input MultiMap creating an independent copy.
* <p/>
* Each internal collection is also cloned.
* <p/>
* NOTE: From Commons Collections 3.1 this method correctly copies a MultiMap to form a truly independent new map.
*
* @param mapToCopy
* a Map to copy
*/
public MultiTreeMap(MultiMap<K, V> mapToCopy) {
this();
for (Iterator<Map.Entry<K, Collection<V>>> it = mapToCopy.entrySet().iterator(); it.hasNext();) {
Map.Entry<K, Collection<V>> entry = it.next();
Collection<V> coll = entry.getValue();
Collection<V> newColl = createCollection(coll);
internalMap.put(entry.getKey(), newColl);
}
}
/**
* Read the object during deserialization.
*/
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
// This method is needed because the 1.2/1.3 Java deserialisation called
// put and thus messed up that method
// default read object
s.defaultReadObject();
// problem only with jvm <1.4
String version = "1.2";
try {
version = System.getProperty("java.version");
} catch (SecurityException ex) {
// ignore and treat as 1.2/1.3
}
if (version.startsWith("1.2") || version.startsWith("1.3")) {
for (Iterator<Map.Entry<K, Collection<V>>> iterator = entrySet().iterator(); iterator.hasNext();) {
Map.Entry<K, Collection<V>> entry = iterator.next();
// put has created a extra collection level, remove it
internalMap.put(entry.getKey(), entry.getValue());
}
}
}
//-----------------------------------------------------------------------
/**
* Gets the total size of the map by counting all the values.
*
* @return the total size of the map counting all values
* @since Commons Collections 3.1
*/
public int totalSize() {
int total = 0;
Collection<Collection<V>> values = internalMap.values();
for (Iterator<Collection<V>> it = values.iterator(); it.hasNext();) {
Collection<V> coll = it.next();
total += coll.size();
}
return total;
}
/**
* Gets the collection mapped to the specified key. This method is a convenience method to typecast the result of <code>get(key)</code>.
*
* @param key
* the key to retrieve
* @return the collection mapped to the key, null if no mapping
* @since Commons Collections 3.1
*/
public Collection<V> getCollection(Object key) {
return internalMap.get(key);
}
/**
* Gets the size of the collection mapped to the specified key.
*
* @param key
* the key to get size for
* @return the size of the collection at the key, zero if key not in map
* @since Commons Collections 3.1
*/
public int size(Object key) {
Collection<V> coll = getCollection(key);
if (coll == null) {
return 0;
}
return coll.size();
}
/**
* Gets an iterator for the collection mapped to the specified key.
*
* @param key
* the key to get an iterator for
* @return the iterator of the collection at the key, empty iterator if key not in map
* @since Commons Collections 3.1
*/
@SuppressWarnings("unchecked")
public Iterator<V> iterator(Object key) {
Collection<V> coll = getCollection(key);
if (coll == null) {
return EmptyIterator.INSTANCE;
}
return coll.iterator();
}
/**
* Adds the value to the collection associated with the specified key.
* <p/>
* Unlike a normal <code>Map</code> the previous value is not replaced. Instead the new value is added to the collection stored against the key.
*
* @param key
* the key to store against
* @param value
* the value to add to the collection at the key
* @return the value added if the map changed and null if the map did not change
*/
public V put(K key, V value) {
// NOTE:: put is called during deserialization in JDK < 1.4 !!!!!!
// so we must have a readObject()
Collection<V> coll = getCollection(key);
if (coll == null) {
coll = createCollection(null);
internalMap.put(key, coll);
}
boolean results = coll.add(value);
return results ? value : null;
}
/**
* Adds a collection of values to the collection associated with the specified key.
*
* @param key
* the key to store against
* @param values
* the values to add to the collection at the key, null ignored
* @return true if this map changed
* @since Commons Collections 3.1
*/
public boolean putAll(K key, Collection<? extends V> values) {
if (values == null || values.size() == 0) {
return false;
}
Collection<V> coll = getCollection(key);
if (coll == null) {
coll = createCollection(values);
if (coll.size() == 0) {
return false;
}
internalMap.put(key, coll);
return true;
} else {
return coll.addAll(values);
}
}
/**
* Checks whether the map contains the value specified.
* <p/>
* This checks all collections15 against all keys for the value, and thus could be slow.
*
* @param value
* the value to search for
* @return true if the map contains the value
*/
public boolean containsValue(Object value) {
Set<Map.Entry<K, Collection<V>>> pairs = internalMap.entrySet();
if (pairs == null) {
return false;
}
Iterator<Map.Entry<K, Collection<V>>> pairsIterator = pairs.iterator();
while (pairsIterator.hasNext()) {
Map.Entry<K, Collection<V>> keyValuePair = pairsIterator.next();
Collection<V> coll = keyValuePair.getValue();
if (coll.contains(value)) {
return true;
}
}
return false;
}
/**
* Checks whether the collection at the specified key contains the value.
*
* @param value
* the value to search for
* @return true if the map contains the value
* @since Commons Collections 3.1
*/
public boolean containsValue(Object key, Object value) {
Collection<V> coll = getCollection(key);
if (coll == null) {
return false;
}
return coll.contains(value);
}
/**
* Removes a specific value from map.
* <p/>
* The item is removed from the collection mapped to the specified key. Other values attached to that key are unaffected.
* <p/>
* If the last value for a key is removed, <code>null</code> will be returned from a subsequant <code>get(key)</code>.
*
* @param key
* the key to remove from
* @param item
* the value to remove
* @return the value removed (which was passed in), null if nothing removed
*/
@SuppressWarnings("unchecked")
public V remove(Object key, Object item) {
Collection<V> valuesForKey = getCollection(key);
if (valuesForKey == null) {
return null;
}
boolean valueRemoved = valuesForKey.remove(item);
// remove the list if it is now empty
// (saves space, and allows equals to work)
if (valuesForKey.isEmpty()) {
remove(key);
}
return (valueRemoved) ? (V) item : null;
}
/**
* Clear the map.
* <p/>
* This clears each collection in the map, and so may be slow.
*/
public void clear() {
// For gc, clear each list in the map
Set<Map.Entry<K, Collection<V>>> pairs = internalMap.entrySet();
Iterator<Map.Entry<K, Collection<V>>> pairsIterator = pairs.iterator();
while (pairsIterator.hasNext()) {
Map.Entry<K, Collection<V>> keyValuePair = pairsIterator.next();
Collection<V> coll = keyValuePair.getValue();
coll.clear();
}
internalMap.clear();
}
public int size() {
return internalMap.size();
}
public Collection<V> get(Object key) {
try {
return internalMap.get(key);
} catch (NullPointerException npe) {
return null;
}
}
public Collection<V> remove(Object key) {
try {
return internalMap.remove(key);
} catch (NullPointerException npe) {
return null;
}
}
public boolean isEmpty() {
return internalMap.isEmpty();
}
public boolean containsKey(Object key) {
return internalMap.containsKey(key);
}
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
public void putAll(MultiMap<? extends K, ? extends V> map) {
for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry<? extends K, Collection<? extends V>> entry = (Map.Entry<? extends K, Collection<? extends V>>) it.next();
for (V v : entry.getValue()) {
put(entry.getKey(), v);
}
}
}
public Set<K> keySet() {
return internalMap.keySet();
}
public Set<Map.Entry<K, Collection<V>>> entrySet() {
return internalMap.entrySet();
}
public Map<K, Collection<V>> map() {
return internalMap;
}
/**
* Gets a collection containing all the values in the map.
* <p/>
* This returns a collection containing the combination of values from all keys.
*
* @return a collection view of the values contained in this map
*/
public Collection<V> values() {
Collection<V> vs = values;
return vs != null ? vs : (values = new Values<V>());
}
//-----------------------------------------------------------------------
/**
* Inner class to view the elements.
*/
private class Values<T> extends AbstractCollection<V> {
@Override
public Iterator<V> iterator() {
return new ValueIterator<V>();
}
@Override
public int size() {
int compt = 0;
Iterator<V> it = iterator();
while (it.hasNext()) {
it.next();
compt++;
}
return compt;
}
@Override
public void clear() {
MultiTreeMap.this.clear();
}
}
/**
* Inner iterator to view the elements.
*/
private class ValueIterator<T> implements Iterator<V> {
private Iterator<Collection<V>> backedIterator;
private Iterator<V> tempIterator;
private ValueIterator() {
backedIterator = internalMap.values().iterator();
}
private boolean searchNextIterator() {
while (tempIterator == null || tempIterator.hasNext() == false) {
if (backedIterator.hasNext() == false) {
return false;
}
tempIterator = backedIterator.next().iterator();
}
return true;
}
public boolean hasNext() {
return searchNextIterator();
}
public V next() {
if (searchNextIterator() == false) {
throw new NoSuchElementException();
}
return tempIterator.next();
}
public void remove() {
if (tempIterator == null) {
throw new IllegalStateException();
}
tempIterator.remove();
}
}
//-----------------------------------------------------------------------
/**
* Clones the map creating an independent copy.
* <p/>
* The clone will shallow clone the collections15 as well as the map.
*
* @return the cloned map
*/
@Override
public Object clone() {
MultiTreeMap<K, V> cloned = new MultiTreeMap<K, V>();
for (Iterator<Map.Entry<K, Collection<V>>> it = internalMap.entrySet().iterator(); it.hasNext();) {
Map.Entry<K, Collection<V>> entry = it.next();
for (V v : entry.getValue()) {
cloned.put(entry.getKey(), v);
}
}
return cloned;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if (obj instanceof MultiTreeMap) {
return internalMap.equals(((MultiTreeMap) obj).map());
} else
return false;
}
@Override
public int hashCode() {
return internalMap.hashCode();
}
/**
* Creates a new instance of the map value Collection container.
* <p/>
* This method can be overridden to use your own collection type.
*
* @param coll
* the collection to copy, may be null
* @return the new collection
*/
protected Collection<V> createCollection(Collection<? extends V> coll) {
if (coll == null) {
return new TreeSet<V>();
} else {
return new TreeSet<V>(coll);
}
}
@Override
public String toString() {
return internalMap.toString();
}
}