    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    }