packagemichael.spica.tool.lang;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;importjava.security.AccessController;importjava.security.PrivilegedAction;importjava.util.HashMap;importjava.util.Map;/**
* Created by michael on 2015-11-26.
*/publicabstractclassEnumSupportextendsEnumSupportWithDynamic{publicEnumSupport(){}protectedstatic<TextendsEnum<?>>Map<Object,T>enumConstantDirectory(Class<T> clazz,String methodName){Map<Object,T> directory =null;if(directory ==null){T[] universe =getEnumConstantsShared(clazz);if(universe ==null){thrownewIllegalArgumentException(clazz.getName()+" is not an enum type.");}Map<Object,T> m =newHashMap<Object,T>(2* universe.length);for(T constant : universe)try{Object key =null;if(methodName !=null&& methodName.trim().length()>0){Method method = constant.getClass().getMethod(methodName);
key = method.invoke(constant);}else{
key =(constant).name();// 默认KEY为ENUM的NAME}
m.put(key, constant);}catch(NoSuchMethodException e){
e.printStackTrace();}catch(InvocationTargetException e){
e.printStackTrace();}catch(IllegalAccessException e){
e.printStackTrace();}
directory = m;}return directory;}privatestatic<TextendsEnum<?>>T[]getEnumConstantsShared(Class<T> clazz){T[] enumConstants =null;if(enumConstants ==null){if(!clazz.isEnum())returnnull;try{finalMethod values = clazz.getMethod("values");AccessController.doPrivileged((PrivilegedAction<Void>)()->{
values.setAccessible(true);returnnull;});T[] temporaryConstants =(T[]) values.invoke(null);
enumConstants = temporaryConstants;}catch(InvocationTargetException e){
e.printStackTrace();}catch(NoSuchMethodException e){
e.printStackTrace();}catch(SecurityException e){
e.printStackTrace();}catch(IllegalAccessException e){
e.printStackTrace();}catch(IllegalArgumentException e){
e.printStackTrace();}}return enumConstants;}}
packagemichael.spica.tool.lang;importorg.apache.commons.lang3.StringUtils;importsun.reflect.ConstructorAccessor;importsun.reflect.FieldAccessor;importsun.reflect.ReflectionFactory;importjava.lang.reflect.AccessibleObject;importjava.lang.reflect.Array;importjava.lang.reflect.Field;importjava.lang.reflect.Modifier;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;importjava.util.stream.Stream;/**
* 枚举动态支持(即:程序运行时动态增加枚举类型)
* <p>
* Created by michael on 2015-11-26.
*/publicabstractclassEnumSupportWithDynamic{publicEnumSupportWithDynamic(){}privatestaticReflectionFactory reflectionFactory =ReflectionFactory.getReflectionFactory();publicstatic<TextendsEnum<?>>voidaddEnum(Class<T> enumClass,List<String> enumNames){
enumNames.stream().filter(enumName ->StringUtils.isNotEmpty(enumName)).forEach(enumName ->addEnum(enumClass, enumName));}publicstatic<TextendsEnum<?>>voidaddEnum(Class<T> enumClass,String... enumNames){Stream.of(enumNames).filter(enumName ->StringUtils.isNotEmpty(enumName)).forEach(enumName ->addEnum(enumClass, enumName));}/**
* 添加枚举
*
* @param enumClass 枚举类
* @param enumName 枚举名
* @param <T>
*/publicstatic<TextendsEnum<?>>voidaddEnum(Class<T> enumClass,String enumName){if(StringUtils.isEmpty(enumName)){thrownewRuntimeException("Enumeration name cannot be empty.");}// 0. Sanity checksif(!Enum.class.isAssignableFrom(enumClass)){thrownewRuntimeException("class "+ enumClass +" is not an instance of Enum.");}// 1. Lookup "$VALUES" holder in enum class and get previous enum instancesField valuesField =null;Field[] fields = enumClass.getDeclaredFields();for(Field field : fields){if(field.getName().contains("$VALUES")){
valuesField = field;break;}}AccessibleObject.setAccessible(newField[]{valuesField},true);try{// 2. Copy itT[] previousValues =(T[]) valuesField.get(enumClass);List values =newArrayList(Arrays.asList(previousValues));// 3. build new enumT newValue =(T)makeEnum(enumClass,// The target enum class
enumName,// THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(),newClass[]{},// can be used to pass values to the enum constuctornewObject[]{});// can be used to pass values to the enum constuctor// 4. add new value
values.add(newValue);// 5. Set new values fieldsetFailsafeFieldValue(valuesField,null, values.toArray((T[])Array.newInstance(enumClass,0)));// 6. Clean enum cachecleanEnumCache(enumClass);}catch(Exception e){
e.printStackTrace();thrownewRuntimeException(e.getMessage(), e);}}privatestaticObjectmakeEnum(Class<?> enumClass,String value,int ordinal,Class<?>[] additionalTypes,Object[] additionalValues)throwsException{Object[] parms =newObject[additionalValues.length +2];
parms[0]= value;
parms[1]=Integer.valueOf(ordinal);System.arraycopy(additionalValues,0, parms,2, additionalValues.length);return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));}privatestaticConstructorAccessorgetConstructorAccessor(Class<?> enumClass,Class<?>[] additionalParameterTypes)throwsNoSuchMethodException{Class<?>[] parameterTypes =newClass[additionalParameterTypes.length +2];
parameterTypes[0]=String.class;
parameterTypes[1]=int.class;System.arraycopy(additionalParameterTypes,0, parameterTypes,2, additionalParameterTypes.length);return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));}privatestaticvoidsetFailsafeFieldValue(Field field,Object target,Object value)throwsNoSuchFieldException,IllegalAccessException{// let's make the field accessible
field.setAccessible(true);// next we change the modifier in the Field instance to// not be final anymore, thus tricking reflection into// letting us modify the static final fieldField modifiersField =Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);int modifiers = modifiersField.getInt(field);// blank out the final bit in the modifiers int
modifiers &=~Modifier.FINAL;
modifiersField.setInt(field, modifiers);FieldAccessor fa = reflectionFactory.newFieldAccessor(field,false);
fa.set(target, value);}/**
* 清除枚举缓存
*
* @param enumClass
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/privatestaticvoidcleanEnumCache(Class<?> enumClass)throwsNoSuchFieldException,IllegalAccessException{blankField(enumClass,"enumConstantDirectory");// Sun (Oracle?!?) JDK 1.5/6blankField(enumClass,"enumConstants");// IBM JDK}privatestaticvoidblankField(Class<?> enumClass,String fieldName)throwsNoSuchFieldException,IllegalAccessException{for(Field field :Class.class.getDeclaredFields()){if(field.getName().contains(fieldName)){AccessibleObject.setAccessible(newField[]{field},true);setFailsafeFieldValue(field, enumClass,null);break;}}}}