001: /* *****************************************************************************
002: * MultiMap.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.utils;
011:
012: import java.util.Collection;
013: import java.util.Hashtable;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.Map;
017: import java.util.Set;
018:
019: /** Partial implementation of a map that stores multiple values for a single
020: * key. */
021: public class MultiMap implements Map {
022: /** Table to store information; each node may hold multiple values */
023: Hashtable mTable = new Hashtable();
024:
025: /** Returns a Set view of the keys contained in this Hashtable. The Set is
026: * backed by the Hashtable, so changes to the Hashtable are reflected in the
027: * Set, and vice-versa. The Set supports element removal (which removes the
028: * corresponding entry from the Hashtable), but not element addition. */
029: public Set keySet() {
030: return mTable.keySet();
031: }
032:
033: /** Returns the set of values to which the specified key is mapped in this
034: * multimap.
035: * @param key key in the map
036: * @return set of values to which the specified key is mapped; null if none
037: * is found */
038: public Object get(Object key) {
039: return mTable.get(key);
040: }
041:
042: /** Maps the specified key to the specified value in this map. The key
043: * and value may not be null.
044: * @param key the map key
045: * @param val the value
046: * @return the set that val was added to */
047: public Object put(Object key, Object val) {
048: Set valSet = (Set) mTable.get(key);
049: if (valSet == null) {
050: valSet = new HashSet();
051: mTable.put(key, valSet);
052: }
053: valSet.add(val);
054: return valSet;
055: }
056:
057: /** Remove a particular value associated with key.
058: * @param key the key to check for associated value
059: * @param val the actual value to remove
060: * @return the value to which the key had be mapped in this mulimap or
061: * null, if the key did not have a mapping */
062: public Object remove(Object key, Object val) {
063: Object ret = null;
064: Set valSet = (Set) mTable.get(key);
065: if (valSet != null) {
066: if (valSet.remove(val))
067: ret = val;
068: if (valSet.isEmpty())
069: mTable.remove(key);
070: }
071: return ret;
072: }
073:
074: /** Remove all values associated with key.
075: * @param key the key to remove
076: * @return the set of values removed that were associated with key */
077: public Object remove(Object key) {
078: return mTable.remove(key);
079: }
080:
081: /** Returns a Collection view of the values contained in this Hashtable. The
082: * Collection is backed by the Hashtable, so changes to the Hashtable are
083: * reflected in the Collection, and vice-versa. The Collection supports
084: * element removal (which removes the corresponding entry from the
085: * Hashtable), but not element addition.
086: * @return a collection view of the values contained in this map */
087: public Collection values() {
088: return mTable.values();
089: }
090:
091: /** Returns the number of key-set mappings in this map. If the map contains
092: * more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE.
093: * @return the number of key-value mappings in this map */
094: public int size() {
095: return mTable.size();
096: }
097:
098: /** Returns <code>true</code> if this map contains no key-value mappings.
099: * @return <code>true</code> if this map contains no key-value mappings */
100: public boolean isEmpty() {
101: return mTable.isEmpty();
102: }
103:
104: /** Returns true if this map contains a mapping for the specified key.
105: * @param key key whose presence in this map is to be tested
106: * @return true if this map contains a mapping for the specified key.
107: */
108: public boolean containsKey(Object key) {
109: return mTable.containsKey(key);
110: }
111:
112: /** Returns <code>true</code> if this map maps one or more keys to the
113: * specified value. More formally, returns true if and only if this map
114: * contains at least one mapping to a value v such that <code>(value==null ?
115: * v==null : value.equals(v))</code>. This operation will probably require
116: * time linear in the map size for most implementations of the Map
117: * interface.
118: * @param value value whose presence in this map is to be tested
119: * @return <code>true</code> if this map maps one or more keys to the
120: * specified value */
121: public boolean containsValue(Object value) {
122: Collection c = mTable.values();
123: Iterator iter = c.iterator();
124: while (iter.hasNext()) {
125: Set set = (Set) iter.next();
126: if (set.contains(value))
127: return true;
128: }
129: return false;
130: }
131:
132: /** Remove value from map and return keys this value was associated with.
133: * @param value value to remove
134: * @return set of keys associated with removed value; null, if none was
135: * found */
136: public Set removeValue(Object value) {
137: Set keySet = new HashSet();
138: Iterator iter = ((Set) mTable.entrySet()).iterator();
139: while (iter.hasNext()) {
140: Map.Entry entry = (Map.Entry) iter.next();
141: Set set = (Set) entry.getValue();
142: if (set.contains(value)) {
143: keySet.add(entry.getKey());
144: set.remove(value);
145: // if set is empty, remove the key-value entry in table
146: if (set.isEmpty())
147: iter.remove();
148: }
149: }
150: return (!keySet.isEmpty() ? keySet : null);
151: }
152:
153: /** Unsupported. */
154: public void putAll(Map m) {
155: throw new UnsupportedOperationException();
156: }
157:
158: /** Unsupported. */
159: public void clear() {
160: throw new UnsupportedOperationException();
161: }
162:
163: /** Returns a set view of the mappings contained in this map. Each element
164: * in the returned set is a Map.Entry. The set is backed by the map, so
165: * changes to the map are reflected in the set, and vice-versa. If the map
166: * is modified while an iteration over the set is in progress, the results
167: * of the iteration are undefined. The set supports element removal, which
168: * removes the corresponding mapping from the map, via the Iterator.remove,
169: * Set.remove, removeAll, retainAll and clear operations. It does not
170: * support the add or addAll operations.
171: * @return a set view of the mappings contained in this map */
172: public Set entrySet() {
173: return mTable.entrySet();
174: }
175:
176: }
|