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>#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 }