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 * @deprecated TODO Make private in 4.0. 427 */ 428 @Deprecated 429 public EnumUtils() { 430 // empty 431 } 432}