    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>#ThreadSafe#</p>
    037 *
    038 * @since 3.0
    039 */
    040public class EnumUtils {
    041
    042    private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
    043    private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
    044    private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
    045    private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
    046
    047    /**
    048     * Validate {@code enumClass}.
    049     *
    050     * @param <E> the type of the enumeration.
    051     * @param enumClass to check.
    052     * @return {@code enumClass}.
    053     * @throws NullPointerException if {@code enumClass} is {@code null}.
    054     * @throws IllegalArgumentException if {@code enumClass} is not an enum class.
    055     * @since 3.2
    056     */
    057    private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
    058        Objects.requireNonNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
    059        Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
    060        return enumClass;
    061    }
    062
    063    /**
    064     * Validate that {@code enumClass} is compatible with representation in a {@code long}.
    065     *
    066     * @param <E> the type of the enumeration.
    067     * @param enumClass to check.
    068     * @return {@code enumClass}.
    069     * @throws NullPointerException if {@code enumClass} is {@code null}.
    070     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values.
    071     * @since 3.0.1
    072     */
    073    private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
    074        final E[] constants = asEnum(enumClass).getEnumConstants();
    075        Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, Integer.valueOf(constants.length), enumClass.getSimpleName(),
    076                Integer.valueOf(Long.SIZE));
    077        return enumClass;
    078    }
    079
    080    /**
    081     * Creates a long bit vector representation of the given array of Enum values.
    082     *
    083     * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
    084     *
    085     * <p>Do not use this method if you have more than 64 values in your Enum, as this
    086     * would create a value greater than a long can hold.</p>
    087     *
    088     * @param enumClass the class of the enum we are working with, not {@code null}.
    089     * @param values    the values we want to convert, not {@code null}.
    090     * @param <E>       the type of the enumeration.
    091     * @return a long whose value provides a binary representation of the given set of enum values.
    092     * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}.
    093     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values.
    094     * @since 3.0.1
    095     * @see #generateBitVectors(Class, Iterable)
    096     */
    097    @SafeVarargs
    098    public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
    099        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}