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)
 * を生成するユーティリティクラスです<P>
 *
 * JavaBeansには以下のクラス情報が含まれている必要があります<P>
 *
 * public static final FK_TABLE_プロパティ名で定義されたテーブル名
 * （省略された場合は、クラス名が外部参照先のテーブル名となります）とプロパティ名
 * に対応するオブジェクトの宣言<BR>
 *
 * public static final FK_COLUM_プロパティ名_外部参照キー名で定義されたフィールド名
 * （省略された場合は,テーブルにマッピングされません）とFK_TABLE_プロパティ名で
 * 指定した外部参照テーブルに対応するプロパティの宣言（このプロパティはForgine Keyに
 * 該当します）<BR>
 * メンバ名は、JavaBeansで定義されているプロパティ名と同じ名前を使用する必要が
 * あります。
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
 */
public class RecordMapFkFactory extends RecordMapFactory{
    /**
     * COLUMマッピング定義宣言
     */
    public static final String FK_COLUM = "FK_COLUM_";
    
    /**
     * TABLEマッピング定義宣言
     */
    public static final String FK_TABLE = "FK_TABLE_";
    
    /**
     * 引数に指定したクラスに関連する外部参照テーブル用のマッピング情報を作成します<br>
     * 外部参照マッピング情報のリストは（キー＝プロパティ名、値＝RecordMapインスタンス）の形式
     * @param className 永続化対象となるJavaBeansのクラス情報
     * @return 外部参照マッピング情報のリストを返します（キー＝プロパティ名、値＝RecordMapインスタンス）
     */
    public static HashMap createFkRecordMaps( String className){
        try{
            Class entity = Class.forName(className);
            return createFkRecordMaps( entity );
        }catch(ClassNotFoundException e){
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 引数に指定したクラスに関連する外部参照テーブル用のマッピング情報を作成します<br>
     * 外部参照マッピング情報のリストは（キー＝プロパティ名、値＝RecordMapインスタンス）の形式
     * @param entity 永続化対象となるJavaBeansのクラス情報
     * @return 外部参照マッピング情報のリストを返します（キー＝プロパティ名、値＝RecordMapインスタンス）
     */
    public static HashMap createFkRecordMaps( Class entity ){
        init();
        try{
            HashMap fkRecordMaps = new HashMap();
        
            fkRecordMaps = createFkRecordMaps( entity, fkRecordMaps );
            return fkRecordMaps;
        
        }catch(Exception e){
            log.error(e.getMessage());
            throw new DaoRuntimeException(e.getMessage());
        }
    }
    
    /**
     * 引数に指定したクラスに関連する外部参照テーブル用のマッピング情報を作成します<br>
     * 外部参照マッピング情報のリストは（キー＝プロパティ名、値＝RecordMapインスタンス）の形式
     * @param entity 永続化対象となるJavaBeansのクラス情報
     * @param fkReocrdMaps 外部参照マッピング情報のリスト（再起呼び出し用）
     * @return 外部参照マッピング情報のリストを返します（キー＝プロパティ名、値＝RecordMapインスタンス）
     */
    public static synchronized HashMap createFkRecordMaps( Class entity, HashMap fkRecordMaps ){
        //親クラスのメンバから走査
        Class superClazz = entity.getSuperclass();
        if( superClazz != null ){
            fkRecordMaps = createFkRecordMaps( superClazz, fkRecordMaps );
        }
        String clazzName = entity.getName();
        //-------------------------------------------------------
        //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();
            try{
                String staticName = field.getName();
                int index = staticName.indexOf(FK_TABLE);
                //public static final 就職子のフィールドでFK_TABLEがつくフィールド
                if( modifier == (Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL)
                && index == 0){
                    String propertyName = staticName.substring((FK_TABLE).length(), staticName.length());
                    String propertyType = getPropertyType(entity, propertyName);
                    
                    RecordMap recordMap = RecordMapFactory.createRecordMap(propertyType);
                    setForignKey( recordMap, entity, FK_TABLE+propertyName, FK_COLUM+propertyName+"_" );
                    
                    if( propertyType != null ){
                        recordMap.setClassName(propertyType);
                        fkRecordMaps.put(propertyName, recordMap);
                    }
                }
                
            }catch(Exception e){}
            
        }
        return fkRecordMaps;
    }
    
    /**
     * 
     */
    private static void setForignKey(RecordMap recordMap, Class entity, String tableHeaderName, String columHeaderName){
        Object obj = BeanUtil.createInstance(entity.getName());
        String tableName = getTableName(entity, obj, tableHeaderName);
        //RecordMap recordMap = new RecordMap(entity.getName(), tableName);
        recordMap.setClassName(entity.getName());
        recordMap.setTableName(tableName);
        
        //-------------------------------------------------------
        //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インスタンスを作成します
            try{
                String staticName = field.getName();
                int index = staticName.indexOf(columHeaderName);
                if( modifier == (Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL)
                && index == 0){
                    //FieldMap fieldMap = new FieldMap();
                    String fieldName = (String)field.get(obj);
                    fieldName = fieldName.toUpperCase();
                    String propertyName = staticName.substring((columHeaderName).length(), staticName.length());
                    FieldMap fieldMap = recordMap.getFieldByFieldName(fieldName);
                    fieldMap.setFkPropertyName(propertyName);
                    fieldMap.setForignKey(true);
                    
                }
                
            }catch(IllegalAccessException e){
                String err = "[" + obj.getClass().getName() + ":" + tableName + "]" +
                    "setForignKey() failed(" + RecordMapFkFactory.class.getName() + ") (" +
                    e.getMessage() + ")";
                throw new DaoRuntimeException(err);
            }catch(NullPointerException e){
                String err = "[" + obj.getClass().getName() + ":" + tableName + "]" +
                    "setForignKey() failed(" + RecordMapFkFactory.class.getName() + ") (" +
                    e.getMessage() + ")";
                throw new DaoRuntimeException(err);
            }
            
        }
        //-----------------------------------------------------------
        //"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);
                String propertyName = fieldMap.getPropertyName();
                //Field field = entity.getDeclaredField(propertyName);
                Field field = BeanUtil.getField(entity, propertyName);
                if(field == null){
                    continue;
                }
                String type = field.getType().getName();
                
                fieldMap.setType(type);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
  
}

