//start of MethodUtil.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
 * MethodUtil.java
 * 
 * Copyright (C) 2002  Michel Ishizuka  All rights reserved.
 * 
 * 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
 * 変更の有無にかかわらず許可する。
 * 
 * １．ソースコードの再配布において著作権表示と この条件のリスト
 *     および下記の声明文を保持しなくてはならない。
 * 
 * ２．バイナリ形式の再配布において著作権表示と この条件のリスト
 *     および下記の声明文を使用説明書もしくは その他の配布物内に
 *     含む資料に記述しなければならない。
 * 
 * このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
 * 的を達成できるという保証、商品価値が有るという保証にとどまらず、
 * いかなる明示的および暗示的な保証もしない。
 * 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
 * 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
 * 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
 * サービスの導入費等が考えられるが、決してそれだけに限定されない
 * 損害)に対して、いかなる事態の原因となったとしても、契約上の責
 * 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
 * 正行為のためであったとしても、またはそのような損害の可能性が報
 * 告されていたとしても一切の責任を負わないものとする。
 */
package jp.gr.java_conf.dangan.lang.reflect;

//import classes and interfaces
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * メソッドに関するユーティリティクラス。
 * 
 * <pre>
 * -- revision history --
 * $Log: MethodUtil.java,v $
 * Revision 1.0  2002/10/01 00:00:00  dangan
 * first edition
 * add to version control
 *
 * </pre>
 * 
 * @author  $Author: dangan $
 * @version $Revision: 1.0 $
 */
