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 * https://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.function.ToIntFunction; 028import java.util.stream.Collectors; 029import java.util.stream.Stream; 030 031import org.apache.commons.lang3.stream.Streams; 032 033/** 034 * Provides methods for Java enums. 035 * 036 * <p> 037 * #ThreadSafe# 038 * </p> 039 * 040 * @since 3.0 041 */ 042public class EnumUtils { 043 044 private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; 045 private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; 046 private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; 047 private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; 048 049 /** 050 * Validate {@code enumClass}. 051 * 052 * @param <E> The type of the enumeration. 053 * @param enumClass to check. 054 * @return {@code enumClass}. 055 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}. 056 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class. 057 * @since 3.2 058 */ 059 private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) { 060 Objects.requireNonNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); 061 Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass); 062 return enumClass; 063 } 064 065 /** 066 * Validate that {@code enumClass} is compatible with representation in a {@code long}. 067 * 068 * @param <E> The type of the enumeration. 069 * @param enumClass to check. 070 * @return {@code enumClass}. 071 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}. 072 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values. 073 * @since 3.0.1 074 */ 075 private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) { 076 final E[] constants = asEnum(enumClass).getEnumConstants(); 077 Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, Integer.valueOf(constants.length), enumClass.getSimpleName(), 078 Integer.valueOf(Long.SIZE)); 079 return enumClass; 080 } 081 082 /** 083 * Creates a long bit vector representation of the given array of Enum values. 084 * 085 * <p> 086 * This generates a value that is usable by {@link EnumUtils#processBitVector}. 087 * </p> 088 * 089 * <p> 090 * Do not use this method if you have more than 64 values in your Enum, as this 091 * would create a value greater than a long can hold. 092 * </p> 093 * 094 * @param enumClass The class of the enum we are working with, not {@code null}. 095 * @param values The values we want to convert, not {@code null}. 096 * @param <E> the type of the enumeration. 097 * @return A long whose value provides a binary representation of the given set of enum values. 098 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}. 099 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values. 100 * @since 3.0.1 101 * @see #generateBitVectors(Class, Iterable) 102 */ 103 @SafeVarargs 104 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) { 105 Validate.noNullElements(values); 106 return generateBitVector(enumClass, Arrays.asList(values)); 107 } 108 109 /** 110 * Creates a long bit vector representation of the given subset of an Enum. 111 * 112 * <p> 113 * This generates a value that is usable by {@link EnumUtils#processBitVector}. 114 * </p> 115 * 116 * <p> 117 * Do not use this method if you have more than 64 values in your Enum, as this 118 * would create a value greater than a long can hold. 119 * </p> 120 * 121 * @param enumClass The class of the enum we are working with, not {@code null}. 122 * @param values The values we want to convert, not {@code null}, neither containing {@code null}. 123 * @param <E> the type of the enumeration. 124 * @return A long whose value provides a binary representation of the given set of enum values. 125 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}. 126 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values, 127 * or if any {@code values} {@code null}. 128 * @since 3.0.1 129 * @see #generateBitVectors(Class, Iterable) 130 */ 131 public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) { 132 checkBitVectorable(enumClass); 133 Objects.requireNonNull(values, "values"); 134 long total = 0; 135 for (final E constant : values) { 136 Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED); 137 total |= 1L << constant.ordinal(); 138 } 139 return total; 140 } 141 142 /** 143 * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed. 144 * 145 * <p> 146 * This generates a value that is usable by {@link EnumUtils#processBitVectors}. 147 * </p> 148 * 149 * <p> 150 * Use this method if you have more than 64 values in your Enum. 151 * </p> 152 * 153 * @param enumClass The class of the enum we are working with, not {@code null}. 154 * @param values The values we want to convert, not {@code null}, neither containing {@code null}. 155 * @param <E> the type of the enumeration. 156 * @return A long[] whose values provide a binary representation of the given set of enum values 157 * with the least significant digits rightmost. 158 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}. 159 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class, or if any {@code values} {@code null}. 160 * @since 3.2 161 */ 162 @SafeVarargs 163 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) { 164 asEnum(enumClass); 165 Validate.noNullElements(values); 166 final EnumSet<E> condensed = EnumSet.noneOf(enumClass); 167 Collections.addAll(condensed, values); 168 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; 169 for (final E value : condensed) { 170 result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE; 171 } 172 ArrayUtils.reverse(result); 173 return result; 174 } 175 176 /** 177 * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed. 178 * 179 * <p> 180 * This generates a value that is usable by {@link EnumUtils#processBitVectors}. 181 * </p> 182 * 183 * <p> 184 * Use this method if you have more than 64 values in your Enum. 185 * </p> 186 * 187 * @param enumClass The class of the enum we are working with, not {@code null}. 188 * @param values The values we want to convert, not {@code null}, neither containing {@code null}. 189 * @param <E> the type of the enumeration. 190 * @return A long[] whose values provide a binary representation of the given set of enum values 191 * with the least significant digits rightmost. 192 * @throws NullPointerException Thrown if {@code enumClass} or {@code values} is {@code null}. 193 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class, or if any {@code values} {@code null}. 194 * @since 3.2 195 */ 196 public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) { 197 asEnum(enumClass); 198 Objects.requireNonNull(values, "values"); 199 final EnumSet<E> condensed = EnumSet.noneOf(enumClass); 200 values.forEach(constant -> condensed.add(Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED))); 201 final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; 202 for (final E value : condensed) { 203 result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE; 204 } 205 ArrayUtils.reverse(result); 206 return result; 207 } 208 209 /** 210 * Gets the enum for the class, returning {@code null} if not found. 211 * 212 * <p> 213 * This method differs from {@link Enum#valueOf} in that it does not throw an exception 214 * for an invalid enum name. 215 * </p> 216 * 217 * @param <E> The type of the enumeration. 218 * @param enumClass The class of the enum to query, not null. 219 * @param enumName The enum name, null returns null. 220 * @return The enum, null if not found. 221 */ 222 public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) { 223 return getEnum(enumClass, enumName, null); 224 } 225 226 /** 227 * Gets the enum for the class, returning {@code defaultEnum} if not found. 228 * 229 * <p> 230 * This method differs from {@link Enum#valueOf} in that it does not throw an exception 231 * for an invalid enum name. 232 * </p> 233 * 234 * @param <E> The type of the enumeration. 235 * @param enumClass The class of the enum to query, null returns default enum. 236 * @param enumName The enum name, null returns default enum. 237 * @param defaultEnum The default enum. 238 * @return The enum, default enum if not found. 239 * @since 3.10 240 */ 241 public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName, final E defaultEnum) { 242 if (enumClass == null || enumName == null) { 243 return defaultEnum; 244 } 245 try { 246 return Enum.valueOf(enumClass, enumName); 247 } catch (final IllegalArgumentException e) { 248 return defaultEnum; 249 } 250 } 251 252 /** 253 * Gets the enum for the class, returning {@code null} if not found. 254 * 255 * <p> 256 * This method differs from {@link Enum#valueOf} in that it does not throw an exception 257 * for an invalid enum name and performs case insensitive matching of the name. 258 * </p> 259 * 260 * @param <E> the type of the enumeration. 261 * @param enumClass The class of the enum to query, may be null. 262 * @param enumName The enum name, null returns null. 263 * @return The enum, null if not found. 264 * @since 3.8 265 */ 266 public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName) { 267 return getEnumIgnoreCase(enumClass, enumName, null); 268 } 269 270 /** 271 * Gets the enum for the class, returning {@code defaultEnum} if not found. 272 * 273 * <p> 274 * This method differs from {@link Enum#valueOf} in that it does not throw an exception 275 * for an invalid enum name and performs case insensitive matching of the name. 276 * </p> 277 * 278 * @param <E> the type of the enumeration. 279 * @param enumClass The class of the enum to query, null returns default enum. 280 * @param enumName The enum name, null returns default enum. 281 * @param defaultEnum The default enum. 282 * @return The enum, default enum if not found. 283 * @since 3.10 284 */ 285 public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName, 286 final E defaultEnum) { 287 return getFirstEnumIgnoreCase(enumClass, enumName, Enum::name, defaultEnum); 288 } 289 290 /** 291 * Gets the {@link List} of enums. 292 * 293 * <p> 294 * This method is useful when you need a list of enums rather than an array. 295 * </p> 296 * 297 * @param <E> The type of the enumeration. 298 * @param enumClass The class of the enum to query, not null. 299 * @return The modifiable list of enums, never null. 300 */ 301 public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) { 302 return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants())); 303 } 304 305 /** 306 * Gets the {@link Map} of enums by name. 307 * 308 * <p> 309 * This method is useful when you need a map of enums by name. 310 * </p> 311 * 312 * @param <E> The type of the enumeration. 313 * @param enumClass The class of the enum to query, not null. 314 * @return The modifiable map of enum names to enums, never null. 315 */ 316 public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) { 317 return getEnumMap(enumClass, E::name); 318 } 319 320 /** 321 * Gets the {@link Map} of enums by name. 322 * 323 * <p> 324 * This method is useful when you need a map of enums by name. 325 * </p> 326 * 327 * @param <E> the type of enumeration. 328 * @param <K> the type of the map key. 329 * @param enumClass The class of the enum to query, not null. 330 * @param keyFunction The function to query for the key, not null. 331 * @return The modifiable map of enums, never null. 332 * @since 3.13.0 333 */ 334 public static <E extends Enum<E>, K> Map<K, E> getEnumMap(final Class<E> enumClass, final Function<E, K> keyFunction) { 335 return stream(enumClass).collect(Collectors.toMap(keyFunction::apply, Function.identity())); 336 } 337 338 /** 339 * Gets the enum for the class in a system property, returning {@code defaultEnum} if not found. 340 * 341 * <p> 342 * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name. 343 * </p> 344 * <p> 345 * If a {@link SecurityException} is caught, the return value is {@code null}. 346 * </p> 347 * 348 * @param <E> the type of the enumeration. 349 * @param enumClass The class of the enum to query, not null. 350 * @param propName The system property key for the enum name, null returns default enum. 351 * @param defaultEnum The default enum. 352 * @return The enum, default enum if not found. 353 * @since 3.13.0 354 */ 355 public static <E extends Enum<E>> E getEnumSystemProperty(final Class<E> enumClass, final String propName, final E defaultEnum) { 356 return getEnum(enumClass, SystemProperties.getProperty(propName), defaultEnum); 357 } 358 359 /** 360 * Gets the enum for the class and value, returning {@code defaultEnum} if not found. 361 * 362 * <p> 363 * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name and performs case insensitive matching of 364 * the name. 365 * </p> 366 * 367 * @param <E> the type of the enumeration. 368 * @param enumClass The class of the enum to query, not null. 369 * @param value The enum name, null returns default enum. 370 * @param toIntFunction The function that gets an int for an enum for comparison to {@code value}. 371 * @param defaultEnum The default enum. 372 * @return An enum, default enum if not found. 373 * @since 3.18.0 374 */ 375 public static <E extends Enum<E>> E getFirstEnum(final Class<E> enumClass, final int value, final ToIntFunction<E> toIntFunction, final E defaultEnum) { 376 if (!isEnum(enumClass)) { 377 return defaultEnum; 378 } 379 return stream(enumClass).filter(e -> value == toIntFunction.applyAsInt(e)).findFirst().orElse(defaultEnum); 380 } 381 382 /** 383 * Gets the enum for the class, returning {@code defaultEnum} if not found. 384 * 385 * <p> 386 * This method differs from {@link Enum#valueOf} in that it does not throw an exception 387 * for an invalid enum name and performs case insensitive matching of the name. 388 * </p> 389 * 390 * @param <E> the type of the enumeration. 391 * @param enumClass The class of the enum to query, null returns default enum. 392 * @param enumName The enum name, null returns default enum. 393 * @param stringFunction The function that gets the string for an enum for comparison to {@code enumName}. 394 * @param defaultEnum The default enum. 395 * @return An enum, default enum if not found. 396 * @since 3.13.0 397 */ 398 public static <E extends Enum<E>> E getFirstEnumIgnoreCase(final Class<E> enumClass, final String enumName, final Function<E, String> stringFunction, 399 final E defaultEnum) { 400 if (enumName == null) { 401 return defaultEnum; 402 } 403 return stream(enumClass).filter(e -> enumName.equalsIgnoreCase(stringFunction.apply(e))).findFirst().orElse(defaultEnum); 404 } 405 406 private static <E extends Enum<E>> boolean isEnum(final Class<E> enumClass) { 407 return enumClass != null && enumClass.isEnum(); 408 } 409 410 /** 411 * Tests whether the specified name is a valid enum for the class. 412 * 413 * <p> 414 * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception. 415 * </p> 416 * 417 * @param <E> the type of the enumeration. 418 * @param enumClass The class of the enum to query, null returns false. 419 * @param enumName The enum name, null returns false. 420 * @return true if the enum name is valid, otherwise false. 421 */ 422 public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) { 423 return getEnum(enumClass, enumName) != null; 424 } 425 426 /** 427 * Tests whether the specified name is a valid enum for the class. 428 * 429 * <p> 430 * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception and performs case 431 * insensitive matching of the name. 432 * </p> 433 * 434 * @param <E> the type of the enumeration. 435 * @param enumClass The class of the enum to query, null returns false. 436 * @param enumName The enum name, null returns false. 437 * @return true if the enum name is valid, otherwise false. 438 * @since 3.8 439 */ 440 public static <E extends Enum<E>> boolean isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName) { 441 return getEnumIgnoreCase(enumClass, enumName) != null; 442 } 443 444 /** 445 * Convert a long value created by {@link EnumUtils#generateBitVector} into the set of 446 * enum values that it represents. 447 * 448 * <p> 449 * If you store this value, beware any changes to the enum that would affect ordinal values. 450 * </p> 451 * 452 * @param enumClass The class of the enum we are working with, not {@code null}. 453 * @param value The long value representation of a set of enum values. 454 * @param <E> the type of the enumeration. 455 * @return A set of enum values. 456 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}. 457 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class or has more than 64 values. 458 * @since 3.0.1 459 */ 460 public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) { 461 return processBitVectors(checkBitVectorable(enumClass), value); 462 } 463 464 /** 465 * Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of 466 * enum values that it represents. 467 * 468 * <p> 469 * If you store this value, beware any changes to the enum that would affect ordinal values. 470 * </p> 471 * 472 * @param enumClass The class of the enum we are working with, not {@code null}. 473 * @param values The long[] bearing the representation of a set of enum values, the least significant digits rightmost, not {@code null}. 474 * @param <E> the type of the enumeration. 475 * @return A set of enum values. 476 * @throws NullPointerException Thrown if {@code enumClass} is {@code null}. 477 * @throws IllegalArgumentException Thrown if {@code enumClass} is not an enum class. 478 * @since 3.2 479 */ 480 public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) { 481 final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass)); 482 final long[] lvalues = ArrayUtils.clone(Objects.requireNonNull(values, "values")); 483 ArrayUtils.reverse(lvalues); 484 stream(enumClass).forEach(constant -> { 485 final int block = constant.ordinal() / Long.SIZE; 486 if (block < lvalues.length && (lvalues[block] & 1L << constant.ordinal() % Long.SIZE) != 0) { 487 results.add(constant); 488 } 489 }); 490 return results; 491 } 492 493 /** 494 * Returns a sequential ordered stream whose elements are the given class' enum values. 495 * 496 * @param <T> the type of stream elements. 497 * @param clazz The class containing the enum values, may be null. 498 * @return The new stream, empty of {@code clazz} is null. 499 * @since 3.18.0 500 * @see Class#getEnumConstants() 501 */ 502 public static <T> Stream<T> stream(final Class<T> clazz) { 503 return clazz != null ? Streams.of(clazz.getEnumConstants()) : Stream.empty(); 504 } 505 506 /** 507 * This constructor is public to permit tools that require a JavaBean 508 * instance to operate. 509 * 510 * @deprecated TODO Make private in 4.0. 511 */ 512 @Deprecated 513 public EnumUtils() { 514 // empty 515 } 516}