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 }