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 * https://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.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.function.Function;
27 import java.util.function.ToIntFunction;
28 import java.util.stream.Collectors;
29 import java.util.stream.Stream;
30
31 import org.apache.commons.lang3.stream.Streams;
32
33 /**
34 * Provides methods for Java enums.
35 *
36 * <p>
37 * #ThreadSafe#
38 * </p>
39 *
40 * @since 3.0
41 */
42 public class EnumUtils {
43
44 private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
45 private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
46 private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
47 private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
48
49 /**
50 * Validate {@code enumClass}.
51 *
52 * @param <E> The type of the enumeration.
53 * @param enumClass to check.
54 * @return {@code enumClass}.
55 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
56 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class.
57 * @since 3.2
58 */
59 private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
60 Objects.requireNonNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
61 Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
62 return enumClass;
63 }
64
65 /**
66 * Validate that {@code enumClass} is compatible with representation in a {@code long}.
67 *
68 * @param <E> The type of the enumeration.
69 * @param enumClass to check.
70 * @return {@code enumClass}.
71 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
72 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values.
73 * @since 3.0.1
74 */
75 private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
76 final E[] constants = asEnum(enumClass).getEnumConstants();
77 Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, Integer.valueOf(constants.length), enumClass.getSimpleName(),
78 Integer.valueOf(Long.SIZE));
79 return enumClass;
80 }
81
82 /**
83 * Creates a long bit vector representation of the given array of Enum values.
84 *
85 * <p>
86 * This generates a value that is usable by {@link EnumUtils#processBitVector}.
87 * </p>
88 *
89 * <p>
90 * Do not use this method if you have more than 64 values in your Enum, as this
91 * would create a value greater than a long can hold.
92 * </p>
93 *
94 * @param enumClass The class of the enum we are working with, not {@code null}.
95 * @param values The values we want to convert, not {@code null}.
96 * @param <E> the type of the enumeration.
97 * @return A long whose value provides a binary representation of the given set of enum values.
98 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}.
99 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values.
100 * @since 3.0.1
101 * @see #generateBitVectors(Class, Iterable)
102 */
103 @SafeVarargs
104 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
105 Validate.noNullElements(values);
106 return generateBitVector(enumClass, Arrays.asList(values));
107 }
108
109 /**
110 * Creates a long bit vector representation of the given subset of an Enum.
111 *
112 * <p>
113 * This generates a value that is usable by {@link EnumUtils#processBitVector}.
114 * </p>
115 *
116 * <p>
117 * Do not use this method if you have more than 64 values in your Enum, as this
118 * would create a value greater than a long can hold.
119 * </p>
120 *
121 * @param enumClass The class of the enum we are working with, not {@code null}.
122 * @param values The values we want to convert, not {@code null}, neither containing {@code null}.
123 * @param <E> the type of the enumeration.
124 * @return A long whose value provides a binary representation of the given set of enum values.
125 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}.
126 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values,
127 * or if any {@code values} {@code null}.
128 * @since 3.0.1
129 * @see #generateBitVectors(Class, Iterable)
130 */
131 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) {
132 checkBitVectorable(enumClass);
133 Objects.requireNonNull(values, "values");
134 long total = 0;
135 for (final E constant : values) {
136 Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
137 total |= 1L << constant.ordinal();
138 }
139 return total;
140 }
141
142 /**
143 * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.
144 *
145 * <p>
146 * This generates a value that is usable by {@link EnumUtils#processBitVectors}.
147 * </p>
148 *
149 * <p>
150 * Use this method if you have more than 64 values in your Enum.
151 * </p>
152 *
153 * @param enumClass The class of the enum we are working with, not {@code null}.
154 * @param values The values we want to convert, not {@code null}, neither containing {@code null}.
155 * @param <E> the type of the enumeration.
156 * @return A long[] whose values provide a binary representation of the given set of enum values
157 * with the least significant digits rightmost.
158 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}.
159 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class, or if any {@code values} {@code null}.
160 * @since 3.2
161 */
162 @SafeVarargs
163 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
164 asEnum(enumClass);
165 Validate.noNullElements(values);
166 final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
167 Collections.addAll(condensed, values);
168 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
169 for (final E value : condensed) {
170 result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE;
171 }
172 ArrayUtils.reverse(result);
173 return result;
174 }
175
176 /**
177 * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.
178 *
179 * <p>
180 * This generates a value that is usable by {@link EnumUtils#processBitVectors}.
181 * </p>
182 *
183 * <p>
184 * Use this method if you have more than 64 values in your Enum.
185 * </p>
186 *
187 * @param enumClass The class of the enum we are working with, not {@code null}.
188 * @param values The values we want to convert, not {@code null}, neither containing {@code null}.
189 * @param <E> the type of the enumeration.
190 * @return A long[] whose values provide a binary representation of the given set of enum values
191 * with the least significant digits rightmost.
192 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}.
193 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class, or if any {@code values} {@code null}.
194 * @since 3.2
195 */
196 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) {
197 asEnum(enumClass);
198 Objects.requireNonNull(values, "values");
199 final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
200 values.forEach(constant -> condensed.add(Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED)));
201 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
202 for (final E value : condensed) {
203 result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE;
204 }
205 ArrayUtils.reverse(result);
206 return result;
207 }
208
209 /**
210 * Gets the enum for the class, returning {@code null} if not found.
211 *
212 * <p>
213 * This method differs from {@link Enum#valueOf} in that it does not throw an exception
214 * for an invalid enum name.
215 * </p>
216 *
217 * @param <E> The type of the enumeration.
218 * @param enumClass The class of the enum to query, not null.
219 * @param enumName The enum name, null returns null.
220 * @return The enum, null if not found.
221 */
222 public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) {
223 return getEnum(enumClass, enumName, null);
224 }
225
226 /**
227 * Gets the enum for the class, returning {@code defaultEnum} if not found.
228 *
229 * <p>
230 * This method differs from {@link Enum#valueOf} in that it does not throw an exception
231 * for an invalid enum name.
232 * </p>
233 *
234 * @param <E> The type of the enumeration.
235 * @param enumClass The class of the enum to query, null returns default enum.
236 * @param enumName The enum name, null returns default enum.
237 * @param defaultEnum The default enum.
238 * @return The enum, default enum if not found.
239 * @since 3.10
240 */
241 public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName, final E defaultEnum) {
242 if (enumClass == null || enumName == null) {
243 return defaultEnum;
244 }
245 try {
246 return Enum.valueOf(enumClass, enumName);
247 } catch (final IllegalArgumentException e) {
248 return defaultEnum;
249 }
250 }
251
252 /**
253 * Gets the enum for the class, returning {@code null} if not found.
254 *
255 * <p>
256 * This method differs from {@link Enum#valueOf} in that it does not throw an exception
257 * for an invalid enum name and performs case insensitive matching of the name.
258 * </p>
259 *
260 * @param <E> the type of the enumeration.
261 * @param enumClass The class of the enum to query, may be null.
262 * @param enumName The enum name, null returns null.
263 * @return The enum, null if not found.
264 * @since 3.8
265 */
266 public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
267 return getEnumIgnoreCase(enumClass, enumName, null);
268 }
269
270 /**
271 * Gets the enum for the class, returning {@code defaultEnum} if not found.
272 *
273 * <p>
274 * This method differs from {@link Enum#valueOf} in that it does not throw an exception
275 * for an invalid enum name and performs case insensitive matching of the name.
276 * </p>
277 *
278 * @param <E> the type of the enumeration.
279 * @param enumClass The class of the enum to query, null returns default enum.
280 * @param enumName The enum name, null returns default enum.
281 * @param defaultEnum The default enum.
282 * @return The enum, default enum if not found.
283 * @since 3.10
284 */
285 public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName,
286 final E defaultEnum) {
287 return getFirstEnumIgnoreCase(enumClass, enumName, Enum::name, defaultEnum);
288 }
289
290 /**
291 * Gets the {@link List} of enums.
292 *
293 * <p>
294 * This method is useful when you need a list of enums rather than an array.
295 * </p>
296 *
297 * @param <E> The type of the enumeration.
298 * @param enumClass The class of the enum to query, not null.
299 * @return The modifiable list of enums, never null.
300 */
301 public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
302 return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants()));
303 }
304
305 /**
306 * Gets the {@link Map} of enums by name.
307 *
308 * <p>
309 * This method is useful when you need a map of enums by name.
310 * </p>
311 *
312 * @param <E> The type of the enumeration.
313 * @param enumClass The class of the enum to query, not null.
314 * @return The modifiable map of enum names to enums, never null.
315 */
316 public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
317 return getEnumMap(enumClass, E::name);
318 }
319
320 /**
321 * Gets the {@link Map} of enums by name.
322 *
323 * <p>
324 * This method is useful when you need a map of enums by name.
325 * </p>
326 *
327 * @param <E> the type of enumeration.
328 * @param <K> the type of the map key.
329 * @param enumClass The class of the enum to query, not null.
330 * @param keyFunction The function to query for the key, not null.
331 * @return The modifiable map of enums, never null.
332 * @since 3.13.0
333 */
334 public static <E extends Enum<E>, K> Map<K, E> getEnumMap(final Class<E> enumClass, final Function<E, K> keyFunction) {
335 return stream(enumClass).collect(Collectors.toMap(keyFunction::apply, Function.identity()));
336 }
337
338 /**
339 * Gets the enum for the class in a system property, returning {@code defaultEnum} if not found.
340 *
341 * <p>
342 * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name.
343 * </p>
344 * <p>
345 * If a {@link SecurityException} is caught, the return value is {@code null}.
346 * </p>
347 *
348 * @param <E> the type of the enumeration.
349 * @param enumClass The class of the enum to query, not null.
350 * @param propName The system property key for the enum name, null returns default enum.
351 * @param defaultEnum The default enum.
352 * @return The enum, default enum if not found.
353 * @since 3.13.0
354 */
355 public static <E extends Enum<E>> E getEnumSystemProperty(final Class<E> enumClass, final String propName, final E defaultEnum) {
356 return getEnum(enumClass, SystemProperties.getProperty(propName), defaultEnum);
357 }
358
359 /**
360 * Gets the enum for the class and value, returning {@code defaultEnum} if not found.
361 *
362 * <p>
363 * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name and performs case insensitive matching of
364 * the name.
365 * </p>
366 *
367 * @param <E> the type of the enumeration.
368 * @param enumClass The class of the enum to query, not null.
369 * @param value The enum name, null returns default enum.
370 * @param toIntFunction The function that gets an int for an enum for comparison to {@code value}.
371 * @param defaultEnum The default enum.
372 * @return An enum, default enum if not found.
373 * @since 3.18.0
374 */
375 public static <E extends Enum<E>> E getFirstEnum(final Class<E> enumClass, final int value, final ToIntFunction<E> toIntFunction, final E defaultEnum) {
376 if (!isEnum(enumClass)) {
377 return defaultEnum;
378 }
379 return stream(enumClass).filter(e -> value == toIntFunction.applyAsInt(e)).findFirst().orElse(defaultEnum);
380 }
381
382 /**
383 * Gets the enum for the class, returning {@code defaultEnum} if not found.
384 *
385 * <p>
386 * This method differs from {@link Enum#valueOf} in that it does not throw an exception
387 * for an invalid enum name and performs case insensitive matching of the name.
388 * </p>
389 *
390 * @param <E> the type of the enumeration.
391 * @param enumClass The class of the enum to query, null returns default enum.
392 * @param enumName The enum name, null returns default enum.
393 * @param stringFunction The function that gets the string for an enum for comparison to {@code enumName}.
394 * @param defaultEnum The default enum.
395 * @return An enum, default enum if not found.
396 * @since 3.13.0
397 */
398 public static <E extends Enum<E>> E getFirstEnumIgnoreCase(final Class<E> enumClass, final String enumName, final Function<E, String> stringFunction,
399 final E defaultEnum) {
400 if (enumName == null) {
401 return defaultEnum;
402 }
403 return stream(enumClass).filter(e -> enumName.equalsIgnoreCase(stringFunction.apply(e))).findFirst().orElse(defaultEnum);
404 }
405
406 private static <E extends Enum<E>> boolean isEnum(final Class<E> enumClass) {
407 return enumClass != null && enumClass.isEnum();
408 }
409
410 /**
411 * Tests whether the specified name is a valid enum for the class.
412 *
413 * <p>
414 * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception.
415 * </p>
416 *
417 * @param <E> the type of the enumeration.
418 * @param enumClass The class of the enum to query, null returns false.
419 * @param enumName The enum name, null returns false.
420 * @return true if the enum name is valid, otherwise false.
421 */
422 public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
423 return getEnum(enumClass, enumName) != null;
424 }
425
426 /**
427 * Tests whether the specified name is a valid enum for the class.
428 *
429 * <p>
430 * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception and performs case
431 * insensitive matching of the name.
432 * </p>
433 *
434 * @param <E> the type of the enumeration.
435 * @param enumClass The class of the enum to query, null returns false.
436 * @param enumName The enum name, null returns false.
437 * @return true if the enum name is valid, otherwise false.
438 * @since 3.8
439 */
440 public static <E extends Enum<E>> boolean isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
441 return getEnumIgnoreCase(enumClass, enumName) != null;
442 }
443
444 /**
445 * Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
446 * enum values that it represents.
447 *
448 * <p>
449 * If you store this value, beware any changes to the enum that would affect ordinal values.
450 * </p>
451 *
452 * @param enumClass The class of the enum we are working with, not {@code null}.
453 * @param value The long value representation of a set of enum values.
454 * @param <E> the type of the enumeration.
455 * @return A set of enum values.
456 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
457 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values.
458 * @since 3.0.1
459 */
460 public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
461 return processBitVectors(checkBitVectorable(enumClass), value);
462 }
463
464 /**
465 * Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
466 * enum values that it represents.
467 *
468 * <p>
469 * If you store this value, beware any changes to the enum that would affect ordinal values.
470 * </p>
471 *
472 * @param enumClass The class of the enum we are working with, not {@code null}.
473 * @param values The long[] bearing the representation of a set of enum values, the least significant digits rightmost, not {@code null}.
474 * @param <E> the type of the enumeration.
475 * @return A set of enum values.
476 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
477 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class.
478 * @since 3.2
479 */
480 public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) {
481 final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
482 final long[] lvalues = ArrayUtils.clone(Objects.requireNonNull(values, "values"));
483 ArrayUtils.reverse(lvalues);
484 stream(enumClass).forEach(constant -> {
485 final int block = constant.ordinal() / Long.SIZE;
486 if (block < lvalues.length && (lvalues[block] & 1L << constant.ordinal() % Long.SIZE) != 0) {
487 results.add(constant);
488 }
489 });
490 return results;
491 }
492
493 /**
494 * Returns a sequential ordered stream whose elements are the given class' enum values.
495 *
496 * @param <T> the type of stream elements.
497 * @param clazz The class containing the enum values, may be null.
498 * @return The new stream, empty of {@code clazz} is null.
499 * @since 3.18.0
500 * @see Class#getEnumConstants()
501 */
502 public static <T> Stream<T> stream(final Class<T> clazz) {
503 return clazz != null ? Streams.of(clazz.getEnumConstants()) : Stream.empty();
504 }
505
506 /**
507 * This constructor is public to permit tools that require a JavaBean
508 * instance to operate.
509 *
510 * @deprecated TODO Make private in 4.0.
511 */
512 @Deprecated
513 public EnumUtils() {
514 // empty
515 }
516 }