001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.util;
028:
029: import java.util.AbstractCollection;
030: import java.util.AbstractSet;
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.Set;
036:
037: /**
038: * An implementation of <code>Map</code> that maintains the elements in
039: * the order they were added, as opposed to the random order of a
040: * <code>HashMap</code>.
041: *
042: * An ArrayMap is a Map, and can be used exactly like a typical Map.
043: * The most significant differences are that<ol>
044: * <li>The elements are kept in the order that they are added.</li>
045: * <li>One can restrict the (key, value) types by overriding
046: * <tt>createEntry(Object key, Object value)</tt>.</li>
047: * <li>There are index-based "getters", such as <tt>getKey(int)</tt>.
048: * </ol>
049: *
050: * @see java.util.Map
051: */
052: public class ArrayMap implements Map, Cloneable, java.io.Serializable {
053:
054: /**
055: * Use an <code>ArrayList</code> to hold the <code>Map.Entry</code>
056: * elements.
057: *
058: * This could be optimized for lookup speed, but a simple ArrayList
059: * should be fine for our needs.
060: */
061: private final ArrayList l;
062:
063: public ArrayMap(int initialCapacity) {
064: l = new ArrayList(initialCapacity);
065: }
066:
067: public ArrayMap() {
068: this (10);
069: }
070:
071: public ArrayMap(Map t) {
072: this ((110 * t.size()) / 100); // Allow 10% room for growth
073: putAll(t);
074: }
075:
076: /**
077: * Create a new <code>Map.Entry</code>.
078: *
079: * @see ArrayMap.ArrayEntry
080: */
081: protected Map.Entry createEntry(final Object key, final Object value) {
082: // create the standard implementation of Map.Entry
083: return new ArrayEntry(key, value);
084: }
085:
086: /**
087: * Helper utility for <tt>createEntry</tt> to see if an
088: * <code>Object</code> represents a wrapped Java primitive:</ul>
089: * <li><code>Boolean</code></li>
090: * <li><code>Character</code></li>
091: * <li><code>Byte</code></li>
092: * <li><code>Short</code></li>
093: * <li><code>Integer</code></li>
094: * <li><code>Long</code></li>
095: * <li><code>Float</code></li>
096: * <li><code>Double</code></li>
097: * <li><code>Void</code></li>
098: * </ul>.
099: *
100: * @return true if the given Object is a wrapped Java primitive
101: */
102: public static final boolean isWrappedPrimitive(final Object o) {
103: // must be better ways to do this! maybe a HashMap of Classes?
104: return ((o instanceof Boolean) || (o instanceof Integer)
105: || (o instanceof Long) || (o instanceof Double)
106: || (o instanceof Float) || (o instanceof Character)
107: || (o instanceof Byte) || (o instanceof Short) || (o instanceof Void));
108: }
109:
110: public int size() {
111: return l.size();
112: }
113:
114: public boolean isEmpty() {
115: return l.isEmpty();
116: }
117:
118: public boolean containsKey(Object key) {
119: // search for key
120: int n = l.size();
121: if (key == null) {
122: for (int i = 0; i < n; i++) {
123: Map.Entry meI = (Map.Entry) l.get(i);
124: if (meI.getKey() == null) {
125: // found matching entry
126: return true;
127: }
128: }
129: } else {
130: for (int i = 0; i < n; i++) {
131: Map.Entry meI = (Map.Entry) l.get(i);
132: if (key.equals(meI.getKey())) {
133: // found matching entry
134: return true;
135: }
136: }
137: }
138: // no such entry
139: return false;
140: }
141:
142: public boolean containsValue(Object value) {
143: // search for key
144: int n = l.size();
145: if (value == null) {
146: for (int i = 0; i < n; i++) {
147: Map.Entry meI = (Map.Entry) l.get(i);
148: if (meI.getValue() == null) {
149: // found matching entry
150: return true;
151: }
152: }
153: } else {
154: for (int i = 0; i < n; i++) {
155: Map.Entry meI = (Map.Entry) l.get(i);
156: if (value.equals(meI.getValue())) {
157: // found matching entry
158: return true;
159: }
160: }
161: }
162: // no such entry
163: return false;
164: }
165:
166: /**
167: * Get the value for the specified key.
168: *
169: * @see #containsKey(Object) especially if <tt>isValidValue</tt> allows
170: * <tt>null</tt>.
171: *
172: * @see #getValue(int) for a indexed lookup
173: */
174: public Object get(Object key) {
175: // search for key
176: int n = l.size();
177: if (key == null) {
178: for (int i = 0; i < n; i++) {
179: Map.Entry meI = (Map.Entry) l.get(i);
180: if (meI.getKey() == null) {
181: // found matching entry
182: //
183: // note that value can be null -- use "containsKey(key)"
184: // to distinguish these cases
185: return meI.getValue();
186: }
187: }
188: } else {
189: for (int i = 0; i < n; i++) {
190: Map.Entry meI = (Map.Entry) l.get(i);
191: if (key.equals(meI.getKey())) {
192: // found matching entry
193: //
194: return meI.getValue();
195: }
196: }
197: }
198: // no such entry
199: return null;
200: }
201:
202: /**
203: * Simple utility from <code>java.util.Properties</code>.
204: *
205: * Note that
206: *
207: * @see #getValue(int) for a indexed lookup
208: */
209: public Object get(Object key, Object defaultValue) {
210: Object val = get(key);
211: return ((val != null) ? val : defaultValue);
212: }
213:
214: public Object put(Object key, Object value) {
215: // see if key is already listed
216: int n = l.size();
217: for (int i = 0; i < n; i++) {
218: Map.Entry meI = (Map.Entry) l.get(i);
219: if (key.equals(meI.getKey())) {
220: // found matching entry, replace value
221: return meI.setValue(value);
222: }
223: }
224: // create a new entry
225: Map.Entry newME = createEntry(key, value);
226: l.add(newME);
227: // no old value
228: return null;
229: }
230:
231: public Object remove(Object key) {
232: // search for key
233: int n = l.size();
234: if (key == null) {
235: for (int i = 0; i < n; i++) {
236: Map.Entry meI = (Map.Entry) l.get(i);
237: if (meI.getKey() == null) {
238: // found matching entry
239: //
240: // note that value can be null -- use "containsKey(key)"
241: // to distinguish these cases
242: l.remove(i);
243: return meI.getValue();
244: }
245: }
246: } else {
247: for (int i = 0; i < n; i++) {
248: Map.Entry meI = (Map.Entry) l.get(i);
249: if (key.equals(meI.getKey())) {
250: // found matching entry
251: //
252: // note that value can be null -- use "containsKey(key)"
253: // to distinguish these cases
254: l.remove(i);
255: return meI.getValue();
256: }
257: }
258: }
259: // no such entry
260: return null;
261: }
262:
263: public Object clone() {
264: try {
265: return super .clone();
266: } catch (CloneNotSupportedException e) {
267: // this shouldn't happen, since we are Cloneable
268: throw new InternalError();
269: }
270: }
271:
272: //
273: // Lots of code taken from HashMap
274: //
275:
276: public void putAll(Map t) {
277: Iterator i = t.entrySet().iterator();
278: while (i.hasNext()) {
279: Map.Entry e = (Map.Entry) i.next();
280: put(e.getKey(), e.getValue());
281: }
282: }
283:
284: public void clear() {
285: l.clear();
286: }
287:
288: private transient Set keySet = null;
289: private transient Set entrySet = null;
290: private transient Collection values = null;
291:
292: public Set keySet() {
293: if (keySet == null) {
294: keySet = new AbstractSet() {
295: public Iterator iterator() {
296: final Iterator lIter = l.iterator();
297: return new Iterator() {
298: public boolean hasNext() {
299: return lIter.hasNext();
300: }
301:
302: public Object next() {
303: return ((Map.Entry) lIter.next()).getKey();
304: }
305:
306: public void remove() {
307: lIter.remove();
308: }
309: };
310: }
311:
312: public int size() {
313: return l.size();
314: }
315:
316: public boolean contains(Object o) {
317: return containsKey(o);
318: }
319:
320: public boolean remove(Object o) {
321: return ArrayMap.this .remove(o) != null;
322: }
323:
324: public void clear() {
325: ArrayMap.this .clear();
326: }
327: };
328: }
329: return keySet;
330: }
331:
332: public Collection values() {
333: if (values == null) {
334: values = new AbstractCollection() {
335: public Iterator iterator() {
336: final Iterator lIter = l.iterator();
337: return new Iterator() {
338: public boolean hasNext() {
339: return lIter.hasNext();
340: }
341:
342: public Object next() {
343: return ((ArrayEntry) lIter.next())
344: .getValue();
345: }
346:
347: public void remove() {
348: lIter.remove();
349: }
350: };
351: }
352:
353: public int size() {
354: return l.size();
355: }
356:
357: public boolean contains(Object o) {
358: return containsValue(o);
359: }
360:
361: public void clear() {
362: ArrayMap.this .clear();
363: }
364: };
365: }
366: return values;
367: }
368:
369: public Set entrySet() {
370: if (entrySet == null) {
371: entrySet = new AbstractSet() {
372: public Iterator iterator() {
373: return l.iterator();
374: }
375:
376: public boolean contains(Object o) {
377: if (o instanceof Map.Entry) {
378: int n = l.size();
379: for (int i = 0; i < n; i++) {
380: if (o.equals(l.get(i))) {
381: return true;
382: }
383: }
384: }
385: return false;
386: }
387:
388: public boolean remove(Object o) {
389: if (o instanceof Map.Entry) {
390: int n = l.size();
391: for (int i = 0; i < n; i++) {
392: if (o.equals(l.get(i))) {
393: l.remove(i);
394: return true;
395: }
396: }
397: }
398: return false;
399: }
400:
401: public int size() {
402: return l.size();
403: }
404:
405: public void clear() {
406: ArrayMap.this .clear();
407: }
408: };
409: }
410:
411: return entrySet;
412: }
413:
414: //
415: // Some List methods that might be useful
416: //
417:
418: public void trimToSize() {
419: l.trimToSize();
420: }
421:
422: /**
423: * Get the <code>Map.Entry</code> at the specifed offset of the
424: * <tt>entrySet().iterator()</tt>.
425: *
426: * <code>Map</code>s typically don't define an indexable ordering.
427: * However, an <code>ArrayMap</code> is defined to be sorted in the
428: * order that the elements were added, so let's make this function
429: * available.
430: */
431: public Map.Entry get(int index) {
432: return (Map.Entry) l.get(index);
433: }
434:
435: /**
436: * @see #get(int)
437: */
438: public Object getKey(int index) {
439: return get(index).getKey();
440: }
441:
442: /**
443: * @see #get(int)
444: */
445: public Object getValue(int index) {
446: return get(index).getValue();
447: }
448:
449: //
450: // Other List methods could be added, but let's limit it to "get*(int)"
451: // for now.
452: //
453:
454: /**
455: * Simple <code>Map.Entry</code> implementation.
456: *
457: * Subclasses of <code>ArrayMap</code> can subclass this implementation,
458: * restrict the (key, value)s, and override <tt>ArrayMap.createEntry</tt>.
459: * For example, one could force String keys and values by overriding
460: * the constructor and <tt>setValue(Object)</tt>.
461: */
462: protected static class ArrayEntry implements Map.Entry, Cloneable,
463: java.io.Serializable {
464:
465: public final Object key;
466: public Object value;
467:
468: public ArrayEntry(final Object key, final Object value) {
469: this .key = key;
470: this .value = value;
471: }
472:
473: public Object getKey() {
474: return key;
475: }
476:
477: public Object getValue() {
478: return value;
479: }
480:
481: public Object setValue(final Object newValue) {
482: Object oldValue = this .value;
483: this .value = newValue;
484: return oldValue;
485: }
486:
487: public Object clone() {
488: try {
489: return super .clone();
490: } catch (CloneNotSupportedException e) {
491: // this shouldn't happen, since we are Cloneable
492: throw new InternalError();
493: }
494: }
495:
496: public boolean equals(final Object o) {
497: if (o == this ) {
498: return true;
499: } else if (o instanceof Map.Entry) {
500: Map.Entry me = (Map.Entry) o;
501: return (((key != null) ? (key.equals(me.getKey()))
502: : (me.getKey() == null)) && ((value != null) ? (value
503: .equals(me.getValue()))
504: : (me.getValue() == null)));
505: } else {
506: return false;
507: }
508: }
509:
510: public int hashCode() {
511: return (((key != null) ? key.hashCode() : 0) ^ ((value != null) ? value
512: .hashCode()
513: : 0));
514: }
515:
516: public String toString() {
517: return key + "=" + value;
518: }
519: }
520:
521: public String toString() {
522: int n = l.size();
523: if (n > 0) {
524: StringBuffer buf = new StringBuffer();
525: buf.append("{");
526: buf.append(l.get(0));
527: for (int i = 1; i < n; i++) {
528: buf.append(", ");
529: buf.append(l.get(i));
530: }
531: buf.append("}");
532: return buf.toString();
533: } else {
534: return "{}";
535: }
536: }
537:
538: private static final long serialVersionUID = 782934727772928381L;
539: }
|