001: /* Licensed to the Apache Software Foundation (ASF) under one or more
002: * contributor license agreements. See the NOTICE file distributed with
003: * this work for additional information regarding copyright ownership.
004: * The ASF licenses this file to You under the Apache License, Version 2.0
005: * (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package java.util;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.ObjectOutputStream;
022: import java.io.Serializable;
023: import java.lang.reflect.Array;
024:
025: public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
026: implements Serializable, Cloneable {
027:
028: private static final long serialVersionUID = 458661240069192865L;
029:
030: private Class<K> keyType;
031:
032: transient Enum[] keys;
033:
034: transient Object[] values;
035:
036: transient boolean[] hasMapping;
037:
038: private transient int mappingsCount;
039:
040: transient int enumSize;
041:
042: private transient EnumMapEntrySet<K, V> entrySet = null;
043:
044: private static class Entry<KT extends Enum<KT>, VT> extends
045: MapEntry<KT, VT> {
046: private final EnumMap<KT, VT> enumMap;
047:
048: private final int ordinal;
049:
050: Entry(KT theKey, VT theValue, EnumMap<KT, VT> em) {
051: super (theKey, theValue);
052: enumMap = em;
053: ordinal = ((Enum) theKey).ordinal();
054: }
055:
056: @SuppressWarnings("unchecked")
057: @Override
058: public boolean equals(Object object) {
059: if (!enumMap.hasMapping[ordinal]) {
060: return false;
061: }
062: boolean isEqual = false;
063: if (object instanceof Map.Entry) {
064: Map.Entry<KT, VT> entry = (Map.Entry<KT, VT>) object;
065: Object enumKey = entry.getKey();
066: if (key.equals(enumKey)) {
067: Object theValue = entry.getValue();
068: isEqual = enumMap.values[ordinal] == null ? null == theValue
069: : enumMap.values[ordinal].equals(theValue);
070: }
071: }
072: return isEqual;
073: }
074:
075: @Override
076: public int hashCode() {
077: return (enumMap.keys[ordinal] == null ? 0
078: : enumMap.keys[ordinal].hashCode())
079: ^ (enumMap.values[ordinal] == null ? 0
080: : enumMap.values[ordinal].hashCode());
081: }
082:
083: @SuppressWarnings("unchecked")
084: @Override
085: public KT getKey() {
086: checkEntryStatus();
087: return (KT) enumMap.keys[ordinal];
088: }
089:
090: @SuppressWarnings("unchecked")
091: @Override
092: public VT getValue() {
093: checkEntryStatus();
094: return (VT) enumMap.values[ordinal];
095: }
096:
097: @SuppressWarnings("unchecked")
098: @Override
099: public VT setValue(VT value) {
100: checkEntryStatus();
101: return enumMap.put((KT) enumMap.keys[ordinal], value);
102: }
103:
104: @Override
105: public String toString() {
106: StringBuilder result = new StringBuilder(
107: enumMap.keys[ordinal].toString());
108: result.append("="); //$NON-NLS-1$
109: result.append(enumMap.values[ordinal].toString());
110: return result.toString();
111: }
112:
113: private void checkEntryStatus() {
114: if (!enumMap.hasMapping[ordinal]) {
115: throw new IllegalStateException();
116: }
117: }
118: }
119:
120: private static class EnumMapIterator<E, KT extends Enum<KT>, VT>
121: implements Iterator<E> {
122: int position = 0;
123:
124: int prePosition = -1;
125:
126: final EnumMap<KT, VT> enumMap;
127:
128: final MapEntry.Type<E, KT, VT> type;
129:
130: EnumMapIterator(MapEntry.Type<E, KT, VT> value,
131: EnumMap<KT, VT> em) {
132: enumMap = em;
133: type = value;
134: }
135:
136: public boolean hasNext() {
137: int length = enumMap.enumSize;
138: for (; position < length; position++) {
139: if (enumMap.hasMapping[position]) {
140: break;
141: }
142: }
143: return position != length;
144: }
145:
146: @SuppressWarnings("unchecked")
147: public E next() {
148: if (!hasNext()) {
149: throw new NoSuchElementException();
150: }
151: prePosition = position++;
152: return type.get(new MapEntry(enumMap.keys[prePosition],
153: enumMap.values[prePosition]));
154: }
155:
156: public void remove() {
157: checkStatus();
158: if (enumMap.hasMapping[prePosition]) {
159: enumMap.remove(enumMap.keys[prePosition]);
160: }
161: prePosition = -1;
162: }
163:
164: @Override
165: @SuppressWarnings("unchecked")
166: public String toString() {
167: if (-1 == prePosition) {
168: return super .toString();
169: }
170: return type.get(
171: new MapEntry(enumMap.keys[prePosition],
172: enumMap.values[prePosition])).toString();
173: }
174:
175: private void checkStatus() {
176: if (-1 == prePosition) {
177: throw new IllegalStateException();
178: }
179: }
180: }
181:
182: private static class EnumMapKeySet<KT extends Enum<KT>, VT> extends
183: AbstractSet<KT> {
184: private final EnumMap<KT, VT> enumMap;
185:
186: EnumMapKeySet(EnumMap<KT, VT> em) {
187: enumMap = em;
188: }
189:
190: @Override
191: public void clear() {
192: enumMap.clear();
193: }
194:
195: @Override
196: public boolean contains(Object object) {
197: return enumMap.containsKey(object);
198: }
199:
200: @Override
201: @SuppressWarnings("unchecked")
202: public Iterator iterator() {
203: return new EnumMapIterator<KT, KT, VT>(
204: new MapEntry.Type<KT, KT, VT>() {
205: public KT get(MapEntry<KT, VT> entry) {
206: return entry.key;
207: }
208: }, enumMap);
209: }
210:
211: @Override
212: @SuppressWarnings("unchecked")
213: public boolean remove(Object object) {
214: if (contains(object)) {
215: enumMap.remove(object);
216: return true;
217: }
218: return false;
219: }
220:
221: @Override
222: public int size() {
223: return enumMap.size();
224: }
225: }
226:
227: private static class EnumMapValueCollection<KT extends Enum<KT>, VT>
228: extends AbstractCollection<VT> {
229: private final EnumMap<KT, VT> enumMap;
230:
231: EnumMapValueCollection(EnumMap<KT, VT> em) {
232: enumMap = em;
233: }
234:
235: @Override
236: public void clear() {
237: enumMap.clear();
238: }
239:
240: @Override
241: public boolean contains(Object object) {
242: return enumMap.containsValue(object);
243: }
244:
245: @SuppressWarnings("unchecked")
246: @Override
247: public Iterator iterator() {
248: return new EnumMapIterator<VT, KT, VT>(
249: new MapEntry.Type<VT, KT, VT>() {
250: public VT get(MapEntry<KT, VT> entry) {
251: return entry.value;
252: }
253: }, enumMap);
254: }
255:
256: @Override
257: public boolean remove(Object object) {
258: if (null == object) {
259: for (int i = 0; i < enumMap.enumSize; i++) {
260: if (enumMap.hasMapping[i]
261: && null == enumMap.values[i]) {
262: enumMap.remove(enumMap.keys[i]);
263: return true;
264: }
265: }
266: } else {
267: for (int i = 0; i < enumMap.enumSize; i++) {
268: if (enumMap.hasMapping[i]
269: && object.equals(enumMap.values[i])) {
270: enumMap.remove(enumMap.keys[i]);
271: return true;
272: }
273: }
274: }
275: return false;
276: }
277:
278: @Override
279: public int size() {
280: return enumMap.size();
281: }
282: }
283:
284: private static class EnumMapEntryIterator<E, KT extends Enum<KT>, VT>
285: extends EnumMapIterator<E, KT, VT> {
286: EnumMapEntryIterator(MapEntry.Type<E, KT, VT> value,
287: EnumMap<KT, VT> em) {
288: super (value, em);
289: }
290:
291: @SuppressWarnings("unchecked")
292: @Override
293: public E next() {
294: if (!hasNext()) {
295: throw new NoSuchElementException();
296: }
297: prePosition = position++;
298: return type.get(new Entry<KT, VT>(
299: (KT) enumMap.keys[prePosition],
300: (VT) enumMap.values[prePosition], enumMap));
301: }
302: }
303:
304: private static class EnumMapEntrySet<KT extends Enum<KT>, VT>
305: extends AbstractSet<Map.Entry<KT, VT>> {
306: private final EnumMap<KT, VT> enumMap;
307:
308: EnumMapEntrySet(EnumMap<KT, VT> em) {
309: enumMap = em;
310: }
311:
312: @Override
313: public void clear() {
314: enumMap.clear();
315: }
316:
317: @Override
318: public boolean contains(Object object) {
319: boolean isEqual = false;
320: if (object instanceof Map.Entry) {
321: Object enumKey = ((Map.Entry) object).getKey();
322: Object enumValue = ((Map.Entry) object).getValue();
323: if (enumMap.containsKey(enumKey)) {
324: VT value = enumMap.get(enumKey);
325: isEqual = (value == null ? null == enumValue
326: : value.equals(enumValue));
327: }
328: }
329: return isEqual;
330: }
331:
332: @Override
333: public Iterator<Map.Entry<KT, VT>> iterator() {
334: return new EnumMapEntryIterator<Map.Entry<KT, VT>, KT, VT>(
335: new MapEntry.Type<Map.Entry<KT, VT>, KT, VT>() {
336: public Map.Entry<KT, VT> get(
337: MapEntry<KT, VT> entry) {
338: return entry;
339: }
340: }, enumMap);
341: }
342:
343: @Override
344: public boolean remove(Object object) {
345: if (contains(object)) {
346: enumMap.remove(((Map.Entry) object).getKey());
347: return true;
348: }
349: return false;
350: }
351:
352: @Override
353: public int size() {
354: return enumMap.size();
355: }
356:
357: @Override
358: public Object[] toArray() {
359: Object[] entryArray = new Object[enumMap.size()];
360: return toArray(entryArray);
361: }
362:
363: @SuppressWarnings("unchecked")
364: @Override
365: public Object[] toArray(Object[] array) {
366: int size = enumMap.size();
367: int index = 0;
368: Object[] entryArray = array;
369: if (size > array.length) {
370: Class<?> clazz = array.getClass().getComponentType();
371: entryArray = (Object[]) Array.newInstance(clazz, size);
372: }
373: Iterator<Map.Entry<KT, VT>> iter = iterator();
374: for (; index < size; index++) {
375: Map.Entry<KT, VT> entry = iter.next();
376: entryArray[index] = new MapEntry<KT, VT>(
377: entry.getKey(), entry.getValue());
378: }
379: if (index < array.length) {
380: entryArray[index] = null;
381: }
382: return entryArray;
383: }
384: }
385:
386: /**
387: * Constructs an empty EnumMap.
388: *
389: * @param keyType
390: * the Class that is to be used for the key type for this map
391: * @throws NullPointerException
392: * if the keyType is null
393: */
394: public EnumMap(Class<K> keyType) {
395: initialization(keyType);
396: }
397:
398: /**
399: * Constructs an EnumMap using the same key type and contents as the given
400: * EnumMap.
401: *
402: * @param map
403: * the EnumMap from which the initial contents of this EnumMap
404: * are copied
405: * @throws NullPointerException
406: * if the map is null
407: */
408: public EnumMap(EnumMap<K, ? extends V> map) {
409: initialization(map);
410: }
411:
412: /**
413: * Constructs an EnumMap with the same contents as the given Map. If the Map
414: * is an EnumMap, this is equivalent to calling
415: * {@link EnumMap#EnumMap(EnumMap)}}. Otherwise, the given map cannot be
416: * empty so that the key type of this EnumMap can be inferred.
417: *
418: * @param map
419: * the Map from which the initial contents of this EnumMap are
420: * copied
421: * @throws IllegalArgumentException
422: * if the map is empty and is not of type <code>EnumMap</code>
423: * @throws NullPointerException
424: * if the map is null
425: */
426: @SuppressWarnings("unchecked")
427: public EnumMap(Map<K, ? extends V> map) {
428: if (map instanceof EnumMap) {
429: initialization((EnumMap<K, V>) map);
430: } else {
431: if (0 == map.size()) {
432: throw new IllegalArgumentException();
433: }
434: Iterator<K> iter = map.keySet().iterator();
435: K enumKey = iter.next();
436: Class clazz = enumKey.getClass();
437: if (clazz.isEnum()) {
438: initialization(clazz);
439: } else {
440: initialization(clazz.getSuperclass());
441: }
442: putAllImpl(map);
443: }
444: }
445:
446: /**
447: * Clears this map.
448: */
449: @Override
450: public void clear() {
451: Arrays.fill(values, null);
452: Arrays.fill(hasMapping, false);
453: mappingsCount = 0;
454: }
455:
456: /**
457: * Clones this map to create a shallow copy.
458: *
459: * @return a shallow copy of this map
460: */
461: @SuppressWarnings("unchecked")
462: @Override
463: public EnumMap<K, V> clone() {
464: try {
465: EnumMap<K, V> enumMap = (EnumMap<K, V>) super .clone();
466: enumMap.initialization(this );
467: return enumMap;
468: } catch (CloneNotSupportedException e) {
469: return null;
470: }
471: }
472:
473: /**
474: * Returns true if the given object is present as a key in this map.
475: *
476: * @param key
477: * the key to look for
478: * @return true if this map contains the key
479: */
480: @Override
481: public boolean containsKey(Object key) {
482: if (isValidKeyType(key)) {
483: int keyOrdinal = ((Enum) key).ordinal();
484: return hasMapping[keyOrdinal];
485: }
486: return false;
487: }
488:
489: /**
490: * Returns true if the given object is present as a value in this map.
491: *
492: * @param value
493: * the value to look for
494: * @return true if this map contains the value.
495: */
496: @Override
497: public boolean containsValue(Object value) {
498: if (null == value) {
499: for (int i = 0; i < enumSize; i++) {
500: if (hasMapping[i] && null == values[i]) {
501: return true;
502: }
503: }
504: } else {
505: for (int i = 0; i < enumSize; i++) {
506: if (hasMapping[i] && value.equals(values[i])) {
507: return true;
508: }
509: }
510: }
511: return false;
512: }
513:
514: /**
515: * Returns a Set of <code>Map.Entry</code>s that represent the entries in
516: * this EnumMap. Making changes to this Set will change the original EnumMap
517: * and vice-versa. Entries can be removed from the Set, or their values can
518: * be changed, but new entries cannot be added.
519: *
520: * The order of the entries in the Set will be the order that the Enum keys
521: * were declared in.
522: *
523: * @return a Set of <code>Map.Entry</code>s representing the entries in
524: * this EnumMap
525: */
526: @Override
527: public Set<Map.Entry<K, V>> entrySet() {
528: if (null == entrySet) {
529: entrySet = new EnumMapEntrySet<K, V>(this );
530: }
531: return entrySet;
532: }
533:
534: /**
535: * Returns true if this EnumMap is equal to the given object.
536: *
537: * @param object
538: * the object
539: * @return true if this EnumMap is equal to the given object.
540: */
541: @SuppressWarnings("unchecked")
542: @Override
543: public boolean equals(Object object) {
544: if (this == object) {
545: return true;
546: }
547: if (!(object instanceof EnumMap)) {
548: return super .equals(object);
549: }
550: EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
551: if (keyType != enumMap.keyType || size() != enumMap.size()) {
552: return false;
553: }
554: return Arrays.equals(hasMapping, enumMap.hasMapping)
555: && Arrays.equals(values, enumMap.values);
556: }
557:
558: /**
559: * Returns the value stored in this map for the given key in this map, or null
560: * if this map has no entry for that key.
561: *
562: * @param key
563: * the key to get the value for.
564: * @return the value for the given key.
565: */
566: @Override
567: @SuppressWarnings("unchecked")
568: public V get(Object key) {
569: if (!isValidKeyType(key)) {
570: return null;
571: }
572: int keyOrdinal = ((Enum) key).ordinal();
573: return (V) values[keyOrdinal];
574: }
575:
576: /**
577: * Returns a Set containing the keys for this EnumMap. Making changes to
578: * this Set will change the original EnumMap and vice-versa. Entries can be
579: * removed from the Set, but new entries cannot be added.
580: *
581: * The order of the Set will be the order that the Enum keys were declared
582: * in.
583: *
584: * @return a Set containing the keys for this EnumMap.
585: */
586: @Override
587: public Set<K> keySet() {
588: if (null == keySet) {
589: keySet = new EnumMapKeySet<K, V>(this );
590: }
591: return keySet;
592: }
593:
594: /**
595: * Stores a value in this map for the given key. If the map already has an
596: * entry for this key the current value will be overwritten.
597: *
598: * @param key
599: * the key
600: * @param value
601: * the value to store for the given key
602: * @return the value stored for the given key, or null if this map has no
603: * entry for the key
604: * @throws NullPointerException
605: * if the key is null
606: */
607: @Override
608: @SuppressWarnings("unchecked")
609: public V put(K key, V value) {
610: return putImpl(key, value);
611: }
612:
613: /**
614: * Add all the entries in the given map to this map
615: *
616: * @param map
617: * the map whose entries to copy
618: * @throws NullPointerException
619: * if the given map or any of its keys are null
620: */
621: @Override
622: @SuppressWarnings("unchecked")
623: public void putAll(Map<? extends K, ? extends V> map) {
624: putAllImpl(map);
625: }
626:
627: /**
628: * Removes the entry for the given key from this map, if there is one.
629: *
630: * @param key
631: * the key to remove
632: * @return the value that had been stored for the key, or null if there was
633: * not one.
634: */
635: @Override
636: @SuppressWarnings("unchecked")
637: public V remove(Object key) {
638: if (!isValidKeyType(key)) {
639: return null;
640: }
641: int keyOrdinal = ((Enum) key).ordinal();
642: if (hasMapping[keyOrdinal]) {
643: hasMapping[keyOrdinal] = false;
644: mappingsCount--;
645: }
646: V oldValue = (V) values[keyOrdinal];
647: values[keyOrdinal] = null;
648: return oldValue;
649: }
650:
651: /**
652: * Returns the size of this map
653: *
654: * @return the number of entries in the map
655: */
656: @Override
657: public int size() {
658: return mappingsCount;
659: }
660:
661: /**
662: * Returns a Collection containing the values for this EnumMap. Making
663: * changes to this Collection will change the original EnumMap and
664: * vice-versa. Values can be removed from the Collection, but new entries
665: * cannot be added.
666: *
667: * The order of the values in the Collection will be the order that their
668: * corresponding Enum keys were declared in.
669: *
670: * @return a Collection containing the values for this EnumMap
671: */
672: @Override
673: public Collection<V> values() {
674: if (null == valuesCollection) {
675: valuesCollection = new EnumMapValueCollection<K, V>(this );
676: }
677: return valuesCollection;
678: }
679:
680: @SuppressWarnings("unchecked")
681: private void readObject(ObjectInputStream stream)
682: throws IOException, ClassNotFoundException {
683: stream.defaultReadObject();
684: initialization(keyType);
685: int elementCount = stream.readInt();
686: Enum<K> enumKey;
687: Object value;
688: for (int i = elementCount; i > 0; i--) {
689: enumKey = (Enum<K>) stream.readObject();
690: value = stream.readObject();
691: putImpl((K) enumKey, (V) value);
692: }
693: }
694:
695: private void writeObject(ObjectOutputStream stream)
696: throws IOException {
697: stream.defaultWriteObject();
698: stream.writeInt(mappingsCount);
699: Iterator<Map.Entry<K, V>> iterator = entrySet().iterator();
700: while (iterator.hasNext()) {
701: Map.Entry<K, V> entry = iterator.next();
702: stream.writeObject(entry.getKey());
703: stream.writeObject(entry.getValue());
704: }
705: }
706:
707: private boolean isValidKeyType(Object key) {
708: if (null != key && keyType.isInstance(key)) {
709: return true;
710: }
711: return false;
712: }
713:
714: @SuppressWarnings("unchecked")
715: private void initialization(EnumMap enumMap) {
716: keyType = enumMap.keyType;
717: keys = enumMap.keys;
718: enumSize = enumMap.enumSize;
719: values = enumMap.values.clone();
720: hasMapping = enumMap.hasMapping.clone();
721: mappingsCount = enumMap.mappingsCount;
722: }
723:
724: private void initialization(Class<K> type) {
725: keyType = type;
726: keys = keyType.getEnumConstants();
727: enumSize = keys.length;
728: values = new Object[enumSize];
729: hasMapping = new boolean[enumSize];
730: }
731:
732: @SuppressWarnings("unchecked")
733: private void putAllImpl(Map map) {
734: Iterator iter = map.entrySet().iterator();
735: while (iter.hasNext()) {
736: Map.Entry entry = (Map.Entry) iter.next();
737: putImpl((K) entry.getKey(), (V) entry.getValue());
738: }
739: }
740:
741: @SuppressWarnings("unchecked")
742: private V putImpl(K key, V value) {
743: if (null == key) {
744: throw new NullPointerException();
745: }
746: if (!isValidKeyType(key)) {
747: throw new ClassCastException();
748: }
749: int keyOrdinal = key.ordinal();
750: if (!hasMapping[keyOrdinal]) {
751: hasMapping[keyOrdinal] = true;
752: mappingsCount++;
753: }
754: V oldValue = (V) values[keyOrdinal];
755: values[keyOrdinal] = value;
756: return oldValue;
757: }
758:
759: }
|