public class MethodUtil {
	//------------------------------------------------------------------
	//  constructor
	//------------------------------------------------------------------
	//  private MethodUtil()
	//------------------------------------------------------------------
	/**
	 * デフォルトコンストラクタ。
	 * 使用不可。
	 */
	private MethodUtil() {
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  invoke static method
	//------------------------------------------------------------------
	//  public static Object invoke( Object obj, String name, Object[] args )
	//------------------------------------------------------------------
	/**
	 * インスタンスobjの nameという名前の
	 * メソッドをargsを引数として実行する。
	 * 
	 * @param obj  インスタンス
	 * @param name メソッド名
	 * @param args 引数の配列
	 * 
	 * @return 戻り値
	 * 
	 * @exception InvocationTargetException
	 *                 コンストラクタで例外が発生した場合
	 * 
	 * @exception NoSuchMethodException
	 *                 args を引数に取れる name という名前の
	 *                 インスタンスメソッドが見つからなかった場合。
	 */
	public static Object invoke(final Object obj, final String name, Object[] args)
			throws InvocationTargetException, NoSuchMethodException {
		final Class type = obj.getClass();
		Method method = MethodUtil.getMatchFullInstanceMethod(type, name, args);
		if (method == null) {
			method = MethodUtil.getInstanceMethod(type, name, args);
			if (method != null)
				args = Type.parseAll(method.getParameterTypes(), args);
		}
		if (method != null) {
			try {
				return method.invoke(obj, args);
			} catch (final IllegalAccessException exception) {
				throw new IllegalAccessError(exception.toString());
			}
		} else {
			throw new NoSuchMethodException();
		}
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  invoke static method
	//------------------------------------------------------------------
	//  public static Object invokeStatic( String classname, String name, Object[] args )
	//  public static Object invokeStatic( Class type, String name, Object[] args )
	//------------------------------------------------------------------
	/**
	 * classname で示されるクラスの nameという名前の
	 * static メソッドをargsを引数として実行する。
	 * 
	 * @param classname クラス名
	 * @param name      メソッド名
	 * @param args      引数の配列
	 * 
	 * @return 戻り値
	 * 
	 * @exception ClassNotFoundException
	 *                 classname のクラスが見つからなかった場合
	 * 
	 * @exception InvocationTargetException
	 *                 コンストラクタで例外が発生した場合
	 * 
	 * @exception NoSuchMethodException
	 *                 args を引数に取れる name という名前の
	 *                 インスタンスメソッドが見つからなかった場合。
	 */
	public static Object invokeStatic(final String classname, final String name, final Object[] args)
			throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
		return MethodUtil.invokeStatic(Class.forName(classname), //throw ClassNotFoundException
				name, args); //throw InvocationTargetException, NoSuchMethodException
	}

	/**
	 * type で示されるクラスの nameという名前の
	 * static メソッドをargsを引数として実行する。
	 * 
	 * @param type 型情報
	 * @param name メソッド名
	 * @param args 引数の配列
	 * 
	 * @return 戻り値
	 * 
	 * @exception InvocationTargetException
	 *                 コンストラクタで例外が発生した場合
	 * 
	 * @exception NoSuchMethodException
	 *                 args を引数に取れる name という名前の
	 *                 インスタンスメソッドが見つからなかった場合。
	 */
	public static Object invokeStatic(final Class type, final String name, Object[] args)
			throws InvocationTargetException, NoSuchMethodException {
		Method method = MethodUtil.getMatchFullStaticMethod(type, name, args);
		if (method == null) {
			method = MethodUtil.getStaticMethod(type, name, args);
			if (method != null)
				args = Type.parseAll(method.getParameterTypes(), args);
		}
		if (method != null) {
			try {
				return method.invoke(null, args);
			} catch (final IllegalAccessException exception) {
				throw new IllegalAccessError(exception.toString());
			}
		} else {
			throw new NoSuchMethodException();
		}
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  get instance method
	//------------------------------------------------------------------
	//  public static Method getInstanceMethod( String classname, 
	//                                          String name, Object[] args )
	//  public static Method getInstanceMethod( Class  type,
	//                                          String name, Object[] args )
	//  public static Method getInstanceMethod( String classname, String  name, 
	//                                          Object[] args,    boolean all )
	//  public static Method getInstanceMethod( Class  type,      String name, 
	//                                          Object[] args,    boolean all )
	//------------------------------------------------------------------
	/**
	 * classname で示されるクラスの public なインスタンスメソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getInstanceMethod(final String classname, final String name, final Object[] args)
			throws ClassNotFoundException {
		return MethodUtil.getInstanceMethod(Class.forName(classname), name, args, false);
	}

	/**
	 * type の public なインスタンスメソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param type 型情報。
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getInstanceMethod(final Class type, final String name, final Object[] args) {
		return MethodUtil.getInstanceMethod(type, name, args, false);
	}

	/**
	 * classname で示されるクラスの インスタンスメソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * @param all       public のメソッドのみを検索するなら false。
	 *                  public, protected, private, パッケージプライベートの
	 *                  全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getInstanceMethod(final String classname, final String name, final Object[] args, final boolean all)
			throws ClassNotFoundException {
		return MethodUtil.getInstanceMethod(Class.forName(classname), name, args, all);
	}

	/**
	 * type の インスタンスメソッドのうち、name という名前で args を
	 * Type.parse した後 受け入れることができるものを得る。
	 * 
	 * @param type 型情報。 
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * @param all  public のメソッドのみを検索するなら false。
	 *             public, protected, private, パッケージプライベートの
	 *             全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getInstanceMethod(final Class type, final String name, final Object[] args, final boolean all) {
		final Method[] methods = all ? type.getDeclaredMethods() : type.getMethods();
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && !Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchFullAll(methods[i].getParameterTypes(), args))
				return methods[i];
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && !Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchRestrictAll(methods[i].getParameterTypes(), args))
				return methods[i];
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && !Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchAll(methods[i].getParameterTypes(), args))
				return methods[i];
		return null;
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  get match full instance method
	//------------------------------------------------------------------
	//  public static Method getMatchFullInstanceMethod( String classname, 
	//                                    String name, Object[] args )
	//  public static Method getMatchFullInstanceMethod( Class  type,
	//                                    String name, Object[] args )
	//  public static Method getMatchFullInstanceMethod( String classname, 
	//                String  name,  Object[] args,    boolean all )
	//  public static Method getMatchFullInstanceMethod( Class  type,
	//                String name,   Object[] args,    boolean all )
	//------------------------------------------------------------------
	/**
	 * classname で示されるクラスの public なインスタンスメソッドのうち、
	 * name という名前で args を 直接受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getMatchFullInstanceMethod(final String classname, final String name, final Object[] args)
			throws ClassNotFoundException {
		return MethodUtil.getMatchFullInstanceMethod(Class.forName(classname), name, args, false);
	}

	/**
	 * type の public なインスタンスメソッドのうち、
	 * name という名前で args を直接受け入れることができるものを得る。
	 * 
	 * @param type 型情報。
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getMatchFullInstanceMethod(final Class type, final String name, final Object[] args) {
		return MethodUtil.getMatchFullInstanceMethod(type, name, args, false);
	}

	/**
	 * classname で示されるクラスの インスタンスメソッドのうち、
	 * name という名前で args を直接受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * @param all       public のメソッドのみを検索するなら false。
	 *                  public, protected, private, パッケージプライベートの
	 *                  全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getMatchFullInstanceMethod(final String classname, final String name, final Object[] args,
			final boolean all) throws ClassNotFoundException {
		return MethodUtil.getMatchFullInstanceMethod(Class.forName(classname), name, args, all);
	}

	/**
	 * type の インスタンスメソッドのうち、name という名前で 
	 * args を直接受け入れることができるものを得る。
	 * 
	 * @param type 型情報。 
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * @param all  public のメソッドのみを検索するなら false。
	 *             public, protected, private, パッケージプライベートの
	 *             全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getMatchFullInstanceMethod(final Class type, final String name, final Object[] args,
			final boolean all) {
		final Method[] methods = all ? type.getDeclaredMethods() : type.getMethods();
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && !Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchFullAll(methods[i].getParameterTypes(), args))
				return methods[i];
		return null;
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  get static method
	//------------------------------------------------------------------
	//  public static Method getStaticMethod( String classname, 
	//                                        String name, Object[] args )
	//  public static Method getStaticMethod( Class  type,
	//                                        String name, Object[] args )
	//  public static Method getStaticMethod( String classname, String  name, 
	//                                        Object[] args,    boolean all )
	//  public static Method getStaticMethod( Class  type,      String name, 
	//                                        Object[] args,    boolean all )
	//------------------------------------------------------------------
	/**
	 * classname で示されるクラスの public static メソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getStaticMethod(final String classname, final String name, final Object[] args)
			throws ClassNotFoundException {
		return MethodUtil.getStaticMethod(Class.forName(classname), name, args, false);
	}

	/**
	 * type の public staticなメソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param type 型情報。
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getStaticMethod(final Class type, final String name, final Object[] args) {
		return MethodUtil.getStaticMethod(type, name, args, false);
	}

	/**
	 * classname で示されるクラスの static メソッドのうち、
	 * name という名前で args を Type.parse した後
	 * 受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * @param all       public のメソッドのみを検索するなら false。
	 *                  public, protected, private, パッケージプライベートの
	 *                  全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getStaticMethod(final String classname, final String name, final Object[] args, final boolean all)
			throws ClassNotFoundException {
		return MethodUtil.getStaticMethod(Class.forName(classname), name, args, all);
	}

	/**
	 * type の static メソッドのうち、name という名前で args を
	 * Type.parse した後 受け入れることができるものを得る。
	 * 
	 * @param type 型情報。 
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * @param all  public のメソッドのみを検索するなら false。
	 *             public, protected, private, パッケージプライベートの
	 *             全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getStaticMethod(final Class type, final String name, final Object[] args, final boolean all) {
		final Method[] methods = all ? type.getDeclaredMethods() : type.getMethods();
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchFullAll(methods[i].getParameterTypes(), args))
				return methods[i];
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchRestrictAll(methods[i].getParameterTypes(), args))
				return methods[i];
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchAll(methods[i].getParameterTypes(), args))
				return methods[i];
		return null;
	}

	//------------------------------------------------------------------
	//	shared method
	//------------------------------------------------------------------
	//  get match full static method
	//------------------------------------------------------------------
	//  public static Method getMatchFullStaticMethod( String classname, 
	//                                    String name, Object[] args )
	//  public static Method getMatchFullStaticMethod( Class  type,
	//                                    String name, Object[] args )
	//  public static Method getMatchFullStaticMethod( String classname, 
	//                String  name,  Object[] args,    boolean all )
	//  public static Method getMatchFullStaticMethod( Class  type,
	//                String name,   Object[] args,    boolean all )
	//------------------------------------------------------------------
	/**
	 * classname で示されるクラスの public static メソッドのうち、
	 * name という名前で args を 直接受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getMatchFullStaticMethod(final String classname, final String name, final Object[] args)
			throws ClassNotFoundException {
		return MethodUtil.getMatchFullStaticMethod(Class.forName(classname), name, args, false);
	}

	/**
	 * type の public staticなメソッドのうち、
	 * name という名前で args を直接受け入れることができるものを得る。
	 * 
	 * @param type 型情報。
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getMatchFullStaticMethod(final Class type, final String name, final Object[] args) {
		return MethodUtil.getMatchFullStaticMethod(type, name, args, false);
	}

	/**
	 * classname で示されるクラスの static メソッドのうち、
	 * name という名前で args を直接受け入れることができるものを得る。
	 * 
	 * @param classname クラス名。
	 * @param name      検索するメソッド名。
	 * @param args      引数配列。
	 *                  null を含めても良いが、null を使用した場合は
	 *                  Object のサブクラスであれば全てマッチしてしまうため、
	 *                  目的のメソッド以外のものが見つかる可能性がある。
	 * @param all       public のメソッドのみを検索するなら false。
	 *                  public, protected, private, パッケージプライベートの
	 *                  全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 * 
	 * @exception ClassNotFoundException
	 *                 classname で示されるクラスが存在しない場合
	 */
	public static Method getMatchFullStaticMethod(final String classname, final String name, final Object[] args,
			final boolean all) throws ClassNotFoundException {
		return MethodUtil.getMatchFullStaticMethod(Class.forName(classname), name, args, all);
	}

	/**
	 * type の static メソッドのうち、name という名前で 
	 * args を直接受け入れることができるものを得る。
	 * 
	 * @param type 型情報。 
	 * @param name 検索するメソッド名。
	 * @param args 引数配列。
	 *             null を含めても良いが、null を使用した場合は
	 *             Object のサブクラスであれば全てマッチしてしまうため、
	 *             目的のメソッド以外のものが見つかる可能性がある。
	 * @param all  public のメソッドのみを検索するなら false。
	 *             public, protected, private, パッケージプライベートの
	 *             全てのメソッドを検索するなら true。
	 * 
	 * @return args を引数に取ることができる nameという名前の メソッド。
	 *         見つからなければ null。
	 */
	public static Method getMatchFullStaticMethod(final Class type, final String name, final Object[] args,
			final boolean all) {
		final Method[] methods = all ? type.getDeclaredMethods() : type.getMethods();
		for (int i = 0; i < methods.length; i++)
			if (methods[i].getName().equals(name) && Modifier.isStatic(methods[i].getModifiers())
					&& Type.matchFullAll(methods[i].getParameterTypes(), args))
				return methods[i];
		return null;
	}
}
//end of Method.java
