fieldType, Class>... parameters) {
+ if(name != null && !field.getName().equals(name))
+ return false;
+
+ if(!fieldType.isAssignableFrom(field.getType()))
+ return false;
+
+ if(parameters.length > 0) {
+ Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
+
+ for(int i = 0; i < parameters.length; i++) {
+ if(arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i])
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Search for the first publicly and privately defined method of the given name and parameter count.
+ *
+ * @param className - lookup name of the class, see {@link #getClass(String)}.
+ * @param methodName - the method name, or NULL to skip.
+ * @param params - the expected parameters.
+ * @return An object that invokes this specific method.
+ * @throws IllegalStateException If we cannot find this method.
+ */
+ public static MethodInvoker getMethod(String className, String methodName, Class>... params) {
+ return getTypedMethod(getClass(className), methodName, null, params);
+ }
+
+ /**
+ * Search for the first publicly and privately defined method of the given name and parameter count.
+ *
+ * @param clazz - a class to start with.
+ * @param methodName - the method name, or NULL to skip.
+ * @param params - the expected parameters.
+ * @return An object that invokes this specific method.
+ * @throws IllegalStateException If we cannot find this method.
+ */
+ public static MethodInvoker getMethod(Class> clazz, String methodName, Class>... params) {
+ return getTypedMethod(clazz, methodName, null, params);
+ }
+
+ /**
+ * Search for the first publicly and privately defined method of the given name and parameter count.
+ *
+ * @param clazz - a class to start with.
+ * @param methodName - the method name, or NULL to skip.
+ * @param returnType - the expected return type, or NULL to ignore.
+ * @param params - the expected parameters.
+ * @return An object that invokes this specific method.
+ * @throws IllegalStateException If we cannot find this method.
+ */
+ public static MethodInvoker getTypedMethod(Class> clazz, String methodName, Class> returnType, Class>... params) {
+ for (final Method method : clazz.getDeclaredMethods()) {
+ if ((methodName == null || method.getName().equals(methodName))
+ && (returnType == null || method.getReturnType().equals(returnType))
+ && Arrays.equals(method.getParameterTypes(), params)) {
+ method.setAccessible(true);
+
+ return (target, arguments) -> {
+ try {
+ return method.invoke(target, arguments);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot invoke method " + method, e);
+ }
+ };
+ }
+ }
+
+ // Search in every superclass
+ if (clazz.getSuperclass() != null)
+ return getTypedMethod(clazz.getSuperclass(), methodName, returnType, params);
+
+ throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
+ }
+
+ /**
+ * Search for the first publically and privately defined constructor of the given name and parameter count.
+ *
+ * @param className - lookup name of the class, see {@link #getClass(String)}.
+ * @param params - the expected parameters.
+ * @return An object that invokes this constructor.
+ * @throws IllegalStateException If we cannot find this method.
+ */
+ public static ConstructorInvoker getConstructor(String className, Class>... params) {
+ return getConstructor(getClass(className), params);
+ }
+
+ /**
+ * Search for the first publically and privately defined constructor of the given name and parameter count.
+ *
+ * @param clazz - a class to start with.
+ * @param params - the expected parameters.
+ * @return An object that invokes this constructor.
+ * @throws IllegalStateException If we cannot find this method.
+ */
+ public static ConstructorInvoker getConstructor(Class> clazz, Class>... params) {
+ for (final Constructor> constructor : clazz.getDeclaredConstructors()) {
+ if (Arrays.equals(constructor.getParameterTypes(), params)) {
+ constructor.setAccessible(true);
+
+ return arguments -> {
+ try {
+ return constructor.newInstance(arguments);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot invoke constructor " + constructor, e);
+ }
+ };
+ }
+ }
+
+ throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
+ }
+
+ /**
+ * Retrieve a class from its full name, without knowing its type on compile time.
+ *
+ * This is useful when looking up fields by a NMS or OBC type.
+ *
+ *
+ * @param lookupName - the class name with variables.
+ * @return The class.
+ */
+ public static Class