<commit_before>package ws.m4ktub.quacking.helpers;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ws.m4ktub.quacking.DuckWing;
import ws.m4ktub.quacking.Invocation;
import ws.m4ktub.quacking.Mixed;
import ws.m4ktub.quacking.Mixed.MethodConfiguration;

/**
 * Utility methods related with reflection classes.
 * 
 * @author m4ktub
 */
public final class Reflections {

	private static Class<?>[][] PRIMITIVE_TYPES = new Class<?>[][] { { boolean.class, Boolean.class }, { byte.class, Byte.class }, { char.class, Character.class },
			{ double.class, Double.class }, { float.class, Float.class }, { int.class, Integer.class }, { long.class, Long.class }, { short.class, Short.class },
			{ void.class, Void.class } };

	/**
	 * Verify if a method is accessible and make it accessible if needed. If the
	 * method is public or is already accessible then nothing is done.
	 * 
	 * @param method
	 *            The method to make accessible.
	 * @throws SecurityException
	 *             If the method is not accessible and there is a security
	 *             manager that forbids the change in the method's
	 *             accessibility.
	 */
	public static void ensureAccessible(Method method) {
		boolean isMethodPublic = Modifier.isPublic(method.getModifiers());
		boolean isClassPublic = Modifier.isPublic(method.getDeclaringClass().getModifiers());
		if ((!isMethodPublic || !isClassPublic) && !method.isAccessible()) {
			method.setAccessible(true);
		}
	}

	/**
	 * Obtains a compatible method from a, possibly unrelated, object instance.
	 * <p>
	 * A compatible method is a method:
	 * <ol>
	 * <li>with the same name,
	 * <li>with compatible argument types, and
	 * <li>with a compatible return type.
	 * </ol>
	 * <p>
	 * In this sense, compatible is more general than standard Java rules. For
	 * example, a method is compatible as long it is possible to call it, via
	 * reflection, without explicitly converting values between types. This
	 * means that primitive wrapper types are considered compatible because the
	 * Reflection API handles that case implicitly.
	 * <p>
	 * If the object implements the given method then the same method is
	 * returned as it is considered the most compatible method found. Otherwise,
	 * a multi-dispatch resolution is used. From all methods with the same name,
	 * the method with the most specific parameter types matching the actual
	 * argument's types will be used.
	 * <p>
	 * In all cases, the method configurations of the mixed object are taken
	 * into account. For example, the resulting method may have a different name
	 * if the mixed object has a rename configuration. It may also not accept
	 * the given arguments if there is a curry configuration. If you do not
	 * control the mixed object you should use this method only to extract a
	 * compatible method.
	 * 
	 * @param object
	 *            The mixed instance where to look for a compatible method.
	 * @param intfMethod
	 *            The template method used to obtain the name and types.
	 * @param args
	 *            The actual arguments that will be used in the invocation.
	 *            Argument's types will be used to determine candidate methods
	 *            in a multi-dispatch resolution.
	 * @return The compatible method found, or <code>null</code> if no
	 *         compatible method was found.
	 */
	public static Method getCompatibleMethod(Mixed object, Method intfMethod, Object[] args) {
		Invocation invocation = getMethodInvocation(object, intfMethod, args);
		return invocation == null ? null : invocation.getMethod();
	}

