001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.EnumSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Objects;
026import java.util.function.Function;
027import java.util.function.ToIntFunction;
028import java.util.stream.Collectors;
029import java.util.stream.Stream;
030
031import org.apache.commons.lang3.stream.Streams;
032
033/**
034 * Provides methods for Java enums.
035 *
036 * <p>
037 * #ThreadSafe#
038 * </p>
039 *
040 * @since 3.0
041 */
042public class EnumUtils {
043
044    private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
045    private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
046    private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
047    private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
048
049    /**
050     * Validate {@code enumClass}.
051     *
052     * @param <E> The type of the enumeration.
053     * @param enumClass to check.
054     * @return {@code enumClass}.
055     * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
056     * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class.
057     * @since 3.2
058     */
059    private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
060        Objects.requireNonNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
061        Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
062        return enumClass;
063    }
064
065    /**
066     * Validate that {@code enumClass} is compatible with representation in a {@code long}.
067     *
068     * @param <E> The type of the enumeration.
069     * @param enumClass to check.
070     * @return {@code enumClass}.
071     * @throws NullPointerException Thrown if {@code enumClass} is {@code null}.
072     * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values.
073     * @since 3.0.1
074     */
075    private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
076        final E[] constants = asEnum(enumClass).getEnumConstants();
077        Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, Integer.valueOf(constants.length), enumClass.getSimpleName(),
078                Integer.valueOf(Long.SIZE));
079        return enumClass;
080    }
081
082    /**
083     * Creates a long bit vector representation of the given array of Enum values.
084     *
085     * <p>
086     * This generates a value that is usable by {@link EnumUtils#processBitVector}.
087     * </p>
088     *
089     * <p>
090     * Do not use this method if you have more than 64 values in your Enum, as this
091     * would create a value greater than a long can hold.
092     * </p>
093     *
094     * @param enumClass The class of the enum we are working with, not {@code null}.
095     * @param values    The values we want to convert, not {@code null}.
096     * @param <E>       the type of the enumeration.
097     * @return A long whose value provides a binary representation of the given set of enum values.
098     * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}.
099     * @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}