EnumSet.java 15.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/* EnumSet.java - Set of enum objects
   Copyright (C) 2004, 2005 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.util;

import java.io.Serializable;

/**
44 45 46 47 48 49 50 51 52 53 54
 * <p>
 * Provides an efficient mechanism for recording a set of enumeration
 * constants.  As enumerations have a known set of possible values, certain
 * assumptions can be made when creating a set of constants.  The maximum
 * size of the set will always be equal to the number of constants, and each
 * value will always be one of these constants.  As a result, the set only needs
 * to store whether a particular constant is present or not rather than the
 * values themselves.  Each constant can thus be represented by a single bit.
 * </p>
 * <p>
 * This class is designed to provide an alternative to using integer bit flags
55
 * by providing a typesafe {@link Collection} interface with an underlying
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
 * implementation that utilises the assumptions above to give an equivalent level
 * of efficiency.  The values in a {@link EnumSet} must all be from the same
 * {@link Enum} type, which allows the contents to be packed into a bit vector.
 * A containment test is then simply a matter of inspecting the appropriate bit, while
 * addition involves setting the same.  Such basic operations take place in constant
 * time.
 * </p>
 * <p>
 * The {@link Iterator} implementation traverses the values in the natural order
 * of the enumeration provided by each constant's {@link Enum#ordinal()}.  It is
 * <emph>weakly consistent</emph> and will not throw a {@link ConcurrentModificationException}.
 * This means that concurrent changes to the set may or may not be noticeable during
 * traversal.
 * </p>
 * <p>
 * As is usual with most collections, the set is not synchronized by default.  This
 * can be remedied by using the {@link Collections#synchronizedSet(Set)} method.  Null
 * elements are not supported and attempts to add one will throw a {@link NullPointerException}.
 * </p>
 *
76 77
 * @author Tom Tromey (tromey@redhat.com)
 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
78
 * @author Dalibor Topic (robilad@kaffe.org)
79
 * @since 1.5
80 81
 */

