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