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