82 83 84
// FIXME: serialization is special, uses SerializationProxy.
// of(E e) is the 'bottom' method that creates a real EnumSet.
public abstract class EnumSet<T extends Enum<T>>
85 86 87 88 89
  extends AbstractSet<T>
  implements Cloneable, Serializable
{
  private static final long serialVersionUID = 4782406773684236311L;

90 91
  // These fields could go into the anonymous inner class in of(E),
  // complementOf would need to be refactored then, though.
92 93 94 95
  /**
   * The store which maintains the bits used to represent
   * the enumeration constants.
   */
96
  BitSet store;
97 98 99 100 101

  /**
   * The cardinality of the set (the current number
   * of bits set).
   */
102
  int cardinality;
103 104 105 106

  /**
   * The enumeration used by this set.
   */
107 108
  Class<T> enumClass;

109 110 111
  /**
   * Empty package-private constructor
   */
112 113 114 115
  EnumSet()
  {
  }

116 117
  /**
   * Returns a clone of the set.
118
   *
119 120
   * @return a clone of the set.
   */
121 122 123 124 125 126
  public EnumSet<T> clone()
  {
    EnumSet<T> r;

    try
      {
127
        r = (EnumSet<T>) super.clone();
128 129 130
      }
    catch (CloneNotSupportedException _)
      {
131 132
        /* Can't happen */
        return null;
133 134 135 136 137
      }
    r.store = (BitSet) store.clone();
    return r;
  }

138 139 140 141 142 143 144 145
  /**
   * Returns a set for the given enumeration type where
   * all the constants are present.
   *
   * @param eltType the type of enumeration to use for the set.
   * @return an {@link EnumSet} with all the bits set.
   * @throws NullPointerException if the element type is <code>null</code>.
   */
146
  public static <T extends Enum<T>> EnumSet<T> allOf(Class<T> eltType)
147
  {
148 149
    // create an EnumSet from the list of values of the type
    return copyOf(Arrays.asList(eltType.getEnumConstants()));
150 151
  }

152 153 154 155 156 157 158 159
  /**
   * Returns a set for the given enumeration type where
   * none of the constants are present.
   *
   * @param eltType the type of enumeration to use for the set.
   * @return an {@link EnumSet} with none of the bits set.
   * @throws NullPointerException if the element type is <code>null</code>.
   */
160
  public static <T extends Enum<T>> EnumSet<T> noneOf(Class<T> eltType)
161
  {
162 163
    return complementOf(allOf(eltType));
  }
164

165 166 167 168 169 170 171
  /**
   * Returns a clone of the given set.
   *
   * @param other the set to clone.
   * @return an {@link EnumSet} that is a clone of the given set.
   * @throws NullPointerException if <code>other</code> is <code>null</code>.
   */
172 173 174 175
  public static <T extends Enum<T>> EnumSet<T> copyOf(EnumSet<T> other)
  {
    return other.clone();
  }
176

177 178 179 180 181 182 183 184 185 186 187
  /**
   * Creates an {@link EnumSet} using the contents of the given collection.
   * If the collection is also an {@link EnumSet}, this method works the
   * same as {@link #copyOf(EnumSet)}.  Otherwise, the elements of the collection
   * are inspected and used to populate the new set.
   *
   * @param other the collection to use to populate the new set.
   * @return an {@link EnumSet} containing elements from the given collection.
   * @throws NullPointerException if <code>other</code> is <code>null</code>.
   * @throws IllegalArgumentException if the collection is empty.
   */
188 189 190 191 192
  public static <T extends Enum<T>> EnumSet<T> copyOf(Collection<T> other)
  {
    if (other instanceof EnumSet)
      return copyOf((EnumSet<T>) other);
    if (other.isEmpty())
193
        throw new IllegalArgumentException("Collection is empty");
194

195 196 197
    EnumSet<T> r = null;

    for (T val : other)
198
      {
199 200 201 202
        if (r == null)
          r = of(val);
        else
          r.add(val);
203
      }
204 205

    return r;
206 207
  }

208 209 210 211 212 213 214 215 216
  /**
   * Returns a set which is the inverse of the supplied set.
   * If a constant is present in the current set, it will not be
   * present in the new set and vice versa.
   *
   * @param other the set to provide the complement of.
   * @return an {@link EnumSet} which is the inverse of the current one.
   * @throws NullPointerException if <code>other</code> is <code>null</code>.
   */
217
  public static <T extends Enum<T>> EnumSet<T> complementOf(EnumSet<T> other)
218
  {
219
    EnumSet<T> r = other.clone();
220 221 222
    int numConstants = r.enumClass.getEnumConstants().length;
    r.store.flip(0, numConstants);
    r.cardinality = numConstants - other.cardinality;
223
    return r;
224 225
  }

226 227
  /**
   * Creates a new {@link EnumSet} populated with the given element.
228
   *
229 230 231 232
   * @param first the element to use to populate the new set.
   * @return an {@link EnumSet} containing the element.
   * @throws NullPointerException if <code>first</code> is <code>null</code>.
   */
233
  public static <T extends Enum<T>> EnumSet<T> of(T first)
234
  {
235 236 237 238
    EnumSet<T> r = new EnumSet<T>()
    {
      public boolean add(T val)
      {
239 240
        if (store.get(val.ordinal()))
          return false;
241

242 243 244
        store.set(val.ordinal());
        ++cardinality;
        return true;
245 246 247
      }

      public boolean addAll(Collection<? extends T> c)
248
      {
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
        boolean result = false;
        if (c instanceof EnumSet)
        {
          EnumSet<T> other = (EnumSet<T>) c;
          if (enumClass == other.enumClass)
          {
            store.or(other.store);
            int save = cardinality;
            cardinality = store.cardinality();
            result = save != cardinality;
          }
        }
        else
        {
          for (T val : c)
          {
            if (add (val))
            result = true;
          }
        }
        return result;
270 271
      }

272
      public void clear()
273
      {
274 275
        store.clear();
        cardinality = 0;
276 277
      }

278
      public boolean contains(Object o)
279
      {
280 281
        if (! (o instanceof Enum))
          return false;
282

283 284 285
        Enum<T> e = (Enum<T>) o;
        if (e.getDeclaringClass() != enumClass)
          return false;
286

287
        return store.get(e.ordinal());
288 289
      }

290
      public boolean containsAll(Collection<?> c)
291
      {
292 293 294 295 296 297 298 299 300
        if (c instanceof EnumSet)
        {
          EnumSet<T> other = (EnumSet<T>) c;
          if (enumClass == other.enumClass)
            return store.containsAll(other.store);

          return false;
        }
        return super.containsAll(c);
301 302
      }

303 304
      public Iterator<T> iterator()
      {
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
        return new Iterator<T>()
        {
          int next = -1;
          int count = 0;

          public boolean hasNext()
          {
            return count < cardinality;
          }

          public T next()
          {
            next = store.nextSetBit(next + 1);
            ++count;
            return enumClass.getEnumConstants()[next];
          }

          public void remove()
          {
            if (! store.get(next))
            {
                store.clear(next);
                --cardinality;
            }
          }
        };
331 332
      }

333 334
      public boolean remove(Object o)
      {
335 336
        if (! (o instanceof Enum))
          return false;
337

338 339 340
        Enum<T> e = (Enum<T>) o;
        if (e.getDeclaringClass() != enumClass)
          return false;
341

342 343 344
        store.clear(e.ordinal());
        --cardinality;
        return true;
345 346 347 348
      }

      public boolean removeAll(Collection<?> c)
      {
349 350 351 352 353 354 355 356 357 358 359 360
        if (c instanceof EnumSet)
        {
          EnumSet<T> other = (EnumSet<T>) c;
          if (enumClass != other.enumClass)
            return false;

          store.andNot(other.store);
          int save = cardinality;
          cardinality = store.cardinality();
          return save != cardinality;
        }
        return super.removeAll(c);
361 362 363 364
      }

      public boolean retainAll(Collection<?> c)
      {
365 366 367 368 369 370 371 372 373 374 375 376
        if (c instanceof EnumSet)
        {
          EnumSet<T> other = (EnumSet<T>) c;
          if (enumClass != other.enumClass)
            return false;

          store.and(other.store);
          int save = cardinality;
          cardinality = store.cardinality();
          return save != cardinality;
        }
        return super.retainAll(c);
377 378 379 380
      }

      public int size()
      {
381
        return cardinality;
382 383 384 385
      }
    };

    // initialize the class
386 387
    r.enumClass = first.getDeclaringClass();
    r.store = new BitSet(r.enumClass.getEnumConstants().length);
388 389

    r.add(first);
390 391 392
    return r;
  }

393 394
  /**
   * Creates a new {@link EnumSet} populated with the given two elements.
395
   *
396 397 398 399 400
   * @param first the first element to use to populate the new set.
   * @param second the second element to use.
   * @return an {@link EnumSet} containing the elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   */
401 402
  public static <T extends Enum<T>> EnumSet<T> of(T first, T second)
  {
403 404
    EnumSet<T> r = of(first);
    r.add(second);
405 406 407
    return r;
  }

408 409
  /**
   * Creates a new {@link EnumSet} populated with the given three elements.
410
   *
411 412 413 414 415 416
   * @param first the first element to use to populate the new set.
   * @param second the second element to use.
   * @param third the third element to use.
   * @return an {@link EnumSet} containing the elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   */
417 418
  public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third)
  {
419 420
    EnumSet<T> r = of(first, second);
    r.add(third);
421 422 423
    return r;
  }

