001 package net.minecraftforge.common; 002 003 import net.minecraft.src.*; 004 import java.lang.reflect.*; 005 import java.util.*; 006 007 public class EnumHelper 008 { 009 private static Object reflectionFactory = null; 010 private static Method newConstructorAccessor = null; 011 private static Method newInstance = null; 012 private static Method newFieldAccessor = null; 013 private static Method fieldAccessorSet = null; 014 private static boolean isSetup = false; 015 016 //Some enums are decompiled with extra arguments, so lets check for that 017 private static Class[][] commonTypes = 018 { 019 {EnumAction.class}, 020 {EnumArmorMaterial.class, int.class, int[].class, int.class}, 021 {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, 022 {EnumCreatureAttribute.class}, 023 {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class}, 024 {EnumDoor.class}, 025 {EnumEnchantmentType.class}, 026 {EnumEntitySize.class}, 027 {EnumMobType.class}, 028 {EnumMovingObjectType.class}, 029 {EnumSkyBlock.class, int.class}, 030 {EnumStatus.class}, 031 {EnumToolMaterial.class, int.class, int.class, float.class, int.class, int.class} 032 }; 033 034 public static EnumAction addAction(String name) 035 { 036 return addEnum(EnumAction.class, name); 037 } 038 public static EnumArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability) 039 { 040 return addEnum(EnumArmorMaterial.class, name, durability, reductionAmounts, enchantability); 041 } 042 public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY) 043 { 044 return addEnum(EnumArt.class, name, tile, sizeX, sizeY, offsetX, offsetY); 045 } 046 public static EnumCreatureAttribute addCreatureAttribute(String name) 047 { 048 return addEnum(EnumCreatureAttribute.class, name); 049 } 050 public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful) 051 { 052 return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful); 053 } 054 public static EnumDoor addDoor(String name) 055 { 056 return addEnum(EnumDoor.class, name); 057 } 058 public static EnumEnchantmentType addEnchantmentType(String name) 059 { 060 return addEnum(EnumEnchantmentType.class, name); 061 } 062 public static EnumEntitySize addEntitySize(String name) 063 { 064 return addEnum(EnumEntitySize.class, name); 065 } 066 public static EnumMobType addMobType(String name) 067 { 068 return addEnum(EnumMobType.class, name); 069 } 070 public static EnumMovingObjectType addMovingObjectType(String name) 071 { 072 if (!isSetup) 073 { 074 setup(); 075 } 076 077 return addEnum(EnumMovingObjectType.class, name); 078 } 079 public static EnumSkyBlock addSkyBlock(String name, int lightValue) 080 { 081 return addEnum(EnumSkyBlock.class, name, lightValue); 082 } 083 public static EnumStatus addStatus(String name) 084 { 085 return addEnum(EnumStatus.class, name); 086 } 087 public static EnumToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, int damage, int enchantability) 088 { 089 return addEnum(EnumToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability); 090 } 091 092 private static void setup() 093 { 094 if (isSetup) 095 { 096 return; 097 } 098 099 try 100 { 101 Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); 102 reflectionFactory = getReflectionFactory.invoke(null); 103 newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class); 104 newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class); 105 newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); 106 fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); 107 } 108 catch (Exception e) 109 { 110 e.printStackTrace(); 111 } 112 113 isSetup = true; 114 } 115 116 /* 117 * Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+ 118 * Also modified for use in decompiled code. 119 * Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically 120 */ 121 private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception 122 { 123 Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2]; 124 parameterTypes[0] = String.class; 125 parameterTypes[1] = int.class; 126 System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); 127 return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes)); 128 } 129 130 private static < T extends Enum<? >> T makeEnum(Class<T> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception 131 { 132 Object[] parms = new Object[additionalValues.length + 2]; 133 parms[0] = value; 134 parms[1] = Integer.valueOf(ordinal); 135 System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); 136 return enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] {parms})); 137 } 138 139 public static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception 140 { 141 field.setAccessible(true); 142 Field modifiersField = Field.class.getDeclaredField("modifiers"); 143 modifiersField.setAccessible(true); 144 modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 145 Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); 146 fieldAccessorSet.invoke(fieldAccessor, target, value); 147 } 148 149 private static void blankField(Class<?> enumClass, String fieldName) throws Exception 150 { 151 for (Field field : Class.class.getDeclaredFields()) 152 { 153 if (field.getName().contains(fieldName)) 154 { 155 field.setAccessible(true); 156 setFailsafeFieldValue(field, enumClass, null); 157 break; 158 } 159 } 160 } 161 162 private static void cleanEnumCache(Class<?> enumClass) throws Exception 163 { 164 blankField(enumClass, "enumConstantDirectory"); 165 blankField(enumClass, "enumConstants"); 166 } 167 168 public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues) 169 { 170 return addEnum(commonTypes, enumType, enumName, paramValues); 171 } 172 173 public static <T extends Enum<? >> T addEnum(Class[][] map, Class<T> enumType, String enumName, Object... paramValues) 174 { 175 for (Class[] lookup : map) 176 { 177 if (lookup[0] == enumType) 178 { 179 Class<?>[] paramTypes = new Class<?>[lookup.length - 1]; 180 if (paramTypes.length > 0) 181 { 182 System.arraycopy(lookup, 1, paramTypes, 0, paramTypes.length); 183 } 184 return addEnum(enumType, enumName, paramTypes, paramValues); 185 } 186 } 187 return null; 188 } 189 190 @SuppressWarnings("unchecked") 191 public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Class<?>[] paramTypes, Object[] paramValues) 192 { 193 if (!isSetup) 194 { 195 setup(); 196 } 197 198 Field valuesField = null; 199 Field[] fields = enumType.getDeclaredFields(); 200 int flags = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; 201 String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); 202 203 for (Field field : fields) 204 { 205 if ((field.getModifiers() & flags) == flags && 206 field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't.. 207 { 208 valuesField = field; 209 break; 210 } 211 } 212 valuesField.setAccessible(true); 213 214 try 215 { 216 T[] previousValues = (T[])valuesField.get(enumType); 217 List<T> values = new ArrayList<T>(Arrays.asList(previousValues)); 218 T newValue = (T)makeEnum(enumType, enumName, values.size(), paramTypes, paramValues); 219 values.add(newValue); 220 setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0))); 221 cleanEnumCache(enumType); 222 223 return newValue; 224 } 225 catch (Exception e) 226 { 227 e.printStackTrace(); 228 throw new RuntimeException(e.getMessage(), e); 229 } 230 } 231 232 static 233 { 234 if (!isSetup) 235 { 236 setup(); 237 } 238 } 239 }