    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.LinkedHashMap;
    024import java.util.List;
    025import java.util.Map;
    026
    027/**
    028 * <p>Utility library to provide helper methods for Java enums.</p>
    029 *
    030 * <p>#ThreadSafe#</p>
    031 *
    032 * @since 3.0
    033 */
    034public class EnumUtils {
    035
    036    private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
    037    private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
    038    private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
    039    private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
    040
    041    /**
    042     * Validate {@code enumClass}.
    043     * @param <E> the type of the enumeration
    044     * @param enumClass to check
    045     * @return {@code enumClass}
    046     * @throws NullPointerException if {@code enumClass} is {@code null}
    047     * @throws IllegalArgumentException if {@code enumClass} is not an enum class
    048     * @since 3.2
    049     */
    050    private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
    051        Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
    052        Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
    053        return enumClass;
    054    }
    055
    056    /**
    057     * Validate that {@code enumClass} is compatible with representation in a {@code long}.
    058     * @param <E> the type of the enumeration
    059     * @param enumClass to check
    060     * @return {@code enumClass}
    061     * @throws NullPointerException if {@code enumClass} is {@code null}
    062     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
    063     * @since 3.0.1
    064     */
    065    private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
    066        final E[] constants = asEnum(enumClass).getEnumConstants();
    067        Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS,
    068            Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE));
    069
    070        return enumClass;
    071    }
    072
    073    /**
    074     * <p>Creates a long bit vector representation of the given array of Enum values.</p>
    075     *
    076     * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
    077     *
    078     * <p>Do not use this method if you have more than 64 values in your Enum, as this
    079     * would create a value greater than a long can hold.</p>
    080     *
    081     * @param enumClass the class of the enum we are working with, not {@code null}
    082     * @param values    the values we want to convert, not {@code null}
    083     * @param <E>       the type of the enumeration
    084     * @return a long whose value provides a binary representation of the given set of enum values.
    085     * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
    086     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
    087     * @since 3.0.1
    088     * @see #generateBitVectors(Class, Iterable)
    089     */
    090    @SafeVarargs
    091    public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
    092        Validate.noNullElements(values);
    093        return generateBitVector(enumClass, Arrays.asList(values));
    094    }
    095
    096    /**
    097     * <p>Creates a long bit vector representation of the given subset of an Enum.</p>
    098     *
    099     * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
    100     *
    101     * <p>Do not use this method if you have more than 64 values in your Enum, as this
    102     * would create a value greater than a long can hold.</p>
    103     *
    104     * @param enumClass the class of the enum we are working with, not {@code null}
    105     * @param values    the values we want to convert, not {@code null}, neither containing {@code null}
    106     * @param <E>       the type of the enumeration
    107     * @return a long whose value provides a binary representation of the given set of enum values.
    108     * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
    109     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values,
    110     *                                  or if any {@code values} {@code null}
    111     * @since 3.0.1
    112     * @see #generateBitVectors(Class, Iterable)
    113     */
    114    public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) {
    115        checkBitVectorable(enumClass);
    116        Validate.notNull(values);
    117        long total = 0;
    118        for (final E constant : values) {
    119            Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
    120            total |= 1L << constant.ordinal();
    121        }
    122        return total;
    123    }
    124
    125    /**
    126     * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
    127     *
    128     * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
    129     *
    130     * <p>Use this method if you have more than 64 values in your Enum.</p>
    131     *
    132     * @param enumClass the class of the enum we are working with, not {@code null}
    133     * @param values    the values we want to convert, not {@code null}, neither containing {@code null}
    134     * @param <E>       the type of the enumeration
    135     * @return a long[] whose values provide a binary representation of the given set of enum values
    136     *         with least significant digits rightmost.
    137     * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
    138     * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
    139     * @since 3.2
    140     */
    141    @SafeVarargs
    142    public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
    143        asEnum(enumClass);
    144        Validate.noNullElements(values);
    145        final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
    146        Collections.addAll(condensed, values);
    147        final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
    148        for (final E value : condensed) {
    149            result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE);
    150        }
    151        ArrayUtils.reverse(result);
    152        return result;
    153    }
    154
    155    /**
    156     * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
    157     *
    158     * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
    159     *
    160     * <p>Use this method if you have more than 64 values in your Enum.</p>
    161     *
    162     * @param enumClass the class of the enum we are working with, not {@code null}
    163     * @param values    the values we want to convert, not {@code null}, neither containing {@code null}
    164     * @param <E>       the type of the enumeration
    165     * @return a long[] whose values provide a binary representation of the given set of enum values
    166     *         with least significant digits rightmost.
    167     * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
    168     * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
    169     * @since 3.2
    170     */
    171    public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) {
    172        asEnum(enumClass);
    173        Validate.notNull(values);
    174        final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
    175        for (final E constant : values) {
    176            Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
    177            condensed.add(constant);
    178        }
    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     * <p>Gets the enum for the class, returning {@code null} if not found.</p>
    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     * <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
    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     * <p>Gets the enum for the class, returning {@code null} if not found.</p>
    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     * <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
    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, final E defaultEnum) {
    256        if (enumName == null || !enumClass.isEnum()) {
    257            return defaultEnum;
    258        }
    259        for (final E each : enumClass.getEnumConstants()) {
    260            if (each.name().equalsIgnoreCase(enumName)) {
    261                return each;
    262            }
    263        }
    264        return defaultEnum;
    265    }
    266
    267    /**
    268     * <p>Gets the {@code List} of enums.</p>
    269     *
    270     * <p>This method is useful when you need a list of enums rather than an array.</p>
    271     *
    272     * @param <E> the type of the enumeration
    273     * @param enumClass  the class of the enum to query, not null
    274     * @return the modifiable list of enums, never null
    275     */
    276    public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
    277        return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants()));
    278    }
    279
    280    /**
    281     * <p>Gets the {@code Map} of enums by name.</p>
    282     *
    283     * <p>This method is useful when you need a map of enums by name.</p>
    284     *
    285     * @param <E> the type of the enumeration
    286     * @param enumClass  the class of the enum to query, not null
    287     * @return the modifiable map of enum names to enums, never null
    288     */
    289    public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
    290        final Map<String, E> map = new LinkedHashMap<>();
    291        for (final E e: enumClass.getEnumConstants()) {
    292            map.put(e.name(), e);
    293        }
    294        return map;
    295    }
    296
    297    /**
    298     * <p>Checks if the specified name is a valid enum for the class.</p>
    299     *
    300     * <p>This method differs from {@link Enum#valueOf} in that checks if the name is
    301     * a valid enum without needing to catch the exception.</p>
    302     *
    303     * @param <E> the type of the enumeration
    304     * @param enumClass  the class of the enum to query, not null
    305     * @param enumName   the enum name, null returns false
    306     * @return true if the enum name is valid, otherwise false
    307     */
    308    public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
    309        return getEnum(enumClass, enumName) != null;
    310    }
    311
    312    /**
    313     * <p>Checks if the specified name is a valid enum for the class.</p>
    314     *
    315     * <p>This method differs from {@link Enum#valueOf} in that checks if the name is
    316     * a valid enum without needing to catch the exception
    317     * and performs case insensitive matching of the name.</p>
    318     *
    319     * @param <E> the type of the enumeration
    320     * @param enumClass  the class of the enum to query, not null
    321     * @param enumName   the enum name, null returns false
    322     * @return true if the enum name is valid, otherwise false
    323     * @since 3.8
    324     */
    325    public static <E extends Enum<E>> boolean isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
    326        return getEnumIgnoreCase(enumClass, enumName) != null;
    327    }
    328
    329    /**
    330     * <p>Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
    331     * enum values that it represents.</p>
    332     *
    333     * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
    334     * @param enumClass the class of the enum we are working with, not {@code null}
    335     * @param value     the long value representation of a set of enum values
    336     * @param <E>       the type of the enumeration
    337     * @return a set of enum values
    338     * @throws NullPointerException if {@code enumClass} is {@code null}
    339     * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
    340     * @since 3.0.1
    341     */
    342    public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
    343        checkBitVectorable(enumClass).getEnumConstants();
    344        return processBitVectors(enumClass, value);
    345    }
    346
    347    /**
    348     * <p>Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
    349     * enum values that it represents.</p>
    350     *
    351     * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
    352     * @param enumClass the class of the enum we are working with, not {@code null}
    353     * @param values     the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null}
    354     * @param <E>       the type of the enumeration
    355     * @return a set of enum values
    356     * @throws NullPointerException if {@code enumClass} is {@code null}
    357     * @throws IllegalArgumentException if {@code enumClass} is not an enum class
    358     * @since 3.2
    359     */
    360    public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) {
    361        final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
    362        final long[] lvalues = ArrayUtils.clone(Validate.notNull(values));
    363        ArrayUtils.reverse(lvalues);
    364        for (final E constant : enumClass.getEnumConstants()) {
    365            final int block = constant.ordinal() / Long.SIZE;
    366            if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) {
    367                results.add(constant);
    368            }
    369        }
    370        return results;
    371    }
    372
    373    /**
    374     * This constructor is public to permit tools that require a JavaBean
    375     * instance to operate.
    376     */
    377    public EnumUtils() {
    378    }
    379}