View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.EnumSet;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  /**
28   * <p>Utility library to provide helper methods for Java enums.</p>
29   *
30   * <p>#ThreadSafe#</p>
31   *
32   * @since 3.0
33   * @version $Id: EnumUtils.java 1573185 2014-03-01 15:51:39Z sebb $
34   */
35  public class EnumUtils {
36  
37      private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
38      private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
39      private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
40      private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
41  
42      /**
43       * This constructor is public to permit tools that require a JavaBean
44       * instance to operate.
45       */
46      public EnumUtils() {
47      }
48  
49      /**
50       * <p>Gets the {@code Map} of enums by name.</p>
51       *
52       * <p>This method is useful when you need a map of enums by name.</p>
53       *
54       * @param <E> the type of the enumeration
55       * @param enumClass  the class of the enum to query, not null
56       * @return the modifiable map of enum names to enums, never null
57       */
58      public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
59          final Map<String, E> map = new LinkedHashMap<String, E>();
60          for (final E e: enumClass.getEnumConstants()) {
61              map.put(e.name(), e);
62          }
63          return map;
64      }
65  
66      /**
67       * <p>Gets the {@code List} of enums.</p>
68       *
69       * <p>This method is useful when you need a list of enums rather than an array.</p>
70       *
71       * @param <E> the type of the enumeration
72       * @param enumClass  the class of the enum to query, not null
73       * @return the modifiable list of enums, never null
74       */
75      public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
76          return new ArrayList<E>(Arrays.asList(enumClass.getEnumConstants()));
77      }
78  
79      /**
80       * <p>Checks if the specified name is a valid enum for the class.</p>
81       *
82       * <p>This method differs from {@link Enum#valueOf} in that checks if the name is
83       * a valid enum without needing to catch the exception.</p>
84       *
85       * @param <E> the type of the enumeration
86       * @param enumClass  the class of the enum to query, not null
87       * @param enumName   the enum name, null returns false
88       * @return true if the enum name is valid, otherwise false
89       */
90      public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
91          if (enumName == null) {
92              return false;
93          }
94          try {
95              Enum.valueOf(enumClass, enumName);
96              return true;
97          } catch (final IllegalArgumentException ex) {
98              return false;
99          }
100     }
101 
102     /**
103      * <p>Gets the enum for the class, returning {@code null} if not found.</p>
104      *
105      * <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
106      * for an invalid enum name.</p>
107      *
108      * @param <E> the type of the enumeration
109      * @param enumClass  the class of the enum to query, not null
110      * @param enumName   the enum name, null returns null
111      * @return the enum, null if not found
112      */
113     public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) {
114         if (enumName == null) {
115             return null;
116         }
117         try {
118             return Enum.valueOf(enumClass, enumName);
119         } catch (final IllegalArgumentException ex) {
120             return null;
121         }
122     }
123 
124     /**
125      * <p>Creates a long bit vector representation of the given subset of an Enum.</p>
126      *
127      * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
128      *
129      * <p>Do not use this method if you have more than 64 values in your Enum, as this
130      * would create a value greater than a long can hold.</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 value provides a binary representation of the given set of enum values.
136      * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
137      * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values,
138      *                                  or if any {@code values} {@code null}
139      * @since 3.0.1
140      * @see #generateBitVectors(Class, Iterable)
141      */
142     public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) {
143         checkBitVectorable(enumClass);
144         Validate.notNull(values);
145         long total = 0;
146         for (final E constant : values) {
147             Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED);
148             total |= 1 << constant.ordinal();
149         }
150         return total;
151     }
152 
153     /**
154      * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
155      *
156      * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
157      *
158      * <p>Use this method if you have more than 64 values in your Enum.</p>
159      *
160      * @param enumClass the class of the enum we are working with, not {@code null}
161      * @param values    the values we want to convert, not {@code null}, neither containing {@code null}
162      * @param <E>       the type of the enumeration
163      * @return a long[] whose values provide a binary representation of the given set of enum values
164      *         with least significant digits rightmost.
165      * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
166      * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
167      * @since 3.2
168      */
169     public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) {
170         asEnum(enumClass);
171         Validate.notNull(values);
172         final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
173         for (final E constant : values) {
174             Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED);
175             condensed.add(constant);
176         }
177         final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
178         for (final E value : condensed) {
179             result[value.ordinal() / Long.SIZE] |= 1 << (value.ordinal() % Long.SIZE);
180         }
181         ArrayUtils.reverse(result);
182         return result;
183     }
184 
185     /**
186      * <p>Creates a long bit vector representation of the given array of Enum values.</p>
187      *
188      * <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
189      *
190      * <p>Do not use this method if you have more than 64 values in your Enum, as this
191      * would create a value greater than a long can hold.</p>
192      *
193      * @param enumClass the class of the enum we are working with, not {@code null}
194      * @param values    the values we want to convert, not {@code null}
195      * @param <E>       the type of the enumeration
196      * @return a long whose value provides a binary representation of the given set of enum values.
197      * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
198      * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
199      * @since 3.0.1
200      * @see #generateBitVectors(Class, Iterable)
201      */
202     public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
203         Validate.noNullElements(values);
204         return generateBitVector(enumClass, Arrays.<E> asList(values));
205     }
206 
207     /**
208      * <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
209      *
210      * <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
211      *
212      * <p>Use this method if you have more than 64 values in your Enum.</p>
213      *
214      * @param enumClass the class of the enum we are working with, not {@code null}
215      * @param values    the values we want to convert, not {@code null}, neither containing {@code null}
216      * @param <E>       the type of the enumeration
217      * @return a long[] whose values provide a binary representation of the given set of enum values
218      *         with least significant digits rightmost.
219      * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
220      * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
221      * @since 3.2
222      */
223     public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
224         asEnum(enumClass);
225         Validate.noNullElements(values);
226         final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
227         Collections.addAll(condensed, values);
228         final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
229         for (final E value : condensed) {
230             result[value.ordinal() / Long.SIZE] |= 1 << (value.ordinal() % Long.SIZE);
231         }
232         ArrayUtils.reverse(result);
233         return result;
234     }
235 
236     /**
237      * <p>Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
238      * enum values that it represents.</p>
239      *
240      * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
241      * @param enumClass the class of the enum we are working with, not {@code null}
242      * @param value     the long value representation of a set of enum values
243      * @param <E>       the type of the enumeration
244      * @return a set of enum values
245      * @throws NullPointerException if {@code enumClass} is {@code null}
246      * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
247      * @since 3.0.1
248      */
249     public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
250         checkBitVectorable(enumClass).getEnumConstants();
251         return processBitVectors(enumClass, value);
252     }
253 
254     /**
255      * <p>Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
256      * enum values that it represents.</p>
257      *
258      * <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
259      * @param enumClass the class of the enum we are working with, not {@code null}
260      * @param values     the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null}
261      * @param <E>       the type of the enumeration
262      * @return a set of enum values
263      * @throws NullPointerException if {@code enumClass} is {@code null}
264      * @throws IllegalArgumentException if {@code enumClass} is not an enum class
265      * @since 3.2
266      */
267     public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) {
268         final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
269         long[] lvalues = ArrayUtils.clone(Validate.notNull(values));
270         ArrayUtils.reverse(lvalues);
271         for (final E constant : enumClass.getEnumConstants()) {
272             final int block = constant.ordinal() / Long.SIZE;
273             if (block < lvalues.length && (lvalues[block] & 1 << (constant.ordinal() % Long.SIZE)) != 0) {
274                 results.add(constant);
275             }
276         }
277         return results;
278     }
279 
280     /**
281      * Validate that {@code enumClass} is compatible with representation in a {@code long}.
282      * @param <E> the type of the enumeration
283      * @param enumClass to check
284      * @return {@code enumClass}
285      * @throws NullPointerException if {@code enumClass} is {@code null}
286      * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
287      * @since 3.0.1
288      */
289     private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
290         final E[] constants = asEnum(enumClass).getEnumConstants();
291         Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, 
292             Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE));
293 
294         return enumClass;
295     }
296 
297     /**
298      * Validate {@code enumClass}.
299      * @param <E> the type of the enumeration
300      * @param enumClass to check
301      * @return {@code enumClass}
302      * @throws NullPointerException if {@code enumClass} is {@code null}
303      * @throws IllegalArgumentException if {@code enumClass} is not an enum class
304      * @since 3.2
305      */
306     private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
307         Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
308         Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
309         return enumClass;
310     }
311 }