Source for java.util.EnumMap

   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: }