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}