1 /*
2  * Copyright (c) 2003, 2019, 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 
RegularEnumSet(Class<E>elementType, Enum<?>[] universe)45     RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
46         super(elementType, universe);
47     }
48 
addRange(E from, E to)49     void addRange(E from, E to) {
50         elements = (-1L >>>  (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
51     }
52 
addAll()53     void addAll() {
54         if (universe.length != 0)
55             elements = -1L >>> -universe.length;
56     }
57 
complement()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      */
iterator()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 
EnumSetIterator()92         EnumSetIterator() {
93             unseen = elements;
94         }
95 
hasNext()96         public boolean hasNext() {
97             return unseen != 0;
98         }
99 
100         @SuppressWarnings("unchecked")
next()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 
remove()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      */
size()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      */
isEmpty()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      */
contains(Object e)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      */
add(E e)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      */
remove(Object e)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      */
containsAll(Collection<?> c)198     public boolean containsAll(Collection<?> c) {
199         if (!(c instanceof RegularEnumSet<?> es))
200             return super.containsAll(c);
201 
202         if (es.elementType != elementType)
203             return es.isEmpty();
204 
205         return (es.elements & ~elements) == 0;
206     }
207 
208     /**
209      * Adds all of the elements in the specified collection to this set.
210      *
211      * @param c collection whose elements are to be added to this set
212      * @return {@code true} if this set changed as a result of the call
213      * @throws NullPointerException if the specified collection or any
214      *     of its elements are null
215      */
addAll(Collection<? extends E> c)216     public boolean addAll(Collection<? extends E> c) {
217         if (!(c instanceof RegularEnumSet<?> es))
218             return super.addAll(c);
219 
220         if (es.elementType != elementType) {
221             if (es.isEmpty())
222                 return false;
223             else
224                 throw new ClassCastException(
225                     es.elementType + " != " + elementType);
226         }
227 
228         long oldElements = elements;
229         elements |= es.elements;
230         return elements != oldElements;
231     }
232 
233     /**
234      * Removes from this set all of its elements that are contained in
235      * the specified collection.
236      *
237      * @param c elements to be removed from this set
238      * @return {@code true} if this set changed as a result of the call
239      * @throws NullPointerException if the specified collection is null
240      */
removeAll(Collection<?> c)241     public boolean removeAll(Collection<?> c) {
242         if (!(c instanceof RegularEnumSet<?> es))
243             return super.removeAll(c);
244 
245         if (es.elementType != elementType)
246             return false;
247 
248         long oldElements = elements;
249         elements &= ~es.elements;
250         return elements != oldElements;
251     }
252 
253     /**
254      * Retains only the elements in this set that are contained in the
255      * specified collection.
256      *
257      * @param c elements to be retained in this set
258      * @return {@code true} if this set changed as a result of the call
259      * @throws NullPointerException if the specified collection is null
260      */
retainAll(Collection<?> c)261     public boolean retainAll(Collection<?> c) {
262         if (!(c instanceof RegularEnumSet<?> es))
263             return super.retainAll(c);
264 
265         if (es.elementType != elementType) {
266             boolean changed = (elements != 0);
267             elements = 0;
268             return changed;
269         }
270 
271         long oldElements = elements;
272         elements &= es.elements;
273         return elements != oldElements;
274     }
275 
276     /**
277      * Removes all of the elements from this set.
278      */
clear()279     public void clear() {
280         elements = 0;
281     }
282 
283     /**
284      * Compares the specified object with this set for equality.  Returns
285      * {@code true} if the given object is also a set, the two sets have
286      * the same size, and every member of the given set is contained in
287      * this set.
288      *
289      * @param o object to be compared for equality with this set
290      * @return {@code true} if the specified object is equal to this set
291      */
equals(Object o)292     public boolean equals(Object o) {
293         if (!(o instanceof RegularEnumSet<?> es))
294             return super.equals(o);
295 
296         if (es.elementType != elementType)
297             return elements == 0 && es.elements == 0;
298         return es.elements == elements;
299     }
300 }
301