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    }