/*
 * 作成日： 2004/09/13
 */
package org.seasar.tapestry.page.translation;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The data transmission between Bean and Bean is supported. 
 * @author maruo_syunsuke
 */
public class BeanCopy implements ObjectTranslation{
    private static final Log LOG = LogFactory.getLog(BeanCopy.class);

    /**
     * 
     * @param dest
     * @param src
     */
    public void translate(Object dest, Object src) {
        Method[] srcMethods = src.getClass().getMethods();
        for (int i = 0; i < srcMethods.length; i++) {
            Method getterMethod = srcMethods[i];
            String getterName = getterMethod.getName() ;
            if( getterName.startsWith("get") == false )continue ;
            String setterName = getSetterFieldName(getterName);
            copyGetterToSetter(dest, src, getterMethod, setterName);
        }
    }

    /**
     * @param dest
     * @param src
     * @param between
     */
    public void translate(Object dest, Object src, Class between) {
        Method[] srcMethods = src.getClass().getMethods();
        for (int i = 0; i < srcMethods.length; i++) {
            Method getterMethod = srcMethods[i];
            String getterName = getterMethod.getName() ;
            if( getterName.startsWith("get") == false )continue ;
            String setterName = makeSetterName(between, getterName);
            copyGetterToSetter(dest, src, getterMethod, setterName);
        }
    }

    private static String makeSetterName(Class between, String getterName) {
        String setterName ;
        try {
            String fieldName = getSetterFieldName(between, getterName);
            setterName = "set"+fieldName.substring(0,1).toUpperCase()
                                        +fieldName.substring(1);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        }
        return setterName;
    }

    private static String getSetterFieldName(String getterName) {
        return "s"+getterName.substring(1);
    }
    private static String getSetterFieldName(Class between, String getterName){
        try{
            String fieldName 
                = getterName.substring(3,4).toLowerCase()
                        +getterName.substring(4);
            return (String) between.getField( fieldName ).get(between);
            
        } catch (IllegalAccessException e) {
            LOG.info("アノテレートクラスに対応する値にアクセスできなかった : "+ getterName);
        } catch (NoSuchFieldException e) {
            LOG.info("アノテレートクラスに対応する値が無かった : "+ getterName);
        }
        return getSetterFieldName(getterName);
    }

    private static void copyGetterToSetter(Object dest, Object src, Method getterMethod, String setterName) {
        Method setterMethod;
        try {
            Class targetClass = getTargetClass(dest);
            setterMethod = targetClass.getDeclaredMethod(
                    setterName,
                    new Class[] {getterMethod.getReturnType() });
            Object returnValueOfGetterMethod = getterMethod.invoke(src,new Object[0]);
            setterMethod.invoke(dest, new Object[] {returnValueOfGetterMethod});
            if( LOG.isDebugEnabled() ){
                LOG.debug( "Getter To Setter : " + targetClass.getName() +"."
                    + setterMethod.getName() + "(" +returnValueOfGetterMethod + ")");
            }
        }catch(NoSuchMethodException e){
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    private static Class getTargetClass(Object dest){
        Class targetClass = dest.getClass();
        while( isAopAppendClass( targetClass ) ){
            targetClass = targetClass.getSuperclass();
            if( targetClass == null ) return null;
        }
        return targetClass ;
    }
    private static boolean isAopAppendClass( Class targetClass ) {
        String name = targetClass.getName();
        return name.indexOf( "$" ) != -1;
    }
}