	/**
	 * Obtains am invocation for a compatible method from a, possibly unrelated,
	 * object instance.
	 * <p>
	 * This method is similar to
	 * {@link #getCompatibleMethod(Mixed, Method, Object[])}. Check the other
	 * method for how a compatible method is determined. The main difference is
	 * the invocation is not unwrapped, which gives the caller more flexibility.
	 * The invocation reflects all the configuration (rename, curry, and around)
	 * the mixed object contains for the given method.
	 * 
	 * @param object
	 *            The mixed instance where to look for a compatible method.
	 * @param intfMethod
	 *            The template method used to obtain the name and types.
	 * @param args
	 *            The actual arguments that will be used in the invocation.
	 *            Argument's types will be used to determine candidate methods
	 *            in a multi-dispatch resolution.
	 * @return An invocation that is prepared to proceed with a compatible
	 *         method that is invoked in the mixed instance with the given
	 *         arguments..
	 */
	public static Invocation getMethodInvocation(Mixed object, Method intfMethod, Object[] args) {
		Object instance = object.getInstance();
		Object[] methodArgs = args;

		if (methodArgs == null) {
			methodArgs = new Object[0];
		}

		String methodName = intfMethod.getName();
		Class<?>[] intfMethodParameterTypes = intfMethod.getParameterTypes();
		Type[] intfMultiParameterTypes = getMultiDispatchParameterTypes(intfMethod, methodArgs);

		Class<?>[] parameterTypes = intfMethodParameterTypes;
		Type[] multiParameterTypes = intfMultiParameterTypes;

		DuckWing wing = null;

		if (object.hasConfigurationFor(methodName)) {
			MethodConfiguration objMethodConf = object.getConfigurationFor(methodName);

			if (objMethodConf.isRenamed()) {
				methodName = objMethodConf.getRename();
			}

			if (objMethodConf.isCurried()) {
				Class<?>[] curriedTypes = objMethodConf.getCurriedTypes();
				Object[] curriedArgs = objMethodConf.getCurriedArgs();

				parameterTypes = new Class<?>[curriedArgs.length + args.length];
				multiParameterTypes = new Class<?>[curriedArgs.length + args.length];
				methodArgs = new Object[curriedArgs.length + args.length];<commit_msg>Fixed invocation of lambda expressions.<commit_after>package ws.m4ktub.quacking.helpers;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ws.m4ktub.quacking.DuckWing;
import ws.m4ktub.quacking.Invocation;
import ws.m4ktub.quacking.Mixed;
import ws.m4ktub.quacking.Mixed.MethodConfiguration;

/**
 * Utility methods related with reflection classes.
 * 
 * @author m4ktub
 */
public final class Reflections {

	private static Class<?>[][] PRIMITIVE_TYPES = new Class<?>[][] { { boolean.class, Boolean.class }, { byte.class, Byte.class }, { char.class, Character.class },
			{ double.class, Double.class }, { float.class, Float.class }, { int.class, Integer.class }, { long.class, Long.class }, { short.class, Short.class },
			{ void.class, Void.class } };

	/**
	 * Verify if a method is accessible and make it accessible if needed. If the
	 * method is public or is already accessible then nothing is done.
	 * 
	 * @param method
	 *            The method to make accessible.
	 * @throws SecurityException
	 *             If the method is not accessible and there is a security
	 *             manager that forbids the change in the method's
	 *             accessibility.
	 */
	public static void ensureAccessible(Method method) {
		boolean isMethodPublic = Modifier.isPublic(method.getModifiers());
		boolean isClassPublic = Modifier.isPublic(method.getDeclaringClass().getModifiers());
		if ((!isMethodPublic || !isClassPublic) && !method.isAccessible()) {
			method.setAccessible(true);
		}
	}

	/**
	 * Obtains a compatible method from a, possibly unrelated, object instance.
	 * <p>
	 * A compatible method is a method:
	 * <ol>
	 * <li>with the same name,
	 * <li>with compatible argument types, and
	 * <li>with a compatible return type.
	 * </ol>
	 * <p>
	 * In this sense, compatible is more general than standard Java rules. For
	 * example, a method is compatible as long it is possible to call it, via
	 * reflection, without explicitly converting values between types. This
	 * means that primitive wrapper types are considered compatible because the
	 * Reflection API handles that case implicitly.
	 * <p>
	 * If the object implements the given method then the same method is
	 * returned as it is considered the most compatible method found. Otherwise,
	 * a multi-dispatch resolution is used. From all methods with the same name,
	 * the method with the most specific parameter types matching the actual
	 * argument's types will be used.
	 * <p>
	 * In all cases, the method configurations of the mixed object are taken
	 * into account. For example, the resulting method may have a different name
	 * if the mixed object has a rename configuration. It may also not accept
	 * the given arguments if there is a curry configuration. If you do not
	 * control the mixed object you should use this method only to extract a
	 * compatible method.
	 * 
	 * @param object
	 *            The mixed instance where to look for a compatible method.
	 * @param intfMethod
	 *            The template method used to obtain the name and types.
	 * @param args
	 *            The actual arguments that will be used in the invocation.
	 *            Argument's types will be used to determine candidate methods
	 *            in a multi-dispatch resolution.
	 * @return The compatible method found, or <code>null</code> if no
	 *         compatible method was found.
	 */
	public static Method getCompatibleMethod(Mixed object, Method intfMethod, Object[] args) {
		Invocation invocation = getMethodInvocation(object, intfMethod, args);
		return invocation == null ? null : invocation.getMethod();
	}

	/**
	 * Obtains am invocation for a compatible method from a, possibly unrelated,
	 * object instance.
	 * <p>
	 * This method is similar to
	 * {@link #getCompatibleMethod(Mixed, Method, Object[])}. Check the other
	 * method for how a compatible method is determined. The main difference is
	 * the invocation is not unwrapped, which gives the caller more flexibility.
	 * The invocation reflects all the configuration (rename, curry, and around)
	 * the mixed object contains for the given method.
	 * 
	 * @param object
	 *            The mixed instance where to look for a compatible method.
	 * @param intfMethod
	 *            The template method used to obtain the name and types.
	 * @param args
	 *            The actual arguments that will be used in the invocation.
	 *            Argument's types will be used to determine candidate methods
	 *            in a multi-dispatch resolution.
	 * @return An invocation that is prepared to proceed with a compatible
	 *         method that is invoked in the mixed instance with the given
	 *         arguments..
	 */
	public static Invocation getMethodInvocation(Mixed object, Method intfMethod, Object[] args) {
		Object instance = object.getInstance();
		Object[] methodArgs = args;

		if (methodArgs == null) {
			methodArgs = new Object[0];
		}

		String methodName = intfMethod.getName();
		Class<?>[] intfMethodParameterTypes = intfMethod.getParameterTypes();
		Type[] intfMultiParameterTypes = getMultiDispatchParameterTypes(intfMethod, methodArgs);

		Class<?>[] parameterTypes = intfMethodParameterTypes;
		Type[] multiParameterTypes = intfMultiParameterTypes;

		DuckWing wing = null;

		if (!object.hasConfigurationFor(methodName)) {
			if (intfMethod.getDeclaringClass().isAssignableFrom(instance.getClass())) {
				return new Invocation(instance, wing, intfMethod, methodArgs);
			}
		} else {
			MethodConfiguration objMethodConf = object.getConfigurationFor(methodName);

			if (objMethodConf.isRenamed()) {
				methodName = objMethodConf.getRename();
			}

			if (objMethodConf.isCurried()) {
				Class<?>[] curriedTypes = objMethodConf.getCurriedTypes();
				Object[] curriedArgs = objMethodConf.getCurriedArgs();

				parameterTypes = new Class<?>[curriedArgs.length + args.length];
				multiParameterTypes = new Class<?>[curriedArgs.length + args.length];
				methodArgs = new Object[curriedArgs.length + args.length];
{'fraction_non_alphanumeric': 0.08721742704480066, 'fraction_numerical': 0.0011508425811755036, 'mean_word_length': 3.5463378176382663, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2142169', 'n_tokens_mistral': 3437, 'n_tokens_neox': 3113, 'n_words': 1254}