001 /* EnumMap.java - Map where keys are enum constants 002 Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.util; 040 041 import java.io.Serializable; 042 043 /** 044 * @author Tom Tromey (tromey@redhat.com) 045 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 046 * @since 1.5 047 */ 048 049 public class EnumMap<K extends Enum<K>, V> 050 extends AbstractMap<K, V> 051 implements Cloneable, Serializable 052 { 053 private static final long serialVersionUID = 458661240069192865L; 054 055 V[] store; 056 int cardinality; 057 Class<K> enumClass; 058 059 /** 060 * The cache for {@link #entrySet()}. 061 */ 062 transient Set<Map.Entry<K, V>> entries; 063 064 static final Object emptySlot = new Object(); 065 066 public EnumMap(Class<K> keyType) 067 { 068 store = (V[]) new Object[keyType.getEnumConstants().length]; 069 Arrays.fill(store, emptySlot); 070 cardinality = 0; 071 enumClass = keyType; 072 } 073 074 public EnumMap(EnumMap<K, ? extends V> map) 075 { 076 store = (V[]) map.store.clone(); 077 cardinality = map.cardinality; 078 enumClass = map.enumClass; 079 } 080 081 public EnumMap(Map<K, ? extends V> map) 082 { 083 if (map instanceof EnumMap) 084 { 085 EnumMap<K, ? extends V> other = (EnumMap<K, ? extends V>) map; 086 store = (V[]) other.store.clone(); 087 cardinality = other.cardinality; 088 enumClass = other.enumClass; 089 } 090 else 091 { 092 for (K key : map.keySet()) 093 { 094 V value = map.get(key); 095 if (store == null) 096 { 097 enumClass = key.getDeclaringClass(); 098 store = (V[]) new Object[enumClass.getEnumConstants().length]; 099 } 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 V o = store[e.ordinal()]; 144 return o == emptySlot ? null : o; 145 } 146 147 public V put(K key, V value) 148 { 149 int o = key.ordinal(); 150 V result; 151 if (store[o] == emptySlot) 152 { 153 result = null; 154 ++cardinality; 155 } 156 else 157 result = store[o]; 158 store[o] = value; 159 return result; 160 } 161 162 public V remove(Object key) 163 { 164 if (! (key instanceof Enum)) 165 return null; 166 Enum<K> e = (Enum<K>) key; 167 if (e.getDeclaringClass() != enumClass) 168 return null; 169 V result = store[e.ordinal()]; 170 if (result == emptySlot) 171 result = null; 172 else 173 --cardinality; 174 store[e.ordinal()] = (V) emptySlot; 175 return result; 176 } 177 178 public void putAll(Map<? extends K, ? extends V> map) 179 { 180 for (K key : map.keySet()) 181 { 182 V value = map.get(key); 183 184 int o = key.ordinal(); 185 if (store[o] == emptySlot) 186 ++cardinality; 187 store[o] = value; 188 } 189 } 190 191 public void clear() 192 { 193 Arrays.fill(store, emptySlot); 194 cardinality = 0; 195 } 196 197 public Set<K> keySet() 198 { 199 if (keys == null) 200 { 201 keys = new AbstractSet<K>() 202 { 203 public int size() 204 { 205 return cardinality; 206 } 207 208 public Iterator<K> iterator() 209 { 210 return new Iterator<K>() 211 { 212 int count = 0; 213 int index = -1; 214 215 public boolean hasNext() 216 { 217 return count < cardinality; 218 } 219 220 public K next() 221 { 222 ++count; 223 for (++index; store[index] == emptySlot; ++index) 224 ; 225 return enumClass.getEnumConstants()[index]; 226 } 227 228 public void remove() 229 { 230 --cardinality; 231 store[index] = (V) emptySlot; 232 } 233 }; 234 } 235 236 public void clear() 237 { 238 EnumMap.this.clear(); 239 } 240 241 public boolean contains(Object o) 242 { 243 return contains(o); 244 } 245 246 public boolean remove(Object o) 247 { 248 return EnumMap.this.remove(o) != null; 249 } 250 }; 251 } 252 return keys; 253 } 254 255 public Collection<V> values() 256 { 257 if (values == null) 258 { 259 values = new AbstractCollection<V>() 260 { 261 public int size() 262 { 263 return cardinality; 264 } 265 266 public Iterator<V> iterator() 267 { 268 return new Iterator<V>() 269 { 270 int count = 0; 271 int index = -1; 272 273 public boolean hasNext() 274 { 275 return count < cardinality; 276 } 277 278 public V next() 279 { 280 ++count; 281 for (++index; store[index] == emptySlot; ++index) 282 ; 283 return store[index]; 284 } 285 286 public void remove() 287 { 288 --cardinality; 289 store[index] = (V) emptySlot; 290 } 291 }; 292 } 293 294 public void clear() 295 { 296 EnumMap.this.clear(); 297 } 298 }; 299 } 300 return values; 301 } 302 303 public Set<Map.Entry<K, V>> entrySet() 304 { 305 if (entries == null) 306 { 307 entries = new AbstractSet<Map.Entry<K, V>>() 308 { 309 public int size() 310 { 311 return cardinality; 312 } 313 314 public Iterator<Map.Entry<K, V>> iterator() 315 { 316 return new Iterator<Map.Entry<K, V>>() 317 { 318 int count = 0; 319 int index = -1; 320 321 public boolean hasNext() 322 { 323 return count < cardinality; 324 } 325 326 public Map.Entry<K,V> next() 327 { 328 ++count; 329 for (++index; store[index] == emptySlot; ++index) 330 ; 331 // FIXME: we could just return something that 332 // only knows the index. That would be cleaner. 333 return new AbstractMap.SimpleEntry<K, V>(enumClass.getEnumConstants()[index], 334 store[index]) 335 { 336 public V setValue(V newVal) 337 { 338 value = newVal; 339 return put(key, newVal); 340 } 341 }; 342 } 343 344 public void remove() 345 { 346 --cardinality; 347 store[index] = (V) emptySlot; 348 } 349 }; 350 } 351 352 public void clear() 353 { 354 EnumMap.this.clear(); 355 } 356 357 public boolean contains(Object o) 358 { 359 if (! (o instanceof Map.Entry)) 360 return false; 361 Map.Entry<K, V> other = (Map.Entry<K, V>) o; 362 return (containsKey(other.getKey()) 363 && AbstractCollection.equals(get(other.getKey()), 364 other.getValue())); 365 } 366 367 public boolean remove(Object o) 368 { 369 if (! (o instanceof Map.Entry)) 370 return false; 371 Map.Entry<K, V> other = (Map.Entry<K, V>) o; 372 return EnumMap.this.remove(other.getKey()) != null; 373 } 374 }; 375 } 376 return entries; 377 } 378 379 public boolean equals(Object o) 380 { 381 if (! (o instanceof EnumMap)) 382 return false; 383 EnumMap<K, V> other = (EnumMap<K, V>) o; 384 if (other.enumClass != enumClass || other.cardinality != cardinality) 385 return false; 386 return Arrays.equals(store, other.store); 387 } 388 389 public EnumMap<K, V> clone() 390 { 391 EnumMap<K, V> result; 392 try 393 { 394 result = (EnumMap<K, V>) super.clone(); 395 } 396 catch (CloneNotSupportedException ignore) 397 { 398 // Can't happen. 399 result = null; 400 } 401 result.store = (V[]) store.clone(); 402 return result; 403 } 404 405 }