/* * Minecraft Forge * Copyright (c) 2016. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml.relauncher; import com.google.common.base.Preconditions; import net.minecraft.launchwrapper.Launch; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Some reflection helper code. * * @author cpw * */ public class ReflectionHelper { public static class UnableToFindMethodException extends RuntimeException { private static final long serialVersionUID = 1L; //private String[] methodNames; public UnableToFindMethodException(String[] methodNames, Exception failed) { super(failed); //this.methodNames = methodNames; } public UnableToFindMethodException(Throwable failed) { super(failed); } } public static class UnableToFindClassException extends RuntimeException { private static final long serialVersionUID = 1L; //private String[] classNames; public UnableToFindClassException(String[] classNames, @Nullable Exception err) { super(err); //this.classNames = classNames; } } public static class UnableToAccessFieldException extends RuntimeException { private static final long serialVersionUID = 1L; //private String[] fieldNameList; public UnableToAccessFieldException(String[] fieldNames, Exception e) { super(e); //this.fieldNameList = fieldNames; } } public static class UnableToFindFieldException extends RuntimeException { private static final long serialVersionUID = 1L; //private String[] fieldNameList; public UnableToFindFieldException(String[] fieldNameList, Exception e) { super(e); //this.fieldNameList = fieldNameList; } } public static Field findField(Class<?> clazz, String... fieldNames) { Exception failed = null; for (String fieldName : fieldNames) { try { Field f = clazz.getDeclaredField(fieldName); f.setAccessible(true); return f; } catch (Exception e) { failed = e; } } throw new UnableToFindFieldException(fieldNames, failed); } @SuppressWarnings("unchecked") public static <T, E> T getPrivateValue(Class <? super E > classToAccess, @Nullable E instance, int fieldIndex) { try { Field f = classToAccess.getDeclaredFields()[fieldIndex]; f.setAccessible(true); return (T) f.get(instance); } catch (Exception e) { throw new UnableToAccessFieldException(new String[0], e); } } @SuppressWarnings("unchecked") public static <T, E> T getPrivateValue(Class <? super E > classToAccess, E instance, String... fieldNames) { try { return (T) findField(classToAccess, fieldNames).get(instance); } catch (Exception e) { throw new UnableToAccessFieldException(fieldNames, e); } } public static <T, E> void setPrivateValue(Class <? super T > classToAccess, T instance, E value, int fieldIndex) { try { Field f = classToAccess.getDeclaredFields()[fieldIndex]; f.setAccessible(true); f.set(instance, value); } catch (Exception e) { throw new UnableToAccessFieldException(new String[0] , e); } } public static <T, E> void setPrivateValue(Class <? super T > classToAccess, T instance, E value, String... fieldNames) { try { findField(classToAccess, fieldNames).set(instance, value); } catch (Exception e) { throw new UnableToAccessFieldException(fieldNames, e); } } @SuppressWarnings("unchecked") public static Class<? super Object> getClass(ClassLoader loader, String... classNames) { Exception err = null; for (String className : classNames) { try { return (Class<? super Object>) Class.forName(className, false, loader); } catch (Exception e) { err = e; } } throw new UnableToFindClassException(classNames, err); } /** * @deprecated use {@link #findMethod(Class, String, String, Class[])} */ @Deprecated public static <E> Method findMethod(Class<? super E> clazz, E instance, String[] methodNames, Class<?>... methodTypes) { Throwable failed = null; for (String methodName : methodNames) { try { Method m = clazz.getDeclaredMethod(methodName, methodTypes); m.setAccessible(true); return m; } catch (Exception e) { failed = e; } } throw new UnableToFindMethodException(failed); } /** * Finds a method with the specified name and parameters in the given class and makes it accessible. * Note: for performance, store the returned value and avoid calling this repeatedly. * <p> * Throws an exception if the method is not found. * * @param clazz The class to find the method on. * @param methodName The name of the method to find (used in developer environments, i.e. "getWorldTime"). * @param methodObfName The obfuscated name of the method to find (used in obfuscated environments, i.e. "func_72820_D"). * If the name you are looking for is on a class that is never obfuscated, this should be null. * @param parameterTypes The parameter types of the method to find. * @return The method with the specified name and parameters in the given class. */ @Nonnull public static Method findMethod(@Nonnull Class<?> clazz, @Nonnull String methodName, @Nullable String methodObfName, Class<?>... parameterTypes) { Preconditions.checkNotNull(clazz); Preconditions.checkArgument(StringUtils.isNotEmpty(methodName), "Method name cannot be empty"); String nameToFind; if (methodObfName == null || (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) { nameToFind = methodName; } else { nameToFind = methodObfName; } try { Method m = clazz.getDeclaredMethod(nameToFind, parameterTypes); m.setAccessible(true); return m; } catch (Exception e) { throw new UnableToFindMethodException(e); } } }