/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;
import java.lang.reflect.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
	
	private static Object invokeMethodPrivateClassBase(
			Class<?> clazz,
			Object instance,
        	String nameMethod, 
        	List<Class<?>> paramsTypeClazz, 
        	Object... args) {
	    try {
	    	//localiza o método
	        Method method = clazz.getDeclaredMethod(
	        	nameMethod, 
	        	paramsTypeClazz.toArray(new Class[paramsTypeClazz.size()]));
	        //torna-o acessível mesmo sendo private
	        method.setAccessible(true);
	        //verifica se tem argumentos variados
	        if (method.isVarArgs()) {
	        	//cria um arrau de parâmetros que deve ter a exata quantidade de parâmetros declarados,
	        	//isto é, sem considerar os argumentos variantes
	        	Object[] parameters = new Object[paramsTypeClazz.size()];
	        	//copia os parâmetros fixos para o novo array, desconsiderando apenas o último que é variante
	        	System.arraycopy(args, 0, parameters, 0, paramsTypeClazz.size() - 1);
	        	//calcula quantos argumentos variantes foram passados
	        	int varArgsSize = args.length - paramsTypeClazz.size() + 1;
	        	//cria um array com o tipo dos parâmetros variantes
	        	Object varArgsParameter = Array.newInstance(
	        		paramsTypeClazz.get(paramsTypeClazz.size() - 1).getComponentType(), 
	        		varArgsSize);
	        	//copia todos os demais argumentos para o array de argumentos variantes
	        	System.arraycopy(args, paramsTypeClazz.size() - 1, varArgsParameter, 0, varArgsSize);
	        	//coloca o array de argumentos variantes como último parâmetro para o método a ser chamado
	        	//isso porque o "..." equivale a um array
	        	parameters[paramsTypeClazz.size() - 1] = varArgsParameter;
	        	//chama o método
	        	return method.invoke(instance, parameters);
	        } else {
	        	return method.invoke(instance, args);
	        }
	    } catch (Throwable e) {
	    	//apenas para teste
	    	System.out.println(e.getMessage());
	    }
	    return "Y";
	}
	
	/**
	 * Método de exemplo a ser chamado via reflexão 
	 */
	public String meuMetodo(String label, Integer... valores) {
		Integer soma = 0;
		for (Integer v : valores) soma += v;
		return label + ": " + soma;
	}
	
	public static void main (String[] args) throws java.lang.Exception {
		
		//modo normal
		System.out.println(new Ideone().meuMetodo("S1", 3, 4, 1));
		
		//prepara lista de tipos dos argumentos
		List<Class<?>> paramsTypeClazz = new ArrayList<Class<?>>();
		paramsTypeClazz.add(String.class);
		paramsTypeClazz.add(Integer[].class);
		
		//modo reflexivo
		System.out.println(Ideone.invokeMethodPrivateClassBase(
			Ideone.class,
			new Ideone(),
			"meuMetodo",
			paramsTypeClazz,
			"S1", 3, 4, 1));
	}
}