    001package net.minecraftforge.common;
    002
    003import java.lang.reflect.*;
    004import java.util.*;
    005
    006import net.minecraft.block.EnumMobType;
    007import net.minecraft.block.material.Material;
    008import net.minecraft.enchantment.EnumEnchantmentType;
    009import net.minecraft.entity.EnumCreatureAttribute;
    010import net.minecraft.entity.EnumCreatureType;
    011import net.minecraft.entity.EnumEntitySize;
    012import net.minecraft.entity.player.EnumStatus;
    013import net.minecraft.item.EnumAction;
    014import net.minecraft.item.EnumArmorMaterial;
    015import net.minecraft.item.EnumToolMaterial;
    016import net.minecraft.util.EnumArt;
    017import net.minecraft.util.EnumMovingObjectType;
    018import net.minecraft.world.EnumSkyBlock;
    019import net.minecraft.world.gen.structure.EnumDoor;
    020
    021public 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}