1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.EnumSet;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 /**
28 * <p>Utility library to provide helper methods for Java enums.</p>
29 *
30 * <p>#ThreadSafe#</p>
31 *
32 * @since 3.0
33 * @version $Id: EnumUtils.java 1436770 2013-01-22 07:09:45Z ggregory $
34 */
35 public class EnumUtils {
36
37 private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
38 private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
39 private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
40 private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
41
42 /**
43 * This constructor is public to permit tools that require a JavaBean
44 * instance to operate.
45 */
46 public EnumUtils() {
47 }
48
49 /**
50 * <p>Gets the {@code Map} of enums by name.</p>
51 *
52 * <p>This method is useful when you need a map of enums by name.</p>
53 *
54 * @param <E> the type of the enumeration
55 * @param enumClass the class of the enum to query, not null
56 * @return the modifiable map of enum names to enums, never null
57 */
58 public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
59 final Map<String, E> map = new LinkedHashMap<String, E>();
60 for (final E e: enumClass.getEnumConstants()) {
61 map.put(e.name(), e);
62 }
63 return map;
64 }
65
66 /**
67 * <p>Gets the {@code List} of enums.</p>
68 *
69 * <p>This method is useful when you need a list of enums rather than an array.</p>
70 *
71 * @param <E> the type of the enumeration
72 * @param enumClass the class of the enum to query, not null
73 * @return the modifiable list of enums, never null
74 */
75 public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
76 return new ArrayList<E>(Arrays.asList(enumClass.getEnumConstants()));
77 }
78
79 /**
80 * <p>Checks if the specified name is a valid enum for the class.</p>
81 *
82 * <p>This method differs from {@link Enum#valueOf} in that checks if the name is
83 * a valid enum without needing to catch the exception.</p>
84 *
85 * @param <E> the type of the enumeration
86 * @param enumClass the class of the enum to query, not null
87 * @param enumName the enum name, null returns false
88 * @return true if the enum name is valid, otherwise false
89 */
90 public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
91 if (enumName == null) {
92 return false;
93 }
94 try {
95 Enum.valueOf(enumClass, enumName);
96 return true;
97 } catch (final IllegalArgumentException ex) {
98 return false;
99 }
100 }
101
102 /**
103 * <p>Gets the enum for the class, returning {@code null} if not found.</p>
104 *
105 * <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
106 * for an invalid enum name.</p>
107 *
108 * @param <E> the type of the enumeration
109 * @param enumClass the class of the enum to query, not null
110 * @param enumName the enum name, null returns null
111 * @return the enum, null if not found
112 */
113 public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) {
114 if (enumName == null) {
115 return null;
116 }
117 try {
118 return Enum.valueOf(enumClass, enumName);
119 } catch (final IllegalArgumentException ex) {
120 return null;
121 }
122 }
123
124 /**
125 * <p>Creates a long bit vector representation of the given subset of an Enum.</p>
126 *
127 * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
128 *
129 * <p>Do not use this method if you have more than 64 values in your Enum, as this
130 * would create a value greater than a long can hold.</p>
131 *
132 * @param enumClass the class of the enum we are working with, not {@code null}
133 * @param values the values we want to convert, not {@code null}, neither containing {@code null}
134 * @param <E> the type of the enumeration
135 * @return a long whose value provides a binary representation of the given set of enum values.
136 * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
137 * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values,
138 * or if any {@code values} {@code null}
139 * @since 3.0.1
140 * @see #generateBitVectors(Class, Iterable)
141 */
142 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<E> values) {
143 checkBitVectorable(enumClass);
144 Validate.notNull(values);
145 long total = 0;
146 for (final E constant : values) {
147 Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED);
148 total |= 1 << constant.ordinal();
149 }
150 return total;
151 }
152
153 /**
154 * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
155 *
156 * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
157 *
158 * <p>Use this method if you have more than 64 values in your Enum.</p>
159 *
160 * @param enumClass the class of the enum we are working with, not {@code null}
161 * @param values the values we want to convert, not {@code null}, neither containing {@code null}
162 * @param <E> the type of the enumeration
163 * @return a long[] whose values provide a binary representation of the given set of enum values
164 * with least significant digits rightmost.
165 * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
166 * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
167 * @since 3.2
168 */
169 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<E> values) {
170 asEnum(enumClass);
171 Validate.notNull(values);
172 final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
173 for (final E constant : values) {
174 Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED);
175 condensed.add(constant);
176 }
177 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
178 for (final E value : condensed) {
179 result[value.ordinal() / Long.SIZE] |= 1 << (value.ordinal() % Long.SIZE);
180 }
181 ArrayUtils.reverse(result);
182 return result;
183 }
184
185 /**
186 * <p>Creates a long bit vector representation of the given array of Enum values.</p>
187 *
188 * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
189 *
190 * <p>Do not use this method if you have more than 64 values in your Enum, as this
191 * would create a value greater than a long can hold.</p>
192 *
193 * @param enumClass the class of the enum we are working with, not {@code null}
194 * @param values the values we want to convert, not {@code null}
195 * @param <E> the type of the enumeration
196 * @return a long whose value provides a binary representation of the given set of enum values.
197 * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
198 * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
199 * @since 3.0.1
200 * @see #generateBitVectors(Class, Iterable)
201 */
202 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
203 Validate.noNullElements(values);
204 return generateBitVector(enumClass, Arrays.<E> asList(values));
205 }
206
207 /**
208 * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
209 *
210 * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
211 *
212 * <p>Use this method if you have more than 64 values in your Enum.</p>
213 *
214 * @param enumClass the class of the enum we are working with, not {@code null}
215 * @param values the values we want to convert, not {@code null}, neither containing {@code null}
216 * @param <E> the type of the enumeration
217 * @return a long[] whose values provide a binary representation of the given set of enum values
218 * with least significant digits rightmost.
219 * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
220 * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
221 * @since 3.2
222 */
223 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
224 asEnum(enumClass);
225 Validate.noNullElements(values);
226 final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
227 Collections.addAll(condensed, values);
228 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
229 for (final E value : condensed) {
230 result[value.ordinal() / Long.SIZE] |= 1 << (value.ordinal() % Long.SIZE);
231 }
232 ArrayUtils.reverse(result);
233 return result;
234 }
235
236 /**
237 * <p>Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
238 * enum values that it represents.</p>
239 *
240 * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
241 * @param enumClass the class of the enum we are working with, not {@code null}
242 * @param value the long value representation of a set of enum values
243 * @param <E> the type of the enumeration
244 * @return a set of enum values
245 * @throws NullPointerException if {@code enumClass} is {@code null}
246 * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
247 * @since 3.0.1
248 */
249 public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
250 checkBitVectorable(enumClass).getEnumConstants();
251 return processBitVectors(enumClass, value);
252 }
253
254 /**
255 * <p>Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
256 * enum values that it represents.</p>
257 *
258 * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
259 * @param enumClass the class of the enum we are working with, not {@code null}
260 * @param values the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null}
261 * @param <E> the type of the enumeration
262 * @return a set of enum values
263 * @throws NullPointerException if {@code enumClass} is {@code null}
264 * @throws IllegalArgumentException if {@code enumClass} is not an enum class
265 * @since 3.2
266 */
267 public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, long... values) {
268 final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
269 values = ArrayUtils.clone(Validate.notNull(values));
270 ArrayUtils.reverse(values);
271 for (final E constant : enumClass.getEnumConstants()) {
272 final int block = constant.ordinal() / Long.SIZE;
273 if (block < values.length && (values[block] & 1 << (constant.ordinal() % Long.SIZE)) != 0) {
274 results.add(constant);
275 }
276 }
277 return results;
278 }
279
280 /**
281 * Validate that {@code enumClass} is compatible with representation in a {@code long}.
282 * @param <E> the type of the enumeration
283 * @param enumClass to check
284 * @return {@code enumClass}
285 * @throws NullPointerException if {@code enumClass} is {@code null}
286 * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
287 * @since 3.0.1
288 */
289 private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
290 final E[] constants = asEnum(enumClass).getEnumConstants();
291 Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, constants.length,
292 enumClass.getSimpleName(), Long.SIZE);
293
294 return enumClass;
295 }
296
297 /**
298 * Validate {@code enumClass}.
299 * @param <E> the type of the enumeration
300 * @param enumClass to check
301 * @return {@code enumClass}
302 * @throws NullPointerException if {@code enumClass} is {@code null}
303 * @throws IllegalArgumentException if {@code enumClass} is not an enum class
304 * @since 3.2
305 */
306 private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
307 Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
308 Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
309 return enumClass;
310 }
311 }