424 425
  /**
   * Creates a new {@link EnumSet} populated with the given four elements.
426
   *
427 428 429 430 431 432 433
   * @param first the first element to use to populate the new set.
   * @param second the second element to use.
   * @param third the third element to use.
   * @param fourth the fourth element to use.
   * @return an {@link EnumSet} containing the elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   */
434
  public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third,
435
                                                  T fourth)
436
  {
437 438
    EnumSet<T> r = of(first, second, third);
    r.add(fourth);
439 440 441
    return r;
  }

442 443
  /**
   * Creates a new {@link EnumSet} populated with the given five elements.
444
   *
445 446 447 448 449 450 451 452
   * @param first the first element to use to populate the new set.
   * @param second the second element to use.
   * @param third the third element to use.
   * @param fourth the fourth element to use.
   * @param fifth the fifth element to use.
   * @return an {@link EnumSet} containing the elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   */
453
  public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third,
454
                                                  T fourth, T fifth)
455
  {
456 457
    EnumSet<T> r = of(first, second, third, fourth);
    r.add(fifth);
458 459 460
    return r;
  }

461 462
  /**
   * Creates a new {@link EnumSet} populated with the given elements.
463
   *
464 465 466 467 468
   * @param first the first element to use to populate the new set.
   * @param rest the other elements to use.
   * @return an {@link EnumSet} containing the elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   */
469 470
  public static <T extends Enum<T>> EnumSet<T> of(T first, T... rest)
  {
471 472
    EnumSet<T> r = noneOf(first.getDeclaringClass());
    r.add(first);
473
    for (T val : rest)
474
      r.add(val);
475 476 477
    return r;
  }

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
  /**
   * Creates a new {@link EnumSet} using the enumeration constants
   * starting from {@code from} and ending at {@code to} inclusive.
   * The two may be the same, but they must be in the correct order.
   * So giving the first constant twice would give a set with just that
   * constant set, while supplying the first and second constant will give
   * a set with those two elements.  However, specifying the second as
   * the {@code from} element followed by an earlier element as the
   * {@code to} element will result in an error.
   *
   * @param from the element to start from.
   * @param to the element to end at (may be the same as {@code from}.
   * @return an {@link EnumSet} containing the specified range of elements.
   * @throws NullPointerException if any of the parameters are <code>null</code>.
   * @throws IllegalArgumentException if {@code first.compareTo(last) > 0}.
   */
494 495 496 497
  public static <T extends Enum<T>> EnumSet<T> range(T from, T to)
  {
    if (from.compareTo(to) > 0)
      throw new IllegalArgumentException();
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    Class<T> type = from.getDeclaringClass();
    EnumSet<T> r = noneOf(type);

    T[] values = type.getEnumConstants();
    // skip over values until start of range is found
    int i = 0;
    while (from != values[i])
      i++;

    // add values until end of range is found
    while (to != values[i]) {
      r.add(values[i]);
      i++;
    }

    // add end of range
    r.add(to);

516 517 518
    return r;
  }
}