    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    }