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}