1 import java.lang.reflect.AccessibleObject;
2 import java.lang.reflect.Array;
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Modifier;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.List;
8
9 import sun.reflect.ConstructorAccessor;
10 import sun.reflect.FieldAccessor;
11 import sun.reflect.ReflectionFactory;
12
13 /**
14 * @Description: 动态枚举操作工具
15 * @author wly
16 * @date 2019年11月29日
17 */
18 @SuppressWarnings("restriction")
19 public class DynamicEnumUtils {
20 private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
21
22 private static void setFailsafeFieldValue(Field field, Object target, Object value)
23 throws NoSuchFieldException, IllegalAccessException {
24
25 field.setAccessible(true);
26
27 Field modifiersField = Field.class.getDeclaredField("modifiers");
28 modifiersField.setAccessible(true);
29 int modifiers = modifiersField.getInt(field);
30
31 modifiers &= ~Modifier.FINAL;
32 modifiersField.setInt(field, modifiers);
33
34 FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
35 fa.set(target, value);
36 }
37
38 private static void blankField(Class<?> enumClass, String fieldName)
39 throws NoSuchFieldException, IllegalAccessException {
40 for (Field field : Class.class.getDeclaredFields()) {
41 if (field.getName().contains(fieldName)) {
42 AccessibleObject.setAccessible(new Field[] {field}, true);
43 setFailsafeFieldValue(field, enumClass, null);
44 break;
45 }
46 }
47 }
48
49 private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException {
50 blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
51 blankField(enumClass, "enumConstants"); // IBM JDK
52 }
53
54 private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
55 throws NoSuchMethodException {
56 Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
57 parameterTypes[0] = String.class;
58 parameterTypes[1] = int.class;
59 System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
60 return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
61 }
62
63 private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes,
64 Object[] additionalValues) throws Exception {
65 Object[] parms = new Object[additionalValues.length + 2];
66 parms[0] = value;
67 parms[1] = Integer.valueOf(ordinal);
68 System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
69 return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
70 }
71
72 @SuppressWarnings("unchecked")
73 public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes,
74 Object[] additionalValues) {
75
76 // 判断该枚举类是否继承自枚举Enum
77 if (!Enum.class.isAssignableFrom(enumType)) {
78 throw new RuntimeException("class " + enumType + " is not an instance of Enum");
79 }
80
81 // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
82 Field valuesField = null;
83 Field[] fields = enumType.getDeclaredFields();
84 for (Field field : fields) {
85 if (field.getName().contains("$VALUES")) {
86 valuesField = field;
87 break;
88 }
89 }
90 AccessibleObject.setAccessible(new Field[] {valuesField}, true);
91
92 try {
93
94 // 2. Copy it
95 T[] previousValues = (T[])valuesField.get(enumType);
96 List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
97
98 // 3. build new enum
99 T newValue = (T)makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues);
100
101 // 4. add new value
102 values.add(newValue);
103
104 // 5. Set new values field
105 setFailsafeFieldValue(valuesField, null, values.toArray((T[])Array.newInstance(enumType, 0)));
106
107 // 6. Clean enum cache
108 cleanEnumCache(enumType);
109
110 } catch (Exception e) {
111 throw new RuntimeException(e.getMessage(), e);
112 }
113 }
114 }