1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package java.util;
17
18import java.io.Serializable;
19
20/**
21 * An EnumSet is a specialized Set to be used with enums as keys.
22 */
23public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
24        implements Cloneable, Serializable {
25    private static final long serialVersionUID = 1009687484059888093L;
26
27    final Class<E> elementClass;
28
29    EnumSet(Class<E> cls) {
30        elementClass = cls;
31    }
32
33    /**
34     * Creates an empty enum set. The permitted elements are of type
35     * Class&lt;E&gt;.
36     *
37     * @param elementType
38     *            the class object for the elements contained.
39     * @return an empty enum set, with permitted elements of type {@code
40     *         elementType}.
41     * @throws ClassCastException
42     *             if the specified element type is not and enum type.
43     */
44    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
45        if (!elementType.isEnum()) {
46            throw new ClassCastException(elementType.getClass().getName() + " is not an Enum");
47        }
48        E[] enums = Enum.getSharedConstants(elementType);
49        if (enums.length <= 64) {
50            return new MiniEnumSet<E>(elementType, enums);
51        }
52        return new HugeEnumSet<E>(elementType, enums);
53    }
54
55    /**
56     * Creates an enum set filled with all the enum elements of the specified
57     * {@code elementType}.
58     *
59     * @param elementType
60     *            the class object for the elements contained.
61     * @return an enum set with elements solely from the specified element type.
62     * @throws ClassCastException
63     *             if the specified element type is not and enum type.
64     */
65    public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
66        EnumSet<E> set = noneOf(elementType);
67        set.complement();
68        return set;
69    }
70
71    /**
72     * Creates an enum set. All the contained elements are of type
73     * Class&lt;E&gt;, and the contained elements are the same as those
74     * contained in {@code s}.
75     *
76     * @param s
77     *            the enum set from which to copy.
78     * @return an enum set with all the elements from the specified enum set.
79     * @throws ClassCastException
80     *             if the specified element type is not and enum type.
81     */
82    public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
83        EnumSet<E> set = EnumSet.noneOf(s.elementClass);
84        set.addAll(s);
85        return set;
86    }
87
88    /**
89     * Creates an enum set. The contained elements are the same as those
90     * contained in collection {@code c}. If c is an enum set, invoking this
91     * method is the same as invoking {@link #copyOf(EnumSet)}.
92     *
93     * @param c
94     *            the collection from which to copy. if it is not an enum set,
95     *            it must not be empty.
96     * @return an enum set with all the elements from the specified collection.
97     * @throws IllegalArgumentException
98     *             if c is not an enum set and contains no elements at all.
99     * @throws NullPointerException
100     *             if {@code c} is {@code null}.
101     */
102    public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
103        if (c instanceof EnumSet) {
104            return copyOf((EnumSet<E>) c);
105        }
106        if (c.isEmpty()) {
107            throw new IllegalArgumentException("empty collection");
108        }
109        Iterator<E> iterator = c.iterator();
110        E element = iterator.next();
111        EnumSet<E> set = EnumSet.noneOf(element.getDeclaringClass());
112        set.add(element);
113        while (iterator.hasNext()) {
114            set.add(iterator.next());
115        }
116        return set;
117    }
118
119    /**
120     * Creates an enum set. All the contained elements complement those from the
121     * specified enum set.
122     *
123     * @param s
124     *            the specified enum set.
125     * @return an enum set with all the elements complementary to those from the
126     *         specified enum set.
127     * @throws NullPointerException
128     *             if {@code s} is {@code null}.
129     */
130    public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
131        EnumSet<E> set = EnumSet.noneOf(s.elementClass);
132        set.addAll(s);
133        set.complement();
134        return set;
135    }
136
137    abstract void complement();
138
139    /**
140     * Creates a new enum set, containing only the specified element. There are
141     * six overloadings of the method. They accept from one to five elements
142     * respectively. The sixth one receives an arbitrary number of elements, and
143     * runs slower than those that only receive a fixed number of elements.
144     *
145     * @param e
146     *            the element to be initially contained.
147     * @return an enum set containing the specified element.
148     * @throws NullPointerException
149     *             if {@code e} is {@code null}.
150     */
151    public static <E extends Enum<E>> EnumSet<E> of(E e) {
152        EnumSet<E> set = EnumSet.noneOf(e.getDeclaringClass());
153        set.add(e);
154        return set;
155    }
156
157    /**
158     * Creates a new enum set, containing only the specified elements. There are
159     * six overloadings of the method. They accept from one to five elements
160     * respectively. The sixth one receives an arbitrary number of elements, and
161     * runs slower than those that only receive a fixed number of elements.
162     *
163     * @param e1
164     *            the initially contained element.
165     * @param e2
166     *            another initially contained element.
167     * @return an enum set containing the specified elements.
168     * @throws NullPointerException
169     *             if any of the specified elements is {@code null}.
170     */
171    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
172        EnumSet<E> set = of(e1);
173        set.add(e2);
174        return set;
175    }
176
177    /**
178     * Creates a new enum set, containing only the specified elements. There are
179     * six overloadings of the method. They accept from one to five elements
180     * respectively. The sixth one receives an arbitrary number of elements, and
181     * runs slower than those that only receive a fixed number of elements.
182     *
183     * @param e1
184     *            the initially contained element.
185     * @param e2
186     *            another initially contained element.
187     * @param e3
188     *            another initially contained element.
189     * @return an enum set containing the specified elements.
190     * @throws NullPointerException
191     *             if any of the specified elements is {@code null}.
192     */
193    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
194        EnumSet<E> set = of(e1, e2);
195        set.add(e3);
196        return set;
197    }
198
199    /**
200     * Creates a new enum set, containing only the specified elements. There are
201     * six overloadings of the method. They accept from one to five elements
202     * respectively. The sixth one receives an arbitrary number of elements, and
203     * runs slower than those that only receive a fixed number of elements.
204     *
205     * @param e1
206     *            the initially contained element.
207     * @param e2
208     *            another initially contained element.
209     * @param e3
210     *            another initially contained element.
211     * @param e4
212     *            another initially contained element.
213     * @return an enum set containing the specified elements.
214     * @throws NullPointerException
215     *             if any of the specified elements is {@code null}.
216     */
217    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
218        EnumSet<E> set = of(e1, e2, e3);
219        set.add(e4);
220        return set;
221    }
222
223    /**
224     * Creates a new enum set, containing only the specified elements. There are
225     * six overloadings of the method. They accept from one to five elements
226     * respectively. The sixth one receives an arbitrary number of elements, and
227     * runs slower than those that only receive a fixed number of elements.
228     *
229     * @param e1
230     *            the initially contained element.
231     * @param e2
232     *            another initially contained element.
233     * @param e3
234     *            another initially contained element.
235     * @param e4
236     *            another initially contained element.
237     * @param e5
238     *            another initially contained element.
239     * @return an enum set containing the specified elements.
240     * @throws NullPointerException
241     *             if any of the specified elements is {@code null}.
242     */
243    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5) {
244        EnumSet<E> set = of(e1, e2, e3, e4);
245        set.add(e5);
246        return set;
247    }
248
249    /**
250     * Creates a new enum set, containing only the specified elements. It can
251     * receive an arbitrary number of elements, and runs slower than those only
252     * receiving a fixed number of elements.
253     *
254     * @param start
255     *            the first initially contained element.
256     * @param others
257     *            the other initially contained elements.
258     * @return an enum set containing the specified elements.
259     * @throws NullPointerException
260     *             if any of the specified elements is {@code null}.
261     */
262    @SafeVarargs
263    public static <E extends Enum<E>> EnumSet<E> of(E start, E... others) {
264        EnumSet<E> set = of(start);
265        for (E e : others) {
266            set.add(e);
267        }
268        return set;
269    }
270
271    /**
272     * Creates an enum set containing all the elements within the range defined
273     * by {@code start} and {@code end} (inclusive). All the elements must be in
274     * order.
275     *
276     * @param start
277     *            the element used to define the beginning of the range.
278     * @param end
279     *            the element used to define the end of the range.
280     * @return an enum set with elements in the range from start to end.
281     * @throws NullPointerException
282     *             if any one of {@code start} or {@code end} is {@code null}.
283     * @throws IllegalArgumentException
284     *             if {@code start} is behind {@code end}.
285     */
286    public static <E extends Enum<E>> EnumSet<E> range(E start, E end) {
287        if (start.compareTo(end) > 0) {
288            throw new IllegalArgumentException("start is behind end");
289        }
290        EnumSet<E> set = EnumSet.noneOf(start.getDeclaringClass());
291        set.setRange(start, end);
292        return set;
293    }
294
295    abstract void setRange(E start, E end);
296
297    /**
298     * Creates a new enum set with the same elements as those contained in this
299     * enum set.
300     *
301     * @return a new enum set with the same elements as those contained in this
302     *         enum set.
303     */
304    @SuppressWarnings("unchecked")
305    @Override
306    public EnumSet<E> clone() {
307        try {
308            return (EnumSet<E>) super.clone();
309        } catch (CloneNotSupportedException e) {
310            throw new AssertionError(e);
311        }
312    }
313
314    boolean isValidType(Class<?> cls) {
315        return cls == elementClass || cls.getSuperclass() == elementClass;
316    }
317
318    private static class SerializationProxy<E extends Enum<E>> implements
319            Serializable {
320
321        private static final long serialVersionUID = 362491234563181265L;
322
323        private Class<E> elementType;
324
325        private E[] elements;
326
327        private Object readResolve() {
328            EnumSet<E> set = EnumSet.noneOf(elementType);
329            for (E e : elements) {
330                set.add(e);
331            }
332            return set;
333        }
334    }
335
336    @SuppressWarnings("unchecked")
337    Object writeReplace() {
338        SerializationProxy proxy = new SerializationProxy();
339        proxy.elements = toArray(new Enum[0]);
340        proxy.elementType = elementClass;
341        return proxy;
342    }
343}
344