6e90b73a289b6364b72e560e9360faa74d71efb9.svn-base 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package cn.com.goldenwater.dcproj.enums;
  2. import sun.reflect.ConstructorAccessor;
  3. import sun.reflect.FieldAccessor;
  4. import sun.reflect.ReflectionFactory;
  5. import java.lang.reflect.AccessibleObject;
  6. import java.lang.reflect.Array;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.Modifier;
  9. import java.util.*;
  10. public class EnumToolService {
  11. private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
  12. public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes, Object[] additionalValues) {
  13. // 0. Sanity checks
  14. if (!Enum.class.isAssignableFrom(enumType)) {
  15. throw new RuntimeException("class " + enumType + " is not an instance of Enum");
  16. }
  17. // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
  18. Field valuesField = null;
  19. Field[] fields = enumType.getDeclaredFields();
  20. for (Field field : fields) {
  21. if (field.getName().contains("$VALUES")) {
  22. valuesField = field;
  23. break;
  24. }
  25. }
  26. AccessibleObject.setAccessible(new Field[]{valuesField}, true);
  27. try {
  28. // 2. Copy it
  29. T[] previousValues = (T[]) valuesField.get(enumType);
  30. List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
  31. // 3. build new enum
  32. T newValue = (T) makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues);
  33. // 4. add new value
  34. values.add(newValue);
  35. // 5. Set new values field
  36. setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
  37. // 6. Clean enum cache
  38. cleanEnumCache(enumType);
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. throw new RuntimeException(e.getMessage(), e);
  42. }
  43. }
  44. private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException {
  45. blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
  46. blankField(enumClass, "enumConstants"); // IBM JDK
  47. }
  48. private static void blankField(Class<?> enumClass, String fieldName) throws NoSuchFieldException,
  49. IllegalAccessException {
  50. for (Field field : Class.class.getDeclaredFields()) {
  51. if (field.getName().contains(fieldName)) {
  52. AccessibleObject.setAccessible(new Field[]{field}, true);
  53. setFailsafeFieldValue(field, enumClass, null);
  54. break;
  55. }
  56. }
  57. }
  58. private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException,
  59. IllegalAccessException {
  60. // let's make the field accessible
  61. field.setAccessible(true);
  62. // next we change the modifier in the Field instance to
  63. // not be final anymore, thus tricking reflection into
  64. // letting us modify the static final field
  65. Field modifiersField = Field.class.getDeclaredField("modifiers");
  66. modifiersField.setAccessible(true);
  67. int modifiers = modifiersField.getInt(field);
  68. // blank out the final bit in the modifiers int
  69. modifiers &= ~Modifier.FINAL;
  70. modifiersField.setInt(field, modifiers);
  71. FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
  72. fa.set(target, value);
  73. }
  74. private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes,
  75. Object[] additionalValues) throws Exception {
  76. Object[] parms = new Object[additionalValues.length + 2];
  77. parms[0] = value;
  78. parms[1] = Integer.valueOf(ordinal);
  79. System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
  80. return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
  81. }
  82. private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
  83. throws NoSuchMethodException {
  84. Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
  85. parameterTypes[0] = String.class;
  86. parameterTypes[1] = int.class;
  87. System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
  88. return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
  89. }
  90. }