1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.util;
18
19import java.io.IOException;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.io.Serializable;
23import java.lang.reflect.Array;
24
25/**
26 * An {@code Map} specialized for use with {@code Enum} types as keys.
27 */
28public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
29        Serializable, Cloneable, Map<K, V> {
30
31    // BEGIN android-changed
32    // added implements Map<K, V> for apicheck
33    // END android-changed
34
35    private static final long serialVersionUID = 458661240069192865L;
36
37    private Class<K> keyType;
38
39    transient K[] keys;
40
41    transient V[] values;
42
43    transient boolean[] hasMapping;
44
45    private transient int mappingsCount;
46
47    transient int enumSize;
48
49    private transient EnumMapEntrySet<K, V> entrySet = null;
50
51    private static class Entry<KT extends Enum<KT>, VT> extends MapEntry<KT, VT> {
52        private final EnumMap<KT, VT> enumMap;
53
54        private final int ordinal;
55
56        Entry(KT theKey, VT theValue, EnumMap<KT, VT> em) {
57            super(theKey, theValue);
58            enumMap = em;
59            ordinal = theKey.ordinal();
60        }
61
62        @Override
63        public boolean equals(Object object) {
64            if (!enumMap.hasMapping[ordinal]) {
65                return false;
66            }
67            boolean isEqual = false;
68            if (object instanceof Map.Entry) {
69                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
70                Object enumKey = entry.getKey();
71                if (key.equals(enumKey)) {
72                    Object theValue = entry.getValue();
73                    if (enumMap.values[ordinal] == null) {
74                        isEqual = (theValue == null);
75                    } else {
76                        isEqual = enumMap.values[ordinal].equals(theValue);
77                    }
78                }
79            }
80            return isEqual;
81        }
82
83        @Override
84        public int hashCode() {
85            return (enumMap.keys[ordinal] == null ? 0 : enumMap.keys[ordinal].hashCode())
86                    ^ (enumMap.values[ordinal] == null ? 0
87                            : enumMap.values[ordinal].hashCode());
88        }
89
90        @Override
91        public KT getKey() {
92            checkEntryStatus();
93            return enumMap.keys[ordinal];
94        }
95
96        @Override
97        public VT getValue() {
98            checkEntryStatus();
99            return enumMap.values[ordinal];
100        }
101
102        @Override
103        public VT setValue(VT value) {
104            checkEntryStatus();
105            return enumMap.put(enumMap.keys[ordinal], value);
106        }
107
108        @Override
109        public String toString() {
110            StringBuilder result = new StringBuilder(enumMap.keys[ordinal].toString());
111            result.append("=");
112            result.append(enumMap.values[ordinal] == null
113                    ? "null" : enumMap.values[ordinal].toString());
114            return result.toString();
115        }
116
117        private void checkEntryStatus() {
118            if (!enumMap.hasMapping[ordinal]) {
119                throw new IllegalStateException();
120            }
121        }
122    }
123
124    private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements Iterator<E> {
125        int position = 0;
126
127        int prePosition = -1;
128
129        final EnumMap<KT, VT> enumMap;
130
131        final MapEntry.Type<E, KT, VT> type;
132
133        EnumMapIterator(MapEntry.Type<E, KT, VT> value, EnumMap<KT, VT> em) {
134            enumMap = em;
135            type = value;
136        }
137
138        public boolean hasNext() {
139            int length = enumMap.enumSize;
140            for (; position < length; position++) {
141                if (enumMap.hasMapping[position]) {
142                    break;
143                }
144            }
145            return position != length;
146        }
147
148        public E next() {
149            if (!hasNext()) {
150                throw new NoSuchElementException();
151            }
152            prePosition = position++;
153            return type.get(new MapEntry<KT, VT>(enumMap.keys[prePosition],
154                    enumMap.values[prePosition]));
155        }
156
157        public void remove() {
158            checkStatus();
159            if (enumMap.hasMapping[prePosition]) {
160                enumMap.remove(enumMap.keys[prePosition]);
161            }
162            prePosition = -1;
163        }
164
165        @Override
166        public String toString() {
167            if (prePosition == -1) {
168                return super.toString();
169            }
170            return type.get(
171                    new MapEntry<KT, VT>(enumMap.keys[prePosition],
172                            enumMap.values[prePosition])).toString();
173        }
174
175        private void checkStatus() {
176            if (prePosition == -1) {
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        public Iterator<KT> iterator() {
202            return new EnumMapIterator<KT, KT, VT>(
203                    new MapEntry.Type<KT, KT, VT>() {
204                        public KT get(MapEntry<KT, VT> entry) {
205                            return entry.key;
206                        }
207                    }, enumMap);
208        }
209
210        @Override
211        public boolean remove(Object object) {
212            if (contains(object)) {
213                enumMap.remove(object);
214                return true;
215            }
216            return false;
217        }
218
219        @Override
220        public int size() {
221            return enumMap.size();
222        }
223    }
224
225    private static class EnumMapValueCollection<KT extends Enum<KT>, VT>
226            extends AbstractCollection<VT> {
227        private final EnumMap<KT, VT> enumMap;
228
229        EnumMapValueCollection(EnumMap<KT, VT> em) {
230            enumMap = em;
231        }
232
233        @Override
234        public void clear() {
235            enumMap.clear();
236        }
237
238        @Override
239        public boolean contains(Object object) {
240            return enumMap.containsValue(object);
241        }
242
243        @Override
244        public Iterator<VT> iterator() {
245            return new EnumMapIterator<VT, KT, VT>(
246                    new MapEntry.Type<VT, KT, VT>() {
247                        public VT get(MapEntry<KT, VT> entry) {
248                            return entry.value;
249                        }
250                    }, enumMap);
251        }
252
253        @Override
254        public boolean remove(Object object) {
255            if (object == null) {
256                for (int i = 0; i < enumMap.enumSize; i++) {
257                    if (enumMap.hasMapping[i] && enumMap.values[i] == null) {
258                        enumMap.remove(enumMap.keys[i]);
259                        return true;
260                    }
261                }
262            } else {
263                for (int i = 0; i < enumMap.enumSize; i++) {
264                    if (enumMap.hasMapping[i]
265                            && object.equals(enumMap.values[i])) {
266                        enumMap.remove(enumMap.keys[i]);
267                        return true;
268                    }
269                }
270            }
271            return false;
272        }
273
274        @Override
275        public int size() {
276            return enumMap.size();
277        }
278    }
279
280    private static class EnumMapEntryIterator<E, KT extends Enum<KT>, VT>
281            extends EnumMapIterator<E, KT, VT> {
282        EnumMapEntryIterator(MapEntry.Type<E, KT, VT> value, EnumMap<KT, VT> em) {
283            super(value, em);
284        }
285
286        @Override
287        public E next() {
288            if (!hasNext()) {
289                throw new NoSuchElementException();
290            }
291            prePosition = position++;
292            return type.get(new EnumMap.Entry<KT, VT>(enumMap.keys[prePosition],
293                    enumMap.values[prePosition], enumMap));
294        }
295    }
296
297    private static class EnumMapEntrySet<KT extends Enum<KT>, VT> extends
298            AbstractSet<Map.Entry<KT, VT>> {
299        private final EnumMap<KT, VT> enumMap;
300
301        EnumMapEntrySet(EnumMap<KT, VT> em) {
302            enumMap = em;
303        }
304
305        @Override
306        public void clear() {
307            enumMap.clear();
308        }
309
310        @Override
311        public boolean contains(Object object) {
312            boolean isEqual = false;
313            if (object instanceof Map.Entry) {
314                Object enumKey = ((Map.Entry<?, ?>) object).getKey();
315                Object enumValue = ((Map.Entry<?, ?>) object).getValue();
316                if (enumMap.containsKey(enumKey)) {
317                    VT value = enumMap.get(enumKey);
318                    if (value == null) {
319                        isEqual = enumValue == null;
320                    } else {
321                        isEqual = value.equals(enumValue);
322                    }
323                }
324            }
325            return isEqual;
326        }
327
328        @Override
329        public Iterator<Map.Entry<KT, VT>> iterator() {
330            return new EnumMapEntryIterator<Map.Entry<KT, VT>, KT, VT>(
331                    new MapEntry.Type<Map.Entry<KT, VT>, KT, VT>() {
332                        public Map.Entry<KT, VT> get(MapEntry<KT, VT> entry) {
333                            return entry;
334                        }
335                    }, enumMap);
336        }
337
338        @Override
339        public boolean remove(Object object) {
340            if (contains(object)) {
341                enumMap.remove(((Map.Entry<?, ?>) object).getKey());
342                return true;
343            }
344            return false;
345        }
346
347        @Override
348        public int size() {
349            return enumMap.size();
350        }
351
352        @Override
353        public Object[] toArray() {
354            Object[] entryArray = new Object[enumMap.size()];
355            return toArray(entryArray);
356        }
357
358        @Override
359        public <T> T[] toArray(T[] array) {
360            int size = enumMap.size();
361            int index = 0;
362            T[] entryArray = array;
363            if (size > array.length) {
364                Class<?> clazz = array.getClass().getComponentType();
365                @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(clazz, size);
366                entryArray = newArray;
367            }
368            Iterator<Map.Entry<KT, VT>> iter = iterator();
369            for (; index < size; index++) {
370                Map.Entry<KT, VT> entry = iter.next();
371                @SuppressWarnings("unchecked") T newEntry =
372                        (T) new MapEntry<KT, VT>(entry.getKey(), entry.getValue());
373                entryArray[index] = newEntry;
374            }
375            if (index < array.length) {
376                entryArray[index] = null;
377            }
378            return entryArray;
379        }
380    }
381
382    /**
383     * Constructs an empty {@code EnumMap} using the given key type.
384     *
385     * @param keyType
386     *            the class object giving the type of the keys used by this {@code EnumMap}.
387     * @throws NullPointerException
388     *             if {@code keyType} is {@code null}.
389     */
390    public EnumMap(Class<K> keyType) {
391        initialization(keyType);
392    }
393
394    /**
395     * Constructs an {@code EnumMap} using the same key type as the given {@code EnumMap} and
396     * initially containing the same mappings.
397     *
398     * @param map
399     *            the {@code EnumMap} from which this {@code EnumMap} is initialized.
400     * @throws NullPointerException
401     *             if {@code map} is {@code null}.
402     */
403    public EnumMap(EnumMap<K, ? extends V> map) {
404        initialization(map);
405    }
406
407    /**
408     * Constructs an {@code EnumMap} initialized from the given map. If the given map
409     * is an {@code EnumMap} instance, this constructor behaves in the exactly the same
410     * way as {@link EnumMap#EnumMap(EnumMap)}}. Otherwise, the given map
411     * should contain at least one mapping.
412     *
413     * @param map
414     *            the map from which this {@code EnumMap} is initialized.
415     * @throws IllegalArgumentException
416     *             if {@code map} is not an {@code EnumMap} instance and does not contain
417     *             any mappings.
418     * @throws NullPointerException
419     *             if {@code map} is {@code null}.
420     */
421    public EnumMap(Map<K, ? extends V> map) {
422        if (map instanceof EnumMap) {
423            @SuppressWarnings("unchecked") EnumMap<K, ? extends V> enumMap =
424                    (EnumMap<K, ? extends V>) map;
425            initialization(enumMap);
426        } else {
427            if (map.isEmpty()) {
428                throw new IllegalArgumentException("map is empty");
429            }
430            Iterator<K> iter = map.keySet().iterator();
431            K enumKey = iter.next();
432            // Confirm the key is actually an enum: Throw ClassCastException if not.
433            Enum.class.cast(enumKey);
434            Class<?> clazz = enumKey.getClass();
435            if (!clazz.isEnum()) {
436                // Each enum value can have its own subclass. In this case we want the abstract
437                // super-class which has the values() method.
438                clazz = clazz.getSuperclass();
439            }
440            @SuppressWarnings("unchecked") Class<K> enumClass = (Class<K>) clazz;
441            initialization(enumClass);
442            putAllImpl(map);
443        }
444    }
445
446    /**
447     * Removes all elements from this {@code EnumMap}, leaving it empty.
448     *
449     * @see #isEmpty()
450     * @see #size()
451     */
452    @Override
453    public void clear() {
454        Arrays.fill(values, null);
455        Arrays.fill(hasMapping, false);
456        mappingsCount = 0;
457    }
458
459    /**
460     * Returns a shallow copy of this {@code EnumMap}.
461     *
462     * @return a shallow copy of this {@code EnumMap}.
463     */
464    @Override
465    public EnumMap<K, V> clone() {
466        try {
467            @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) super.clone();
468            enumMap.initialization(this);
469            return enumMap;
470        } catch (CloneNotSupportedException e) {
471            throw new AssertionError(e);
472        }
473    }
474
475    /**
476     * Returns whether this {@code EnumMap} contains the specified key.
477     *
478     * @param key
479     *            the key to search for.
480     * @return {@code true} if this {@code EnumMap} contains the specified key,
481     *         {@code false} otherwise.
482     */
483    @Override
484    public boolean containsKey(Object key) {
485        if (isValidKeyType(key)) {
486            int keyOrdinal = ((Enum) key).ordinal();
487            return hasMapping[keyOrdinal];
488        }
489        return false;
490    }
491
492    /**
493     * Returns whether this {@code EnumMap} contains the specified value.
494     *
495     * @param value
496     *            the value to search for.
497     * @return {@code true} if this {@code EnumMap} contains the specified value,
498     *         {@code false} otherwise.
499     */
500    @Override
501    public boolean containsValue(Object value) {
502        if (value == null) {
503            for (int i = 0; i < enumSize; i++) {
504                if (hasMapping[i] && values[i] == null) {
505                    return true;
506                }
507            }
508        } else {
509            for (int i = 0; i < enumSize; i++) {
510                if (hasMapping[i] && value.equals(values[i])) {
511                    return true;
512                }
513            }
514        }
515        return false;
516    }
517
518    /**
519     * Returns a {@code Set} containing all of the mappings in this {@code EnumMap}. Each mapping is
520     * an instance of {@link Map.Entry}. As the {@code Set} is backed by this {@code EnumMap},
521     * changes in one will be reflected in the other.
522     * <p>
523     * The order of the entries in the set will be the order that the enum keys
524     * were declared in.
525     *
526     * @return a {@code Set} of the mappings.
527     */
528    @Override
529    public Set<Map.Entry<K, V>> entrySet() {
530        if (entrySet == null) {
531            entrySet = new EnumMapEntrySet<K, V>(this);
532        }
533        return entrySet;
534    }
535
536    /**
537     * Compares the argument to the receiver, and returns {@code true} if the
538     * specified {@code Object} is an {@code EnumMap} and both {@code EnumMap}s contain the same mappings.
539     *
540     * @param object
541     *            the {@code Object} to compare with this {@code EnumMap}.
542     * @return boolean {@code true} if {@code object} is the same as this {@code EnumMap},
543     *         {@code false} otherwise.
544     * @see #hashCode()
545     * @see #entrySet()
546     */
547    @Override
548    public boolean equals(Object object) {
549        if (this == object) {
550            return true;
551        }
552        if (!(object instanceof EnumMap)) {
553            return super.equals(object);
554        }
555        @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
556        if (keyType != enumMap.keyType || size() != enumMap.size()) {
557            return false;
558        }
559        return Arrays.equals(hasMapping, enumMap.hasMapping)
560                && Arrays.equals(values, enumMap.values);
561    }
562
563    /**
564     * Returns the value of the mapping with the specified key.
565     *
566     * @param key
567     *            the key.
568     * @return the value of the mapping with the specified key, or {@code null}
569     *         if no mapping for the specified key is found.
570     */
571    @Override
572    public V get(Object key) {
573        if (!isValidKeyType(key)) {
574            return null;
575        }
576        int keyOrdinal = ((Enum) key).ordinal();
577        return values[keyOrdinal];
578    }
579
580    /**
581     * Returns a set of the keys contained in this {@code EnumMap}. The {@code Set} is backed by
582     * this {@code EnumMap} so changes to one are reflected in the other. The {@code Set} does not
583     * support adding.
584     * <p>
585     * The order of the set will be the order that the enum keys were declared
586     * in.
587     *
588     * @return a {@code Set} of the keys.
589     */
590    @Override
591    public Set<K> keySet() {
592        if (keySet == null) {
593            keySet = new EnumMapKeySet<K, V>(this);
594        }
595        return keySet;
596    }
597
598    /**
599     * Maps the specified key to the specified value.
600     *
601     * @param key
602     *            the key.
603     * @param value
604     *            the value.
605     * @return the value of any previous mapping with the specified key or
606     *         {@code null} if there was no mapping.
607     * @throws UnsupportedOperationException
608     *                if adding to this map is not supported.
609     * @throws ClassCastException
610     *                if the class of the key or value is inappropriate for this
611     *                map.
612     * @throws IllegalArgumentException
613     *                if the key or value cannot be added to this map.
614     * @throws NullPointerException
615     *                if the key or value is {@code null} and this {@code EnumMap} does not
616     *                support {@code null} keys or values.
617     */
618    @Override
619    public V put(K key, V value) {
620        return putImpl(key, value);
621    }
622
623    /**
624     * Copies every mapping in the specified {@code Map} to this {@code EnumMap}.
625     *
626     * @param map
627     *            the {@code Map} to copy mappings from.
628     * @throws UnsupportedOperationException
629     *                if adding to this {@code EnumMap} is not supported.
630     * @throws ClassCastException
631     *                if the class of a key or value is inappropriate for this
632     *                {@code EnumMap}.
633     * @throws IllegalArgumentException
634     *                if a key or value cannot be added to this map.
635     * @throws NullPointerException
636     *                if a key or value is {@code null} and this {@code EnumMap} does not
637     *                support {@code null} keys or values.
638     */
639    @Override
640    public void putAll(Map<? extends K, ? extends V> map) {
641        putAllImpl(map);
642    }
643
644    /**
645     * Removes a mapping with the specified key from this {@code EnumMap}.
646     *
647     * @param key
648     *            the key of the mapping to remove.
649     * @return the value of the removed mapping or {@code null} if no mapping
650     *         for the specified key was found.
651     * @throws UnsupportedOperationException
652     *                if removing from this {@code EnumMap} is not supported.
653     */
654    @Override
655    public V remove(Object key) {
656        if (!isValidKeyType(key)) {
657            return null;
658        }
659        int keyOrdinal = ((Enum) key).ordinal();
660        if (hasMapping[keyOrdinal]) {
661            hasMapping[keyOrdinal] = false;
662            mappingsCount--;
663        }
664        V oldValue = values[keyOrdinal];
665        values[keyOrdinal] = null;
666        return oldValue;
667    }
668
669    /**
670     * Returns the number of elements in this {@code EnumMap}.
671     *
672     * @return the number of elements in this {@code EnumMap}.
673     */
674    @Override
675    public int size() {
676        return mappingsCount;
677    }
678
679    /**
680     * Returns a {@code Collection} of the values contained in this {@code EnumMap}. The returned
681     * {@code Collection} complies with the general rule specified in
682     * {@link Map#values()}. The {@code Collection}'s {@code Iterator} will return the values
683     * in the their corresponding keys' natural order (the {@code Enum} constants are
684     * declared in this order).
685     * <p>
686     * The order of the values in the collection will be the order that their
687     * corresponding enum keys were declared in.
688     *
689     * @return a collection of the values contained in this {@code EnumMap}.
690     */
691    @Override
692    public Collection<V> values() {
693        if (valuesCollection == null) {
694            valuesCollection = new EnumMapValueCollection<K, V>(this);
695        }
696        return valuesCollection;
697    }
698
699    @SuppressWarnings("unchecked")
700    private void readObject(ObjectInputStream stream) throws IOException,
701            ClassNotFoundException {
702        stream.defaultReadObject();
703        initialization(keyType);
704        int elementCount = stream.readInt();
705        K enumKey;
706        V value;
707        for (int i = elementCount; i > 0; i--) {
708            enumKey = (K) stream.readObject();
709            value = (V) stream.readObject();
710            putImpl(enumKey, value);
711        }
712    }
713
714    private void writeObject(ObjectOutputStream stream) throws IOException {
715        stream.defaultWriteObject();
716        stream.writeInt(mappingsCount);
717        for (Map.Entry<K, V> entry : entrySet()) {
718            stream.writeObject(entry.getKey());
719            stream.writeObject(entry.getValue());
720        }
721    }
722
723    private boolean isValidKeyType(Object key) {
724        return key != null && keyType.isInstance(key);
725    }
726
727    private void initialization(EnumMap<K, ? extends V> enumMap) {
728        keyType = enumMap.keyType;
729        keys = enumMap.keys;
730        enumSize = enumMap.enumSize;
731        values = enumMap.values.clone();
732        hasMapping = enumMap.hasMapping.clone();
733        mappingsCount = enumMap.mappingsCount;
734    }
735
736    private void initialization(Class<K> type) {
737        keyType = type;
738        keys = Enum.getSharedConstants(keyType);
739        enumSize = keys.length;
740        // The value array is actually Object[] for speed of creation. It is treated as a V[]
741        // because it is safe to do so and eliminates unchecked warning suppression throughout.
742        @SuppressWarnings("unchecked") V[] valueArray = (V[]) new Object[enumSize];
743        values = valueArray;
744        hasMapping = new boolean[enumSize];
745    }
746
747    private void putAllImpl(Map<? extends K, ? extends V> map) {
748        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
749            putImpl(entry.getKey(), entry.getValue());
750        }
751    }
752
753    private V putImpl(K key, V value) {
754        if (key == null) {
755            throw new NullPointerException("key == null");
756        }
757        keyType.cast(key); // Called to throw ClassCastException.
758        int keyOrdinal = key.ordinal();
759        if (!hasMapping[keyOrdinal]) {
760            hasMapping[keyOrdinal] = true;
761            mappingsCount++;
762        }
763        V oldValue = values[keyOrdinal];
764        values[keyOrdinal] = value;
765        return oldValue;
766    }
767
768}
769