package org.itscool.commons.dao;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.itscool.commons.bean.BeanUtil;

/**
 * RecordsMp作成クラス<P>
 *
 * JavaBeansのアノテーションを読み取り、テーブルマッピングクラス（RecordMap)
 * を生成するユーティリティクラスです<br>
 * JavaBeansには以下のクラス情報が含まれている必要があります<BR>
 * public static final TABLEで定義されたテーブル名（省略された場合は、クラス名
 * がテーブル名となります）<BR>
 * public static final COLUM_メンバ名で定義されたフィールド名（省略された場合
 * は,テーブルにマッピングされません）<BR>
 * メンバ名は、JavaBeansで定義されているプロパティ名と同じ名前を使用する必要が
 * あります。
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
 */
public class RecordMapFactory {
    /**
     * COLUMマッピング定義宣言
     */
    public static final String COLUM = "COLUM_";
    
    public static final String PK_COLUM = "PK_COLUM_";
    
    /**
     * TABLEマッピング定義宣言
     */
    public static final String TABLE = "TABLE";
    
    /**
     * JavaBeansのクラス情報を元にRecordMapインスタンスを生成します
     * @param className 永続化対象となるJavaBeansのクラス情報
     * @return RecordMapインスタンスを取得します
     */
    public static RecordMap createRecordMap( String className){
        try{
            Class entity = Class.forName(className);
            return create( entity );
        }catch(ClassNotFoundException e){
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * JavaBeansのクラス情報を元にRecordMapインスタンスを生成します
     * @param entity 永続化対象となるJavaBeansのクラス情報
     * @return RecordMapインスタンスを取得します
     */
    public static RecordMap createRecordMap( Class entity){
        return create( entity );
    }
    
    private static RecordMap create( Class entity ){
        Object obj = BeanUtil.createInstance(entity.getName());
        String tableName = getTableName(entity, obj, TABLE);
        RecordMap recordMap = new RecordMap(entity.getName(), tableName);
        
        recordMap = create( entity, obj, recordMap );
        //-----------------------------------------------------------
        //"COLUM_メンバ名"に対応するプロパティ名をJavaBeansクラス情報から
        //取得します
        //-----------------------------------------------------------
        HashMap records = recordMap.getFields();
        Set keySet = records.keySet();
        Iterator iterator = keySet.iterator();
        while( iterator.hasNext() ){
            try{
                Object key = iterator.next();
                FieldMap fieldMap = (FieldMap)records.get(key);
                //modify 2005/09/20 start
                //Field field = entity.getDeclaredField(fieldMap.getPropertyName());
                Field field = BeanUtil.getField(entity, fieldMap.getPropertyName());
                if( field == null ){
                    continue;
                }
                //modify 2005/09/20 end
                String type = field.getType().getName();
                
                fieldMap.setType(type);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        return recordMap;
    }
    
    private static RecordMap create( Class entity, Object obj, RecordMap recordMap ){
        //親クラスのメンバから走査
        Class superClazz = entity.getSuperclass();
        if( superClazz != null ){
            recordMap = create( superClazz, obj, recordMap );
        }
        //-------------------------------------------------------
        //RecordMapが保有する各FieldMapインスタンスに対して
        //対応するカラム情報をセットしていきます。
        //カラムをセットする変数は"COLUM_プロパティ名"である必要があります
        //-------------------------------------------------------
        Field[] fields = entity.getDeclaredFields();
        for( int i=0; i<fields.length; i++){
            //JavaBeansに定義されたフィールド情報を取得します
            Field field = fields[i];
            //フィールドのModifier情報を取得します
            int modifier = field.getModifiers();
            //FieldMapインスタンスを作成します
            String staticName = field.getName();
            try{
                int index = staticName.indexOf(COLUM);
                int pkIndex = staticName.indexOf(PK_COLUM);
                if( modifier == (Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL)
                && pkIndex == 0 ){
                    FieldMap fieldMap = new FieldMap();
                    String fieldName = (String)field.get(obj);
                    String propertyName = staticName.substring((PK_COLUM).length(), staticName.length());
                    fieldMap.setColumName(fieldName);
                    fieldMap.setPropertyName(propertyName);
                    fieldMap.setRecordMap(recordMap);
                    fieldMap.setPrimaryKey(true);
                    recordMap.addField(fieldMap);
                }else if( modifier == (Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL)
                && index == 0 ){
                    FieldMap fieldMap = new FieldMap();
                    String fieldName = (String)field.get(obj);
                    String propertyName = staticName.substring((COLUM).length(), staticName.length());
                    fieldMap.setColumName(fieldName);
                    fieldMap.setPropertyName(propertyName);
                    fieldMap.setRecordMap(recordMap);
                    fieldMap.setPrimaryKey(false);
                    recordMap.addField(fieldMap);
                }
            }catch(IllegalAccessException e){
                String err = "[" + obj.getClass().getName() + ":" + staticName + "]" +
                    "create() failed(" + RecordMapFactory.class.getName() + ") (" +
                    e.getMessage() + ")";
                throw new DaoRuntimeException(err);
            }
            
        }
        return recordMap;
    }
    /**
     * JavaBeansクラス定義からテーブル名を取得します
     * @param entity JavaBeansクラス定義情報
     * @param obj JavaBeansから生成された仮のインスタンス
     * @return TABEL名を取得します
     */
    protected static String getTableName(Class entity, Object obj, String tableHeaderName){
        String tableName = entity.getName();
        try{
            //modify 2005/09/20 start
            //Field field = entity.getField(tableHeaderName);
            Field field = BeanUtil.getField(entity, tableHeaderName);
            //modify 2005/09/20 end
            if( field != null ){
                tableName = (String)field.get(obj);
            }
        }catch(Exception e){
            //TABLE_NAMEが無い場合はクラス名をセットする
            tableName = entity.getName();
        }
        return tableName;
    }
    
//    public static RecordMap createRecordMapBAK( Class entity){
//        Object obj = BeanUtil.createInstance(entity.getName());
//        String tableName = getTableName(entity, obj);
//        RecordMap recordMap = new RecordMap(entity.getName(), tableName);
//
//        //entityクラスのフィールド情報を取得します
//        Field[] fields = entity.getDeclaredFields();
//        for( int i=0; i<fields.length; i++){
//            Field field = fields[i];
//
//            int modifier = field.getModifiers();
//            switch(modifier){
//            //private、protectedフィールドのみを評価します
//            case Modifier.PRIVATE:
//            case Modifier.PROTECTED:
//                //FieldMapインスタンスを作成します
//                FieldMap param = new FieldMap();
//            	//プロパティ名をセット
//            	param.setPropertyName(field.getName());
//            	//プロパティの型名をセット
//            	param.setType(field.getType().getName());
//            	param.setRecordMap(recordMap);
//            	recordMap.addField(param);
//                break;
//            }
//        }
//        //RecordMapが保有する各FieldMapインスタンスに対して
//        //対応するカラム情報をセットしていきます。
//        //カラムをセットする変数はプロパティ名（大文字）とする
//        HashMap records = recordMap.getFields();
//        Set keySet = records.keySet();
//        Iterator iterator = keySet.iterator();
//        while( iterator.hasNext() ){
//            Object key = iterator.next();
//            FieldMap fieldMap = (FieldMap)records.get(key);
//            String propertyName = fieldMap.getPropertyName();
//            String staticName = propertyName.toUpperCase();
//            try{
//	            //プロパティに対応するpublic static final変数を取得
//	            Field field = entity.getDeclaredField(staticName);
//	            //プロパティに対応するpublic static final変数がある場合
//	            if( field.getModifiers() == (Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL)){
//	                fieldMap.setColumName((String)field.get(obj));
//	            //プロパティに対応するpublic static final変数がない場合はプロパティ名(大文字)をカラム名とする
//	            }else{
//	                fieldMap.setColumName(staticName);
//	            }
//            //プロパティに対応するpublic static final変数がない場合はプロパティ名(大文字)をカラム名とする
//            }catch(Exception e){
//                fieldMap.setColumName(staticName);
//            }
//        }
//        return recordMap;
//    }
    
    public static void testParseClass(Class clazz) throws Exception{
        System.out.println("---------------------------");
        RecordMap record = RecordMapFactory.createRecordMap(clazz);
        System.out.println(record.getTableName() + "," + record.getClassName());
        HashMap fields = record.getFields();
        Set keySet = fields.keySet();
        Iterator iterator = keySet.iterator();
        while( iterator.hasNext() ){
            Object key = iterator.next();
            FieldMap fieldMap = (FieldMap)fields.get(key);
            System.out.println(fieldMap.getColumName()+","+fieldMap.getPropertyName()+","+fieldMap.getType());
        }
    }
    
}

