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