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 * @version $Id: EnumUtils.java 1557159 2014-01-10 15:41:57Z mbenson $ 034 */ 035public class EnumUtils { 036 037 private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; 038 private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; 039 private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; 040 private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; 041 042 /** 043 * This constructor is public to permit tools that require a JavaBean 044 * instance to operate. 045 */ 046 public EnumUtils() { 047 } 048 049 /** 050 * <p>Gets the {@code Map} of enums by name.</p> 051 * 052 * <p>This method is useful when you need a map of enums by name.</p> 053 * 054 * @param <E> the type of the enumeration 055 * @param enumClass the class of the enum to query, not null 056 * @return the modifiable map of enum names to enums, never null 057 */ 058 public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) { 059 final Map<String, E> map = new LinkedHashMap<String, E>(); 060 for (final E e: enumClass.getEnumConstants()) { 061 map.put(e.name(), e); 062 } 063 return map; 064 } 065 066 /** 067 * <p>Gets the {@code List} of enums.</p> 068 * 069 * <p>This method is useful when you need a list of enums rather than an array.</p> 070 * 071 * @param <E> the type of the enumeration 072 * @param enumClass the class of the enum to query, not null 073 * @return the modifiable list of enums, never null 074 */ 075 public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) { 076 return new ArrayList<E>(Arrays.asList(enumClass.getEnumConstants())); 077 } 078 079 /** 080 * <p>Checks if the specified name is a valid enum for the class.</p> 081 * 082 * <p>This method differs from {@link Enum#valueOf} in that checks if the name is 083 * a valid enum without needing to catch the exception.</p> 084 * 085 * @param <E> the type of the enumeration 086 * @param enumClass the class of the enum to query, not null 087 * @param enumName the enum name, null returns false 088 * @return true if the enum name is valid, otherwise false 089 */ 090 public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) { 091 if (enumName == null) { 092 return false; 093 } 094 try { 095 Enum.valueOf(enumClass, enumName); 096 return true; 097 } catch (final IllegalArgumentException ex) { 098 return false; 099 } 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, long... values) { 268 final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass)); 269 values = ArrayUtils.clone(Validate.notNull(values)); 270 ArrayUtils.reverse(values); 271 for (final E constant : enumClass.getEnumConstants()) { 272 final int block = constant.ordinal() / Long.SIZE; 273 if (block < values.length && (values[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, constants.length, 292 enumClass.getSimpleName(), 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}