package org.arefgard.icerya.flow.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

/**
 * リフレクションに関するユーティリティ。
 * 
 * @author Takashi Yamashina
 * @since 1.0.0
 */
public class ReflectionUtil {

	/**
	 * クラスの完全修飾名からオブジェクトを生成する。
	 * 
	 * @param className クラスの完全修飾名
	 * @return 生成したオブジェクト
	 * @throws ClassNotFoundException クラスが見つからない場合
	 */
	public static Object generate(String className) throws ClassNotFoundException {
		try {
			Class clazz = Class.forName(className);
			return clazz.newInstance();
		}catch(Exception e) {
			throw new ClassNotFoundException("クラス「" + className + "」が見つかりません", e);
		}
	}
	
	/**
	 * 指定したプロパティに値を設定する。
	 * 
	 * @param obj 対象オブジェクト
	 * @param property プロパティ名
	 * @param value 設定する値
	 * @throws Exception プロパティの設定に失敗した場合
	 */
	public static void setProperty(Object obj, String property, Object value) throws Exception {
		Class clazz = obj.getClass();
		StringBuffer methodName = new StringBuffer("set");
		methodName.append(property.substring(0, 1).toUpperCase());
		methodName.append(property.substring(1));
		Method method = null;
		Method[] methods = clazz.getDeclaredMethods();
		for(int i = 0, n = methods.length;i < n; i++) {
			if(methods[i].getName().equals(methodName.toString())) {
				method = methods[i];
				break;
			}
		}
		method.invoke(obj, new Object[] { value });
	}
	
	/**
	 * 指定したプロパティから値を取得する。
	 * @param obj 対象オブジェクト
	 * @param property プロパティ名
	 * @return 取得した値
	 * @throws Exception プロパティの取得に失敗した場合
	 */
	public static Object getProperty(Object obj, String property) throws Exception {
		Class clazz = obj.getClass();
		
		StringBuffer methodName = new StringBuffer("get");
		methodName.append(property.substring(0, 1).toUpperCase());
		methodName.append(property.substring(1));
		
		Method method = clazz.getDeclaredMethod(methodName.toString());
		return method.invoke(obj);
	}
	
	/**
	 * 指定したメソッドを実行する。
	 * 
	 * @param obj 対象オブジェクト
	 * @param methodName メソッド名
	 * @param parameter パラメータリスト
	 * @return 戻り値
	 * @throws Exception メソッドの実行に失敗した場合
	 */
	public static Object execute(Object obj, String methodName, List<Object> parameter) throws Exception {
		Class clazz = obj.getClass();
		Method method = null;
		if(parameter == null) {
			method = clazz.getDeclaredMethod(methodName, (Class[])null);
			method.setAccessible(true);
			return method.invoke(obj, (Object[])null);
		}else {
			Object[] objList = parameter.toArray();
			Class[] classList = new Class[objList.length];
			for(int i = 0; i < objList.length; i++) {
				classList[i] = objList[i].getClass();
			}
			Method[] methods = clazz.getDeclaredMethods();
			for(int i = 0, n = methods.length;i < n; i++) {
				if(methods[i].getName().equals(methodName.toString())) {
					method = methods[i];
					break;
				}
			}
			return method.invoke(obj, objList);
		}
	}

	/**
	 * 文字列で渡された値を対象オブジェクトのフィールドの型に合ったオブジェクトに
	 * 変換する。
	 * @param obj 対象オブジェクト
	 * @param fieldName フィールド名
	 * @param value 入力値
	 * @return 変換後の値
	 * @throws Exception 変換に失敗した場合
	 */
	public static Object convertObject(Object obj, String fieldName, String value) throws Exception {
		Class clazz = obj.getClass();
		Field field = clazz.getDeclaredField(fieldName);
		field.setAccessible(true);
		Class type = field.getType();
		if(type.getName().equals("byte") || type.getName().equals("java.lang.Byte")) {
			return new Byte(value);
		}else if(type.getName().equals("short") || type.getName().equals("java.lang.Short")) {
			return new Short(value);
		}else if(type.getName().equals("int") || type.getName().equals("java.lang.Integer")) {
			return new Integer(value);
		}else if(type.getName().equals("long") || type.getName().equals("java.lang.Long")) {
			return new Long(value);
		}else if(type.getName().equals("float") || type.getName().equals("java.lang.Float")) {
			return new Float(value);
		}else if(type.getName().equals("double") || type.getName().equals("java.lang.Double")) {
			return new Double(value);
		}else if(type.getName().equals("char") || type.getName().equals("java.lang.Character")) {
			return new Character(value.toCharArray()[0]);
		}
		return value;
	}
	
}
