001: /**********************************************************************
002: Copyright (c) 2003 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: ...
018: **********************************************************************/package org.jpox.util;
019:
020: import java.util.AbstractCollection;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.NoSuchElementException;
028: import java.util.Set;
029:
030: /**
031: * An implementation of a <code>MultiMap</code>, which is basically a Map
032: * with multiple values for a key. This will be removed when SUN see sense and
033: * include it in the JDK java.util package as standard.
034: *
035: * @version $Revision: 1.6 $
036: */
037: public class MultiMap extends HashMap {
038: private transient Collection values = null;
039:
040: /**
041: * Constructor.
042: */
043: public MultiMap() {
044: super ();
045: }
046:
047: /**
048: * Constructor.
049: *
050: * @param initialCapacity the initial capacity
051: */
052: public MultiMap(int initialCapacity) {
053: super (initialCapacity);
054: }
055:
056: /**
057: * Constructor.
058: * @param initialCapacity initial capacity
059: * @param loadFactor load factor for the Map.
060: */
061: public MultiMap(int initialCapacity, float loadFactor) {
062: super (initialCapacity, loadFactor);
063: }
064:
065: /**
066: * Constructor.
067: * @param map The initial Map.
068: */
069: public MultiMap(MultiMap map) {
070: super ();
071: if (map != null) {
072: Iterator it = map.entrySet().iterator();
073: while (it.hasNext()) {
074: Map.Entry entry = (Map.Entry) it.next();
075: super .put(entry.getKey(), new ArrayList((List) entry
076: .getValue()));
077: }
078: }
079: }
080:
081: /**
082: * Check if the map contains the passed value.
083: *
084: * @param value the value to search for
085: * @return true if the list contains the value
086: */
087: public boolean containsValue(Object value) {
088: Set pairs = super .entrySet();
089:
090: if (pairs == null) {
091: return false;
092: }
093: Iterator pairsIterator = pairs.iterator();
094: while (pairsIterator.hasNext()) {
095: Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
096: Collection coll = (Collection) keyValuePair.getValue();
097: if (coll.contains(value)) {
098: return true;
099: }
100: }
101: return false;
102: }
103:
104: /**
105: * Add a key, and its value, to the map.
106: *
107: * @param key the key to set
108: * @param value the value to set the key to
109: * @return the value added when successful, or null if an error
110: */
111: public Object put(Object key, Object value) {
112: Collection c = (Collection) super .get(key);
113: if (c == null) {
114: c = createCollection(null);
115: super .put(key, c);
116: }
117: boolean results = c.add(value);
118:
119: return (results ? value : null);
120: }
121:
122: /**
123: * Removes a specific value from map.
124: * The item is removed from the collection mapped to the specified key.
125: *
126: * @param key the key to remove from
127: * @param item the value to remove
128: * @return the value removed (which was passed in)
129: */
130: public Object remove(Object key, Object item) {
131: Collection valuesForKey = (Collection) super .get(key);
132: if (valuesForKey == null) {
133: return null;
134: }
135: valuesForKey.remove(item);
136:
137: // remove the list if it is now empty
138: // (saves space, and allows equals to work)
139: if (valuesForKey.isEmpty()) {
140: remove(key);
141: }
142: return item;
143: }
144:
145: /**
146: * Clear the map.
147: */
148: public void clear() {
149: // Clear the mappings
150: Set pairs = super .entrySet();
151: Iterator pairsIterator = pairs.iterator();
152: while (pairsIterator.hasNext()) {
153: Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
154: Collection coll = (Collection) keyValuePair.getValue();
155: coll.clear();
156: }
157: super .clear();
158: }
159:
160: /**
161: * Accessor for the values in the Map.
162: * @return all of the values in the map
163: */
164: public Collection values() {
165: Collection vs = values;
166: return (vs != null ? vs : (values = new ValueElement()));
167: }
168:
169: /**
170: * Method to clone the Map. Performs a shallow copy of the entry set.
171: * @return the cloned map
172: */
173: public Object clone() {
174: MultiMap obj = (MultiMap) super .clone();
175:
176: // Clone the entry set.
177: for (Iterator it = entrySet().iterator(); it.hasNext();) {
178: Map.Entry entry = (Map.Entry) it.next();
179: Collection coll = (Collection) entry.getValue();
180: Collection newColl = createCollection(coll);
181: entry.setValue(newColl);
182: }
183: return obj;
184: }
185:
186: /**
187: * Creates a new instance of the map value Collection container.
188: *
189: * @param c the collection to copy
190: * @return new collection
191: */
192: protected Collection createCollection(Collection c) {
193: if (c == null) {
194: return new ArrayList();
195: } else {
196: return new ArrayList(c);
197: }
198: }
199:
200: /**
201: * Representation of the values.
202: */
203: private class ValueElement extends AbstractCollection {
204: public Iterator iterator() {
205: return new ValueElementIter();
206: }
207:
208: public int size() {
209: int i = 0;
210: Iterator iter = iterator();
211: while (iter.hasNext()) {
212: iter.next();
213: i++;
214: }
215: return i;
216: }
217:
218: public void clear() {
219: MultiMap.this .clear();
220: }
221: }
222:
223: /**
224: * Iterator for the values.
225: */
226: private class ValueElementIter implements Iterator {
227: private Iterator backing;
228: private Iterator temp;
229:
230: private ValueElementIter() {
231: backing = MultiMap.super .values().iterator();
232: }
233:
234: private boolean searchNextIterator() {
235: while (temp == null || temp.hasNext() == false) {
236: if (backing.hasNext() == false) {
237: return false;
238: }
239: temp = ((Collection) backing.next()).iterator();
240: }
241: return true;
242: }
243:
244: public boolean hasNext() {
245: return searchNextIterator();
246: }
247:
248: public Object next() {
249: if (searchNextIterator() == false) {
250: throw new NoSuchElementException();
251: }
252: return temp.next();
253: }
254:
255: public void remove() {
256: if (temp == null) {
257: throw new IllegalStateException();
258: }
259: temp.remove();
260: }
261:
262: }
263: }
|