View Javadoc
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 }