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