1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/emul/compact/src/main/java/java/util/EnumMap.java Sun Sep 22 21:49:42 2013 +0200
1.3 @@ -0,0 +1,799 @@
1.4 +/*
1.5 + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util;
1.30 +
1.31 +import java.util.Map.Entry;
1.32 +import sun.misc.SharedSecrets;
1.33 +
1.34 +/**
1.35 + * A specialized {@link Map} implementation for use with enum type keys. All
1.36 + * of the keys in an enum map must come from a single enum type that is
1.37 + * specified, explicitly or implicitly, when the map is created. Enum maps
1.38 + * are represented internally as arrays. This representation is extremely
1.39 + * compact and efficient.
1.40 + *
1.41 + * <p>Enum maps are maintained in the <i>natural order</i> of their keys
1.42 + * (the order in which the enum constants are declared). This is reflected
1.43 + * in the iterators returned by the collections views ({@link #keySet()},
1.44 + * {@link #entrySet()}, and {@link #values()}).
1.45 + *
1.46 + * <p>Iterators returned by the collection views are <i>weakly consistent</i>:
1.47 + * they will never throw {@link ConcurrentModificationException} and they may
1.48 + * or may not show the effects of any modifications to the map that occur while
1.49 + * the iteration is in progress.
1.50 + *
1.51 + * <p>Null keys are not permitted. Attempts to insert a null key will
1.52 + * throw {@link NullPointerException}. Attempts to test for the
1.53 + * presence of a null key or to remove one will, however, function properly.
1.54 + * Null values are permitted.
1.55 +
1.56 + * <P>Like most collection implementations <tt>EnumMap</tt> is not
1.57 + * synchronized. If multiple threads access an enum map concurrently, and at
1.58 + * least one of the threads modifies the map, it should be synchronized
1.59 + * externally. This is typically accomplished by synchronizing on some
1.60 + * object that naturally encapsulates the enum map. If no such object exists,
1.61 + * the map should be "wrapped" using the {@link Collections#synchronizedMap}
1.62 + * method. This is best done at creation time, to prevent accidental
1.63 + * unsynchronized access:
1.64 + *
1.65 + * <pre>
1.66 + * Map<EnumKey, V> m
1.67 + * = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...));
1.68 + * </pre>
1.69 + *
1.70 + * <p>Implementation note: All basic operations execute in constant time.
1.71 + * They are likely (though not guaranteed) to be faster than their
1.72 + * {@link HashMap} counterparts.
1.73 + *
1.74 + * <p>This class is a member of the
1.75 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
1.76 + * Java Collections Framework</a>.
1.77 + *
1.78 + * @author Josh Bloch
1.79 + * @see EnumSet
1.80 + * @since 1.5
1.81 + */
1.82 +public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
1.83 + implements java.io.Serializable, Cloneable
1.84 +{
1.85 + /**
1.86 + * The <tt>Class</tt> object for the enum type of all the keys of this map.
1.87 + *
1.88 + * @serial
1.89 + */
1.90 + private final Class<K> keyType;
1.91 +
1.92 + /**
1.93 + * All of the values comprising K. (Cached for performance.)
1.94 + */
1.95 + private transient K[] keyUniverse;
1.96 +
1.97 + /**
1.98 + * Array representation of this map. The ith element is the value
1.99 + * to which universe[i] is currently mapped, or null if it isn't
1.100 + * mapped to anything, or NULL if it's mapped to null.
1.101 + */
1.102 + private transient Object[] vals;
1.103 +
1.104 + /**
1.105 + * The number of mappings in this map.
1.106 + */
1.107 + private transient int size = 0;
1.108 +
1.109 + /**
1.110 + * Distinguished non-null value for representing null values.
1.111 + */
1.112 + private static final Object NULL = new Integer(0);
1.113 +
1.114 + private Object maskNull(Object value) {
1.115 + return (value == null ? NULL : value);
1.116 + }
1.117 +
1.118 + private V unmaskNull(Object value) {
1.119 + return (V) (value == NULL ? null : value);
1.120 + }
1.121 +
1.122 + private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
1.123 +
1.124 + /**
1.125 + * Creates an empty enum map with the specified key type.
1.126 + *
1.127 + * @param keyType the class object of the key type for this enum map
1.128 + * @throws NullPointerException if <tt>keyType</tt> is null
1.129 + */
1.130 + public EnumMap(Class<K> keyType) {
1.131 + this.keyType = keyType;
1.132 + keyUniverse = getKeyUniverse(keyType);
1.133 + vals = new Object[keyUniverse.length];
1.134 + }
1.135 +
1.136 + /**
1.137 + * Creates an enum map with the same key type as the specified enum
1.138 + * map, initially containing the same mappings (if any).
1.139 + *
1.140 + * @param m the enum map from which to initialize this enum map
1.141 + * @throws NullPointerException if <tt>m</tt> is null
1.142 + */
1.143 + public EnumMap(EnumMap<K, ? extends V> m) {
1.144 + keyType = m.keyType;
1.145 + keyUniverse = m.keyUniverse;
1.146 + vals = m.vals.clone();
1.147 + size = m.size;
1.148 + }
1.149 +
1.150 + /**
1.151 + * Creates an enum map initialized from the specified map. If the
1.152 + * specified map is an <tt>EnumMap</tt> instance, this constructor behaves
1.153 + * identically to {@link #EnumMap(EnumMap)}. Otherwise, the specified map
1.154 + * must contain at least one mapping (in order to determine the new
1.155 + * enum map's key type).
1.156 + *
1.157 + * @param m the map from which to initialize this enum map
1.158 + * @throws IllegalArgumentException if <tt>m</tt> is not an
1.159 + * <tt>EnumMap</tt> instance and contains no mappings
1.160 + * @throws NullPointerException if <tt>m</tt> is null
1.161 + */
1.162 + public EnumMap(Map<K, ? extends V> m) {
1.163 + if (m instanceof EnumMap) {
1.164 + EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
1.165 + keyType = em.keyType;
1.166 + keyUniverse = em.keyUniverse;
1.167 + vals = em.vals.clone();
1.168 + size = em.size;
1.169 + } else {
1.170 + if (m.isEmpty())
1.171 + throw new IllegalArgumentException("Specified map is empty");
1.172 + keyType = m.keySet().iterator().next().getDeclaringClass();
1.173 + keyUniverse = getKeyUniverse(keyType);
1.174 + vals = new Object[keyUniverse.length];
1.175 + putAll(m);
1.176 + }
1.177 + }
1.178 +
1.179 + // Query Operations
1.180 +
1.181 + /**
1.182 + * Returns the number of key-value mappings in this map.
1.183 + *
1.184 + * @return the number of key-value mappings in this map
1.185 + */
1.186 + public int size() {
1.187 + return size;
1.188 + }
1.189 +
1.190 + /**
1.191 + * Returns <tt>true</tt> if this map maps one or more keys to the
1.192 + * specified value.
1.193 + *
1.194 + * @param value the value whose presence in this map is to be tested
1.195 + * @return <tt>true</tt> if this map maps one or more keys to this value
1.196 + */
1.197 + public boolean containsValue(Object value) {
1.198 + value = maskNull(value);
1.199 +
1.200 + for (Object val : vals)
1.201 + if (value.equals(val))
1.202 + return true;
1.203 +
1.204 + return false;
1.205 + }
1.206 +
1.207 + /**
1.208 + * Returns <tt>true</tt> if this map contains a mapping for the specified
1.209 + * key.
1.210 + *
1.211 + * @param key the key whose presence in this map is to be tested
1.212 + * @return <tt>true</tt> if this map contains a mapping for the specified
1.213 + * key
1.214 + */
1.215 + public boolean containsKey(Object key) {
1.216 + return isValidKey(key) && vals[((Enum)key).ordinal()] != null;
1.217 + }
1.218 +
1.219 + private boolean containsMapping(Object key, Object value) {
1.220 + return isValidKey(key) &&
1.221 + maskNull(value).equals(vals[((Enum)key).ordinal()]);
1.222 + }
1.223 +
1.224 + /**
1.225 + * Returns the value to which the specified key is mapped,
1.226 + * or {@code null} if this map contains no mapping for the key.
1.227 + *
1.228 + * <p>More formally, if this map contains a mapping from a key
1.229 + * {@code k} to a value {@code v} such that {@code (key == k)},
1.230 + * then this method returns {@code v}; otherwise it returns
1.231 + * {@code null}. (There can be at most one such mapping.)
1.232 + *
1.233 + * <p>A return value of {@code null} does not <i>necessarily</i>
1.234 + * indicate that the map contains no mapping for the key; it's also
1.235 + * possible that the map explicitly maps the key to {@code null}.
1.236 + * The {@link #containsKey containsKey} operation may be used to
1.237 + * distinguish these two cases.
1.238 + */
1.239 + public V get(Object key) {
1.240 + return (isValidKey(key) ?
1.241 + unmaskNull(vals[((Enum)key).ordinal()]) : null);
1.242 + }
1.243 +
1.244 + // Modification Operations
1.245 +
1.246 + /**
1.247 + * Associates the specified value with the specified key in this map.
1.248 + * If the map previously contained a mapping for this key, the old
1.249 + * value is replaced.
1.250 + *
1.251 + * @param key the key with which the specified value is to be associated
1.252 + * @param value the value to be associated with the specified key
1.253 + *
1.254 + * @return the previous value associated with specified key, or
1.255 + * <tt>null</tt> if there was no mapping for key. (A <tt>null</tt>
1.256 + * return can also indicate that the map previously associated
1.257 + * <tt>null</tt> with the specified key.)
1.258 + * @throws NullPointerException if the specified key is null
1.259 + */
1.260 + public V put(K key, V value) {
1.261 + typeCheck(key);
1.262 +
1.263 + int index = key.ordinal();
1.264 + Object oldValue = vals[index];
1.265 + vals[index] = maskNull(value);
1.266 + if (oldValue == null)
1.267 + size++;
1.268 + return unmaskNull(oldValue);
1.269 + }
1.270 +
1.271 + /**
1.272 + * Removes the mapping for this key from this map if present.
1.273 + *
1.274 + * @param key the key whose mapping is to be removed from the map
1.275 + * @return the previous value associated with specified key, or
1.276 + * <tt>null</tt> if there was no entry for key. (A <tt>null</tt>
1.277 + * return can also indicate that the map previously associated
1.278 + * <tt>null</tt> with the specified key.)
1.279 + */
1.280 + public V remove(Object key) {
1.281 + if (!isValidKey(key))
1.282 + return null;
1.283 + int index = ((Enum)key).ordinal();
1.284 + Object oldValue = vals[index];
1.285 + vals[index] = null;
1.286 + if (oldValue != null)
1.287 + size--;
1.288 + return unmaskNull(oldValue);
1.289 + }
1.290 +
1.291 + private boolean removeMapping(Object key, Object value) {
1.292 + if (!isValidKey(key))
1.293 + return false;
1.294 + int index = ((Enum)key).ordinal();
1.295 + if (maskNull(value).equals(vals[index])) {
1.296 + vals[index] = null;
1.297 + size--;
1.298 + return true;
1.299 + }
1.300 + return false;
1.301 + }
1.302 +
1.303 + /**
1.304 + * Returns true if key is of the proper type to be a key in this
1.305 + * enum map.
1.306 + */
1.307 + private boolean isValidKey(Object key) {
1.308 + if (key == null)
1.309 + return false;
1.310 +
1.311 + // Cheaper than instanceof Enum followed by getDeclaringClass
1.312 + Class keyClass = key.getClass();
1.313 + return keyClass == keyType || keyClass.getSuperclass() == keyType;
1.314 + }
1.315 +
1.316 + // Bulk Operations
1.317 +
1.318 + /**
1.319 + * Copies all of the mappings from the specified map to this map.
1.320 + * These mappings will replace any mappings that this map had for
1.321 + * any of the keys currently in the specified map.
1.322 + *
1.323 + * @param m the mappings to be stored in this map
1.324 + * @throws NullPointerException the specified map is null, or if
1.325 + * one or more keys in the specified map are null
1.326 + */
1.327 + public void putAll(Map<? extends K, ? extends V> m) {
1.328 + if (m instanceof EnumMap) {
1.329 + EnumMap<? extends K, ? extends V> em =
1.330 + (EnumMap<? extends K, ? extends V>)m;
1.331 + if (em.keyType != keyType) {
1.332 + if (em.isEmpty())
1.333 + return;
1.334 + throw new ClassCastException(em.keyType + " != " + keyType);
1.335 + }
1.336 +
1.337 + for (int i = 0; i < keyUniverse.length; i++) {
1.338 + Object emValue = em.vals[i];
1.339 + if (emValue != null) {
1.340 + if (vals[i] == null)
1.341 + size++;
1.342 + vals[i] = emValue;
1.343 + }
1.344 + }
1.345 + } else {
1.346 + super.putAll(m);
1.347 + }
1.348 + }
1.349 +
1.350 + /**
1.351 + * Removes all mappings from this map.
1.352 + */
1.353 + public void clear() {
1.354 + Arrays.fill(vals, null);
1.355 + size = 0;
1.356 + }
1.357 +
1.358 + // Views
1.359 +
1.360 + /**
1.361 + * This field is initialized to contain an instance of the entry set
1.362 + * view the first time this view is requested. The view is stateless,
1.363 + * so there's no reason to create more than one.
1.364 + */
1.365 + private transient Set<Map.Entry<K,V>> entrySet = null;
1.366 +
1.367 + /**
1.368 + * Returns a {@link Set} view of the keys contained in this map.
1.369 + * The returned set obeys the general contract outlined in
1.370 + * {@link Map#keySet()}. The set's iterator will return the keys
1.371 + * in their natural order (the order in which the enum constants
1.372 + * are declared).
1.373 + *
1.374 + * @return a set view of the keys contained in this enum map
1.375 + */
1.376 + public Set<K> keySet() {
1.377 + Set<K> ks = keySet;
1.378 + if (ks != null)
1.379 + return ks;
1.380 + else
1.381 + return keySet = new KeySet();
1.382 + }
1.383 +
1.384 + private class KeySet extends AbstractSet<K> {
1.385 + public Iterator<K> iterator() {
1.386 + return new KeyIterator();
1.387 + }
1.388 + public int size() {
1.389 + return size;
1.390 + }
1.391 + public boolean contains(Object o) {
1.392 + return containsKey(o);
1.393 + }
1.394 + public boolean remove(Object o) {
1.395 + int oldSize = size;
1.396 + EnumMap.this.remove(o);
1.397 + return size != oldSize;
1.398 + }
1.399 + public void clear() {
1.400 + EnumMap.this.clear();
1.401 + }
1.402 + }
1.403 +
1.404 + /**
1.405 + * Returns a {@link Collection} view of the values contained in this map.
1.406 + * The returned collection obeys the general contract outlined in
1.407 + * {@link Map#values()}. The collection's iterator will return the
1.408 + * values in the order their corresponding keys appear in map,
1.409 + * which is their natural order (the order in which the enum constants
1.410 + * are declared).
1.411 + *
1.412 + * @return a collection view of the values contained in this map
1.413 + */
1.414 + public Collection<V> values() {
1.415 + Collection<V> vs = values;
1.416 + if (vs != null)
1.417 + return vs;
1.418 + else
1.419 + return values = new Values();
1.420 + }
1.421 +
1.422 + private class Values extends AbstractCollection<V> {
1.423 + public Iterator<V> iterator() {
1.424 + return new ValueIterator();
1.425 + }
1.426 + public int size() {
1.427 + return size;
1.428 + }
1.429 + public boolean contains(Object o) {
1.430 + return containsValue(o);
1.431 + }
1.432 + public boolean remove(Object o) {
1.433 + o = maskNull(o);
1.434 +
1.435 + for (int i = 0; i < vals.length; i++) {
1.436 + if (o.equals(vals[i])) {
1.437 + vals[i] = null;
1.438 + size--;
1.439 + return true;
1.440 + }
1.441 + }
1.442 + return false;
1.443 + }
1.444 + public void clear() {
1.445 + EnumMap.this.clear();
1.446 + }
1.447 + }
1.448 +
1.449 + /**
1.450 + * Returns a {@link Set} view of the mappings contained in this map.
1.451 + * The returned set obeys the general contract outlined in
1.452 + * {@link Map#keySet()}. The set's iterator will return the
1.453 + * mappings in the order their keys appear in map, which is their
1.454 + * natural order (the order in which the enum constants are declared).
1.455 + *
1.456 + * @return a set view of the mappings contained in this enum map
1.457 + */
1.458 + public Set<Map.Entry<K,V>> entrySet() {
1.459 + Set<Map.Entry<K,V>> es = entrySet;
1.460 + if (es != null)
1.461 + return es;
1.462 + else
1.463 + return entrySet = new EntrySet();
1.464 + }
1.465 +
1.466 + private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
1.467 + public Iterator<Map.Entry<K,V>> iterator() {
1.468 + return new EntryIterator();
1.469 + }
1.470 +
1.471 + public boolean contains(Object o) {
1.472 + if (!(o instanceof Map.Entry))
1.473 + return false;
1.474 + Map.Entry entry = (Map.Entry)o;
1.475 + return containsMapping(entry.getKey(), entry.getValue());
1.476 + }
1.477 + public boolean remove(Object o) {
1.478 + if (!(o instanceof Map.Entry))
1.479 + return false;
1.480 + Map.Entry entry = (Map.Entry)o;
1.481 + return removeMapping(entry.getKey(), entry.getValue());
1.482 + }
1.483 + public int size() {
1.484 + return size;
1.485 + }
1.486 + public void clear() {
1.487 + EnumMap.this.clear();
1.488 + }
1.489 + public Object[] toArray() {
1.490 + return fillEntryArray(new Object[size]);
1.491 + }
1.492 + @SuppressWarnings("unchecked")
1.493 + public <T> T[] toArray(T[] a) {
1.494 + int size = size();
1.495 + if (a.length < size)
1.496 + a = (T[])java.lang.reflect.Array
1.497 + .newInstance(a.getClass().getComponentType(), size);
1.498 + if (a.length > size)
1.499 + a[size] = null;
1.500 + return (T[]) fillEntryArray(a);
1.501 + }
1.502 + private Object[] fillEntryArray(Object[] a) {
1.503 + int j = 0;
1.504 + for (int i = 0; i < vals.length; i++)
1.505 + if (vals[i] != null)
1.506 + a[j++] = new AbstractMap.SimpleEntry<>(
1.507 + keyUniverse[i], unmaskNull(vals[i]));
1.508 + return a;
1.509 + }
1.510 + }
1.511 +
1.512 + private abstract class EnumMapIterator<T> implements Iterator<T> {
1.513 + // Lower bound on index of next element to return
1.514 + int index = 0;
1.515 +
1.516 + // Index of last returned element, or -1 if none
1.517 + int lastReturnedIndex = -1;
1.518 +
1.519 + public boolean hasNext() {
1.520 + while (index < vals.length && vals[index] == null)
1.521 + index++;
1.522 + return index != vals.length;
1.523 + }
1.524 +
1.525 + public void remove() {
1.526 + checkLastReturnedIndex();
1.527 +
1.528 + if (vals[lastReturnedIndex] != null) {
1.529 + vals[lastReturnedIndex] = null;
1.530 + size--;
1.531 + }
1.532 + lastReturnedIndex = -1;
1.533 + }
1.534 +
1.535 + private void checkLastReturnedIndex() {
1.536 + if (lastReturnedIndex < 0)
1.537 + throw new IllegalStateException();
1.538 + }
1.539 + }
1.540 +
1.541 + private class KeyIterator extends EnumMapIterator<K> {
1.542 + public K next() {
1.543 + if (!hasNext())
1.544 + throw new NoSuchElementException();
1.545 + lastReturnedIndex = index++;
1.546 + return keyUniverse[lastReturnedIndex];
1.547 + }
1.548 + }
1.549 +
1.550 + private class ValueIterator extends EnumMapIterator<V> {
1.551 + public V next() {
1.552 + if (!hasNext())
1.553 + throw new NoSuchElementException();
1.554 + lastReturnedIndex = index++;
1.555 + return unmaskNull(vals[lastReturnedIndex]);
1.556 + }
1.557 + }
1.558 +
1.559 + private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
1.560 + private Entry lastReturnedEntry = null;
1.561 +
1.562 + public Map.Entry<K,V> next() {
1.563 + if (!hasNext())
1.564 + throw new NoSuchElementException();
1.565 + lastReturnedEntry = new Entry(index++);
1.566 + return lastReturnedEntry;
1.567 + }
1.568 +
1.569 + public void remove() {
1.570 + lastReturnedIndex =
1.571 + ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
1.572 + super.remove();
1.573 + lastReturnedEntry.index = lastReturnedIndex;
1.574 + lastReturnedEntry = null;
1.575 + }
1.576 +
1.577 + private class Entry implements Map.Entry<K,V> {
1.578 + private int index;
1.579 +
1.580 + private Entry(int index) {
1.581 + this.index = index;
1.582 + }
1.583 +
1.584 + public K getKey() {
1.585 + checkIndexForEntryUse();
1.586 + return keyUniverse[index];
1.587 + }
1.588 +
1.589 + public V getValue() {
1.590 + checkIndexForEntryUse();
1.591 + return unmaskNull(vals[index]);
1.592 + }
1.593 +
1.594 + public V setValue(V value) {
1.595 + checkIndexForEntryUse();
1.596 + V oldValue = unmaskNull(vals[index]);
1.597 + vals[index] = maskNull(value);
1.598 + return oldValue;
1.599 + }
1.600 +
1.601 + public boolean equals(Object o) {
1.602 + if (index < 0)
1.603 + return o == this;
1.604 +
1.605 + if (!(o instanceof Map.Entry))
1.606 + return false;
1.607 +
1.608 + Map.Entry e = (Map.Entry)o;
1.609 + V ourValue = unmaskNull(vals[index]);
1.610 + Object hisValue = e.getValue();
1.611 + return (e.getKey() == keyUniverse[index] &&
1.612 + (ourValue == hisValue ||
1.613 + (ourValue != null && ourValue.equals(hisValue))));
1.614 + }
1.615 +
1.616 + public int hashCode() {
1.617 + if (index < 0)
1.618 + return super.hashCode();
1.619 +
1.620 + return entryHashCode(index);
1.621 + }
1.622 +
1.623 + public String toString() {
1.624 + if (index < 0)
1.625 + return super.toString();
1.626 +
1.627 + return keyUniverse[index] + "="
1.628 + + unmaskNull(vals[index]);
1.629 + }
1.630 +
1.631 + private void checkIndexForEntryUse() {
1.632 + if (index < 0)
1.633 + throw new IllegalStateException("Entry was removed");
1.634 + }
1.635 + }
1.636 + }
1.637 +
1.638 + // Comparison and hashing
1.639 +
1.640 + /**
1.641 + * Compares the specified object with this map for equality. Returns
1.642 + * <tt>true</tt> if the given object is also a map and the two maps
1.643 + * represent the same mappings, as specified in the {@link
1.644 + * Map#equals(Object)} contract.
1.645 + *
1.646 + * @param o the object to be compared for equality with this map
1.647 + * @return <tt>true</tt> if the specified object is equal to this map
1.648 + */
1.649 + public boolean equals(Object o) {
1.650 + if (this == o)
1.651 + return true;
1.652 + if (o instanceof EnumMap)
1.653 + return equals((EnumMap)o);
1.654 + if (!(o instanceof Map))
1.655 + return false;
1.656 +
1.657 + Map<K,V> m = (Map<K,V>)o;
1.658 + if (size != m.size())
1.659 + return false;
1.660 +
1.661 + for (int i = 0; i < keyUniverse.length; i++) {
1.662 + if (null != vals[i]) {
1.663 + K key = keyUniverse[i];
1.664 + V value = unmaskNull(vals[i]);
1.665 + if (null == value) {
1.666 + if (!((null == m.get(key)) && m.containsKey(key)))
1.667 + return false;
1.668 + } else {
1.669 + if (!value.equals(m.get(key)))
1.670 + return false;
1.671 + }
1.672 + }
1.673 + }
1.674 +
1.675 + return true;
1.676 + }
1.677 +
1.678 + private boolean equals(EnumMap em) {
1.679 + if (em.keyType != keyType)
1.680 + return size == 0 && em.size == 0;
1.681 +
1.682 + // Key types match, compare each value
1.683 + for (int i = 0; i < keyUniverse.length; i++) {
1.684 + Object ourValue = vals[i];
1.685 + Object hisValue = em.vals[i];
1.686 + if (hisValue != ourValue &&
1.687 + (hisValue == null || !hisValue.equals(ourValue)))
1.688 + return false;
1.689 + }
1.690 + return true;
1.691 + }
1.692 +
1.693 + /**
1.694 + * Returns the hash code value for this map. The hash code of a map is
1.695 + * defined to be the sum of the hash codes of each entry in the map.
1.696 + */
1.697 + public int hashCode() {
1.698 + int h = 0;
1.699 +
1.700 + for (int i = 0; i < keyUniverse.length; i++) {
1.701 + if (null != vals[i]) {
1.702 + h += entryHashCode(i);
1.703 + }
1.704 + }
1.705 +
1.706 + return h;
1.707 + }
1.708 +
1.709 + private int entryHashCode(int index) {
1.710 + return (keyUniverse[index].hashCode() ^ vals[index].hashCode());
1.711 + }
1.712 +
1.713 + /**
1.714 + * Returns a shallow copy of this enum map. (The values themselves
1.715 + * are not cloned.
1.716 + *
1.717 + * @return a shallow copy of this enum map
1.718 + */
1.719 + public EnumMap<K, V> clone() {
1.720 + EnumMap<K, V> result = null;
1.721 + try {
1.722 + result = (EnumMap<K, V>) super.clone();
1.723 + } catch(CloneNotSupportedException e) {
1.724 + throw new AssertionError();
1.725 + }
1.726 + result.vals = result.vals.clone();
1.727 + return result;
1.728 + }
1.729 +
1.730 + /**
1.731 + * Throws an exception if e is not of the correct type for this enum set.
1.732 + */
1.733 + private void typeCheck(K key) {
1.734 + Class keyClass = key.getClass();
1.735 + if (keyClass != keyType && keyClass.getSuperclass() != keyType)
1.736 + throw new ClassCastException(keyClass + " != " + keyType);
1.737 + }
1.738 +
1.739 + /**
1.740 + * Returns all of the values comprising K.
1.741 + * The result is uncloned, cached, and shared by all callers.
1.742 + */
1.743 + private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
1.744 + return SharedSecrets.getJavaLangAccess()
1.745 + .getEnumConstantsShared(keyType);
1.746 + }
1.747 +
1.748 + private static final long serialVersionUID = 458661240069192865L;
1.749 +
1.750 + /**
1.751 + * Save the state of the <tt>EnumMap</tt> instance to a stream (i.e.,
1.752 + * serialize it).
1.753 + *
1.754 + * @serialData The <i>size</i> of the enum map (the number of key-value
1.755 + * mappings) is emitted (int), followed by the key (Object)
1.756 + * and value (Object) for each key-value mapping represented
1.757 + * by the enum map.
1.758 + */
1.759 + private void writeObject(java.io.ObjectOutputStream s)
1.760 + throws java.io.IOException
1.761 + {
1.762 + // Write out the key type and any hidden stuff
1.763 + s.defaultWriteObject();
1.764 +
1.765 + // Write out size (number of Mappings)
1.766 + s.writeInt(size);
1.767 +
1.768 + // Write out keys and values (alternating)
1.769 + int entriesToBeWritten = size;
1.770 + for (int i = 0; entriesToBeWritten > 0; i++) {
1.771 + if (null != vals[i]) {
1.772 + s.writeObject(keyUniverse[i]);
1.773 + s.writeObject(unmaskNull(vals[i]));
1.774 + entriesToBeWritten--;
1.775 + }
1.776 + }
1.777 + }
1.778 +
1.779 + /**
1.780 + * Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e.,
1.781 + * deserialize it).
1.782 + */
1.783 + private void readObject(java.io.ObjectInputStream s)
1.784 + throws java.io.IOException, ClassNotFoundException
1.785 + {
1.786 + // Read in the key type and any hidden stuff
1.787 + s.defaultReadObject();
1.788 +
1.789 + keyUniverse = getKeyUniverse(keyType);
1.790 + vals = new Object[keyUniverse.length];
1.791 +
1.792 + // Read in size (number of Mappings)
1.793 + int size = s.readInt();
1.794 +
1.795 + // Read the keys and values, and put the mappings in the HashMap
1.796 + for (int i = 0; i < size; i++) {
1.797 + K key = (K) s.readObject();
1.798 + V value = (V) s.readObject();
1.799 + put(key, value);
1.800 + }
1.801 + }
1.802 +}