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    }