1 /*
   2  * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util;
  27 
  28 /**
  29  * Private implementation class for EnumSet, for "regular sized" enum types
  30  * (i.e., those with 64 or fewer enum constants).
  31  *
  32  * @author Josh Bloch
  33  * @since 1.5
  34  * @serial exclude
  35  */
  36 class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
  37     @java.io.Serial
  38     private static final long serialVersionUID = 3411599620347842686L;
  39     /**
  40      * Bit vector representation of this set.  The 2^k bit indicates the
  41      * presence of universe[k] in this set.
  42      */
  43     private long elements = 0L;
  44 
  45     RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
  46         super(elementType, universe);
  47     }
  48 
  49     void addRange(E from, E to) {
  50         elements = (-1L >>>  (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
  51     }
  52 
  53     void addAll() {
  54         if (universe.length != 0)
  55             elements = -1L >>> -universe.length;
  56     }
  57 
  58     void complement() {
  59         if (universe.length != 0) {
  60             elements = ~elements;
  61             elements &= -1L >>> -universe.length;  // Mask unused bits
  62         }
  63     }
  64 
  65     /**
  66      * Returns an iterator over the elements contained in this set.  The
  67      * iterator traverses the elements in their <i>natural order</i> (which is
  68      * the order in which the enum constants are declared). The returned
  69      * Iterator is a "snapshot" iterator that will never throw {@link
  70      * ConcurrentModificationException}; the elements are traversed as they
  71      * existed when this call was invoked.
  72      *
  73      * @return an iterator over the elements contained in this set
  74      */
  75     public Iterator<E> iterator() {
  76         return new EnumSetIterator<>();
  77     }
  78 
  79     private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
  80         /**
  81          * A bit vector representing the elements in the set not yet
  82          * returned by this iterator.
  83          */
  84         long unseen;
  85 
  86         /**
  87          * The bit representing the last element returned by this iterator
  88          * but not removed, or zero if no such element exists.
  89          */
  90         long lastReturned = 0;
  91 
  92         EnumSetIterator() {
  93             unseen = elements;
  94         }
  95 
  96         public boolean hasNext() {
  97             return unseen != 0;
  98         }
  99 
 100         @SuppressWarnings("unchecked")
 101         public E next() {
 102             if (unseen == 0)
 103                 throw new NoSuchElementException();
 104             lastReturned = unseen & -unseen;
 105             unseen -= lastReturned;
 106             return (E) universe[Long.numberOfTrailingZeros(lastReturned)];
 107         }
 108 
 109         public void remove() {
 110             if (lastReturned == 0)
 111                 throw new IllegalStateException();
 112             elements &= ~lastReturned;
 113             lastReturned = 0;
 114         }
 115     }
 116 
 117     /**
 118      * Returns the number of elements in this set.
 119      *
 120      * @return the number of elements in this set
 121      */
 122     public int size() {
 123         return Long.bitCount(elements);
 124     }
 125 
 126     /**
 127      * Returns {@code true} if this set contains no elements.
 128      *
 129      * @return {@code true} if this set contains no elements
 130      */
 131     public boolean isEmpty() {
 132         return elements == 0;
 133     }
 134 
 135     /**
 136      * Returns {@code true} if this set contains the specified element.
 137      *
 138      * @param e element to be checked for containment in this collection
 139      * @return {@code true} if this set contains the specified element
 140      */
 141     public boolean contains(Object e) {
 142         if (e == null)
 143             return false;
 144         Class<?> eClass = e.getClass();
 145         if (eClass != elementType && eClass.getSuperclass() != elementType)
 146             return false;
 147 
 148         return (elements & (1L << ((Enum<?>)e).ordinal())) != 0;
 149     }
 150 
 151     // Modification Operations
 152 
 153     /**
 154      * Adds the specified element to this set if it is not already present.
 155      *
 156      * @param e element to be added to this set
 157      * @return {@code true} if the set changed as a result of the call
 158      *
 159      * @throws NullPointerException if {@code e} is null
 160      */
 161     public boolean add(E e) {
 162         typeCheck(e);
 163 
 164         long oldElements = elements;
 165         elements |= (1L << ((Enum<?>)e).ordinal());
 166         return elements != oldElements;
 167     }
 168 
 169     /**
 170      * Removes the specified element from this set if it is present.
 171      *
 172      * @param e element to be removed from this set, if present
 173      * @return {@code true} if the set contained the specified element
 174      */
 175     public boolean remove(Object e) {
 176         if (e == null)
 177             return false;
 178         Class<?> eClass = e.getClass();
 179         if (eClass != elementType && eClass.getSuperclass() != elementType)
 180             return false;
 181 
 182         long oldElements = elements;
 183         elements &= ~(1L << ((Enum<?>)e).ordinal());
 184         return elements != oldElements;
 185     }
 186 
 187     // Bulk Operations
 188 
 189     /**
 190      * Returns {@code true} if this set contains all of the elements
 191      * in the specified collection.
 192      *
 193      * @param c collection to be checked for containment in this set
 194      * @return {@code true} if this set contains all of the elements
 195      *        in the specified collection
 196      * @throws NullPointerException if the specified collection is null
 197      */
 198     public boolean containsAll(Collection<?> c) {
 199         if (!(c instanceof RegularEnumSet))
 200             return super.containsAll(c);
 201 
 202         RegularEnumSet<?> es = (RegularEnumSet<?>)c;
 203         if (es.elementType != elementType)
 204             return es.isEmpty();
 205 
 206         return (es.elements & ~elements) == 0;
 207     }
 208 
 209     /**
 210      * Adds all of the elements in the specified collection to this set.
 211      *
 212      * @param c collection whose elements are to be added to this set
 213      * @return {@code true} if this set changed as a result of the call
 214      * @throws NullPointerException if the specified collection or any
 215      *     of its elements are null
 216      */
 217     public boolean addAll(Collection<? extends E> c) {
 218         if (!(c instanceof RegularEnumSet))
 219             return super.addAll(c);
 220 
 221         RegularEnumSet<?> es = (RegularEnumSet<?>)c;
 222         if (es.elementType != elementType) {
 223             if (es.isEmpty())
 224                 return false;
 225             else
 226                 throw new ClassCastException(
 227                     es.elementType + " != " + elementType);
 228         }
 229 
 230         long oldElements = elements;
 231         elements |= es.elements;
 232         return elements != oldElements;
 233     }
 234 
 235     /**
 236      * Removes from this set all of its elements that are contained in
 237      * the specified collection.
 238      *
 239      * @param c elements to be removed from this set
 240      * @return {@code true} if this set changed as a result of the call
 241      * @throws NullPointerException if the specified collection is null
 242      */
 243     public boolean removeAll(Collection<?> c) {
 244         if (!(c instanceof RegularEnumSet))
 245             return super.removeAll(c);
 246 
 247         RegularEnumSet<?> es = (RegularEnumSet<?>)c;
 248         if (es.elementType != elementType)
 249             return false;
 250 
 251         long oldElements = elements;
 252         elements &= ~es.elements;
 253         return elements != oldElements;
 254     }
 255 
 256     /**
 257      * Retains only the elements in this set that are contained in the
 258      * specified collection.
 259      *
 260      * @param c elements to be retained in this set
 261      * @return {@code true} if this set changed as a result of the call
 262      * @throws NullPointerException if the specified collection is null
 263      */
 264     public boolean retainAll(Collection<?> c) {
 265         if (!(c instanceof RegularEnumSet))
 266             return super.retainAll(c);
 267 
 268         RegularEnumSet<?> es = (RegularEnumSet<?>)c;
 269         if (es.elementType != elementType) {
 270             boolean changed = (elements != 0);
 271             elements = 0;
 272             return changed;
 273         }
 274 
 275         long oldElements = elements;
 276         elements &= es.elements;
 277         return elements != oldElements;
 278     }
 279 
 280     /**
 281      * Removes all of the elements from this set.
 282      */
 283     public void clear() {
 284         elements = 0;
 285     }
 286 
 287     /**
 288      * Compares the specified object with this set for equality.  Returns
 289      * {@code true} if the given object is also a set, the two sets have
 290      * the same size, and every member of the given set is contained in
 291      * this set.
 292      *
 293      * @param o object to be compared for equality with this set
 294      * @return {@code true} if the specified object is equal to this set
 295      */
 296     public boolean equals(Object o) {
 297         if (!(o instanceof RegularEnumSet))
 298             return super.equals(o);
 299 
 300         RegularEnumSet<?> es = (RegularEnumSet<?>)o;
 301         if (es.elementType != elementType)
 302             return elements == 0 && es.elements == 0;
 303         return es.elements == elements;
 304     }
 305 }