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