1: /* EnumMap.java - Map where keys are enum constants 2: Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.util; 40: 41: import java.io.Serializable; 42: 43: /** 44: * @author Tom Tromey (tromey@redhat.com) 45: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 46: * @since 1.5 47: */ 48: 49: public class EnumMap<K extends Enum<K>, V> 50: extends AbstractMap<K, V> 51: implements Cloneable, Serializable 52: { 53: private static final long serialVersionUID = 458661240069192865L; 54: 55: V[] store; 56: int cardinality; 57: Class<K> enumClass; 58: 59: /** 60: * The cache for {@link #entrySet()}. 61: */ 62: transient Set<Map.Entry<K, V>> entries; 63: 64: static final Object emptySlot = new Object(); 65: 66: public EnumMap(Class<K> keyType) 67: { 68: store = (V[]) new Object[keyType.getEnumConstants().length]; 69: Arrays.fill(store, emptySlot); 70: cardinality = 0; 71: enumClass = keyType; 72: } 73: 74: public EnumMap(EnumMap<K, ? extends V> map) 75: { 76: store = (V[]) map.store.clone(); 77: cardinality = map.cardinality; 78: enumClass = map.enumClass; 79: } 80: 81: public EnumMap(Map<K, ? extends V> map) 82: { 83: if (map instanceof EnumMap) 84: { 85: EnumMap<K, ? extends V> other = (EnumMap<K, ? extends V>) map; 86: store = (V[]) other.store.clone(); 87: cardinality = other.cardinality; 88: enumClass = other.enumClass; 89: } 90: else 91: { 92: for (K key : map.keySet()) 93: { 94: V value = map.get(key); 95: if (store == null) 96: { 97: enumClass = key.getDeclaringClass(); 98: store = (V[]) new Object[enumClass.getEnumConstants().length]; 99: } 100: int o = key.ordinal(); 101: if (store[o] == emptySlot) 102: ++cardinality; 103: store[o] = value; 104: } 105: // There must be a single element. 106: if (store == null) 107: throw new IllegalArgumentException("no elements in map"); 108: } 109: } 110: 111: public int size() 112: { 113: return cardinality; 114: } 115: 116: public boolean containsValue(Object value) 117: { 118: for (V i : store) 119: { 120: if (i != emptySlot && AbstractCollection.equals(i , value)) 121: return true; 122: } 123: return false; 124: } 125: 126: public boolean containsKey(Object key) 127: { 128: if (! (key instanceof Enum)) 129: return false; 130: Enum<K> e = (Enum<K>) key; 131: if (e.getDeclaringClass() != enumClass) 132: return false; 133: return store[e.ordinal()] != emptySlot; 134: } 135: 136: public V get(Object key) 137: { 138: if (! (key instanceof Enum)) 139: return null; 140: Enum<K> e = (Enum<K>) key; 141: if (e.getDeclaringClass() != enumClass) 142: return null; 143: return store[e.ordinal()]; 144: } 145: 146: public V put(K key, V value) 147: { 148: int o = key.ordinal(); 149: V result; 150: if (store[o] == emptySlot) 151: { 152: result = null; 153: ++cardinality; 154: } 155: else 156: result = store[o]; 157: store[o] = value; 158: return result; 159: } 160: 161: public V remove(Object key) 162: { 163: if (! (key instanceof Enum)) 164: return null; 165: Enum<K> e = (Enum<K>) key; 166: if (e.getDeclaringClass() != enumClass) 167: return null; 168: V result = store[e.ordinal()]; 169: if (result == emptySlot) 170: result = null; 171: else 172: --cardinality; 173: store[e.ordinal()] = (V) emptySlot; 174: return result; 175: } 176: 177: public void putAll(Map<? extends K, ? extends V> map) 178: { 179: for (K key : map.keySet()) 180: { 181: V value = map.get(key); 182: 183: int o = key.ordinal(); 184: if (store[o] == emptySlot) 185: ++cardinality; 186: store[o] = value; 187: } 188: } 189: 190: public void clear() 191: { 192: Arrays.fill(store, emptySlot); 193: cardinality = 0; 194: } 195: 196: public Set<K> keySet() 197: { 198: if (keys == null) 199: { 200: keys = new AbstractSet<K>() 201: { 202: public int size() 203: { 204: return cardinality; 205: } 206: 207: public Iterator<K> iterator() 208: { 209: return new Iterator<K>() 210: { 211: int count = 0; 212: int index = -1; 213: 214: public boolean hasNext() 215: { 216: return count < cardinality; 217: } 218: 219: public K next() 220: { 221: ++count; 222: for (++index; store[index] == emptySlot; ++index) 223: ; 224: return enumClass.getEnumConstants()[index]; 225: } 226: 227: public void remove() 228: { 229: --cardinality; 230: store[index] = (V) emptySlot; 231: } 232: }; 233: } 234: 235: public void clear() 236: { 237: EnumMap.this.clear(); 238: } 239: 240: public boolean contains(Object o) 241: { 242: return contains(o); 243: } 244: 245: public boolean remove(Object o) 246: { 247: return EnumMap.this.remove(o) != null; 248: } 249: }; 250: } 251: return keys; 252: } 253: 254: public Collection<V> values() 255: { 256: if (values == null) 257: { 258: values = new AbstractCollection<V>() 259: { 260: public int size() 261: { 262: return cardinality; 263: } 264: 265: public Iterator<V> iterator() 266: { 267: return new Iterator<V>() 268: { 269: int count = 0; 270: int index = -1; 271: 272: public boolean hasNext() 273: { 274: return count < cardinality; 275: } 276: 277: public V next() 278: { 279: ++count; 280: for (++index; store[index] == emptySlot; ++index) 281: ; 282: return store[index]; 283: } 284: 285: public void remove() 286: { 287: --cardinality; 288: store[index] = (V) emptySlot; 289: } 290: }; 291: } 292: 293: public void clear() 294: { 295: EnumMap.this.clear(); 296: } 297: }; 298: } 299: return values; 300: } 301: 302: public Set<Map.Entry<K, V>> entrySet() 303: { 304: if (entries == null) 305: { 306: entries = new AbstractSet<Map.Entry<K, V>>() 307: { 308: public int size() 309: { 310: return cardinality; 311: } 312: 313: public Iterator<Map.Entry<K, V>> iterator() 314: { 315: return new Iterator<Map.Entry<K, V>>() 316: { 317: int count = 0; 318: int index = -1; 319: 320: public boolean hasNext() 321: { 322: return count < cardinality; 323: } 324: 325: public Map.Entry<K,V> next() 326: { 327: ++count; 328: for (++index; store[index] == emptySlot; ++index) 329: ; 330: // FIXME: we could just return something that 331: // only knows the index. That would be cleaner. 332: return new AbstractMap.SimpleEntry<K, V>(enumClass.getEnumConstants()[index], 333: store[index]) 334: { 335: public V setValue(V newVal) 336: { 337: value = newVal; 338: return put(key, newVal); 339: } 340: }; 341: } 342: 343: public void remove() 344: { 345: --cardinality; 346: store[index] = (V) emptySlot; 347: } 348: }; 349: } 350: 351: public void clear() 352: { 353: EnumMap.this.clear(); 354: } 355: 356: public boolean contains(Object o) 357: { 358: if (! (o instanceof Map.Entry)) 359: return false; 360: Map.Entry<K, V> other = (Map.Entry<K, V>) o; 361: return (containsKey(other.getKey()) 362: && AbstractCollection.equals(get(other.getKey()), 363: other.getValue())); 364: } 365: 366: public boolean remove(Object o) 367: { 368: if (! (o instanceof Map.Entry)) 369: return false; 370: Map.Entry<K, V> other = (Map.Entry<K, V>) o; 371: return EnumMap.this.remove(other.getKey()) != null; 372: } 373: }; 374: } 375: return entries; 376: } 377: 378: public boolean equals(Object o) 379: { 380: if (! (o instanceof EnumMap)) 381: return false; 382: EnumMap<K, V> other = (EnumMap<K, V>) o; 383: if (other.enumClass != enumClass || other.cardinality != cardinality) 384: return false; 385: return Arrays.equals(store, other.store); 386: } 387: 388: public EnumMap<K, V> clone() 389: { 390: /* This constructor provides this functionality */ 391: return new EnumMap(this); 392: } 393: 394: }