package example.common.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import example.common.exception.SystemException;

/**
 * ユーティリティクラスの例です。
 * 
 * @author glad
 */
public class ExampleUtils {

    /**
     * 指定された位置の引数を返します。
     * <p>
     * index が配列のサイズ以上の場合は null を返します。
     * 
     * @param args 引数の配列
     * @param index 引数の位置
     * @return 引数
     */
    public static String getArg(String[] args, int index) {
        if (args == null || args.length <= index) {
            return null;
        } else {
            return args[index];
        }
    }

    /**
     * 文字列が空白か判定します。
     * 
     * @param s 文字列
     * @return 空白ならば true、そうでなければ false
     */
    public static boolean isBlank(String s) {
        return (s == null || s.trim().length() == 0);
    }

    /**
     * Java Bean の文字列表現を返します。
     * 
     * @param bean Java Bean
     * @return 文字列表現
     */
    public static String fromBeanToString(Object bean) {
        return fromBeanToString(bean, false);
    }

    /**
     * Java Bean の文字列表現を返します。
     * 
     * @param bean Java Bean
     * @param ignoreNull 値が null のプロパティを無視する場合は true
     * @return 文字列表現
     */
    public static String fromBeanToString(Object bean, boolean ignoreNull) {
        Class<?> clazz = bean.getClass();
        StringBuilder sb = new StringBuilder();
        sb.append(clazz.getSimpleName());
        BeanInfo info = getBeanInfo(clazz);
        boolean hasProps = false;
        for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
            String name = pd.getName();
            if ("class".equals(name)) continue;
            Method readMethod = pd.getReadMethod();
            if (readMethod == null) continue;
            if (pd.getWriteMethod() == null) continue;
            Object value = invoke(readMethod, bean);
            if (ignoreNull && value == null) continue;
            if (!hasProps) {
                hasProps = true;
                sb.append(" {");
            } else {
                sb.append(", ");
            }
            sb.append(name).append('=').append(value);
        }
        if (hasProps) {
            sb.append('}');
        }
        return sb.toString();
    }

    static BeanInfo getBeanInfo(Class<?> clazz) {
        try {
            return Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
    }

    static Object invoke(Method method, Object obj, Object... args) {
        try {
            return method.invoke(obj, args);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw newRuntimeException(e.getCause());
        }
    }

    static RuntimeException newRuntimeException(Throwable t) {
        if (t instanceof Error) {
            throw (Error) t;
        } else if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            return new RuntimeException(t);
        }
    }

    public static <T> T ejbLookup(Class<T> intf) {
        return jndiLookup(intf, intf.getName());
    }

    public static Object jndiLookup(String name) {
        try {
            return doJndiLookup(name);
        } catch (NamingException e) {
            throw new SystemException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T jndiLookup(Class<T> clazz, String name) {
        return (T) jndiLookup(name);
    }

    public static Object doJndiLookup(final String name) throws NamingException {
        final Context context = new InitialContext();
        return new GenericTemplate<Object, NamingException>("lookup", name) {
            @Override
            public Object invoke() throws NamingException {
                try {
                    return doInvoke();
                } catch (Throwable t) {
                    throw toThrowableException(t);
                }
            }
            protected void execute() throws NamingException {
                setReturning(context.lookup(name));
            }
            @Override
            protected void after() throws NamingException {
                context.close();
            }
        }.invoke();
    }

}
