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 }