package org.maachang.dao ;

import java.sql.ResultSet;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.maachang.dao.dbms.DbUtil;
import org.maachang.dao.dbms.MetaColumn;
import org.maachang.dao.dbms.Record;

/**
 * MaachangDao.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangDao 1.00
 */
public class MaachangDao {
    
    /**
     * シングルトン.
     */
    private static final MaachangDao SNGL = new MaachangDao() ;
    
    /**
     * コンストラクタ.
     */
    private MaachangDao() {
        
    }
    
    /**
     * オブジェクトを取得.
     * <BR><BR>
     * オブジェクトを取得します.
     * <BR>
     * @return MaachangDao オブジェクト情報が返されます.
     */
    public static final MaachangDao getInstance() {
        return SNGL ;
    }
    
    /**
     * データ保存.
     * <BR><BR>
     * データを保存します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param model 保存対象のモデル情報を設定します.
     * @return Map<String,Object> 保存された内容が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public Map<String,Object> save( DaoSession session,String modelName,Map<String,Object> model )
        throws MaachangDaoException {
        if( session == null || modelName == null || model == null || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            boolean idFlag = ExecutionDao.isSequence( meta ) ;
            // データが存在するかチェック.
            // または、IDカラムが存在しない場合は、強制的に
            // insertにする.
            if( isEntry( record,idFlag,meta,model,modelName ) == true ) {
                // データが存在する場合は、update.
                StringBuilder buf = new StringBuilder() ;
                buf.append( "update " ).append( meta.getTable() ).append( " set " ) ;
                int len = meta.size() ;
                int cnt = 0 ;
                ArrayList<Object> pms = new ArrayList<Object>() ;
                for( int i = 0 ; i < len ; i ++ ) {
                    String column = meta.getColumnName( i ) ;
                    String javaName = DbUtil.convertDBNameByJavaName( false,column ) ;
                    if( ExecutionDao.SEQ_COLUMN.equals( column ) == false ) {
                        Object value = null ;
                        // 更新カラムが存在する場合.
                        if( ExecutionDao.UPDATE_TIME.equals( column ) ) {
                            value = new java.sql.Timestamp( System.currentTimeMillis() ) ;
                            model.put( javaName,value ) ;
                        }
                        else {
                            value = model.get( javaName ) ;
                        }
                        if( value != null ) {
                            if( cnt != 0 ) {
                                buf.append( "," ) ;
                            }
                            buf.append( column ).append( "=" ).
                                append( "?" ) ;
                            pms.add( value ) ;
                            cnt ++ ;
                        }
                    }
                }
                buf.append( " where id=?" ) ;
                if( pms.size() <= 0 ) {
                    throw new IllegalArgumentException( "保存対象のパラメータが全てNULLです" ) ;
                }
                String sql = buf.toString() ;
                buf = null ;
                Object id = model.get( ExecutionDao.SEQ_COLUMN ) ;
                pms.add( id ) ;
                ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,pms ) ;
            }
            // データが存在しない場合Insert.
            else {
                StringBuilder buf = new StringBuilder() ;
                buf.append( "insert into " ).append( meta.getTable() ).append( " (" ) ;
                int len = meta.size() ;
                ArrayList<Object> pms = new ArrayList<Object>() ;
                boolean addId = false ;
                if( idFlag == true && record.isSequence() == true ) {
                    pms.add( new Object() ) ;
                    buf.append( ExecutionDao.SEQ_COLUMN ) ;
                    if( len != 1 ) {
                        buf.append( "," ) ;
                        addId = true ;
                    }
                }
                int cnt = 0 ;
                for( int i = 0 ; i < len ; i ++ ) {
                    String column = meta.getColumnName( i ) ;
                    int type = meta.getColumnType( i ) ;
                    String lowerColumn = column.toLowerCase() ;
                    String javaName = DbUtil.convertDBNameByJavaName( false,column ) ;
                    if( ExecutionDao.SEQ_COLUMN.equals( column ) == false ) {
                        Object value = null ;
                        // 生成/更新カラムが存在する場合.
                        if( ExecutionDao.CREATE_TIME.equals( lowerColumn ) ||
                            ExecutionDao.UPDATE_TIME.equals( lowerColumn ) ) {
                            value = new java.sql.Timestamp( System.currentTimeMillis() ) ;
                            model.put( javaName,value ) ;
                        }
                        // 楽観的ロックカラムが存在する場合.
                        else if( ExecutionDao.OPTIMISTIC_LOCK_COLUMN.equals( lowerColumn ) &&
                            Types.BIGINT == type) {
                            value = new Long( 1 ) ;
                            model.put( javaName,value ) ;
                        }
                        else {
                            value = model.get( javaName ) ;
                        }
                        if( cnt != 0 ) {
                            buf.append( "," ) ;
                        }
                        buf.append( column ) ;
                        if( value == null ) {
                            pms.add( null ) ;
                        }
                        else {
                            pms.add( value ) ;
                        }
                        cnt ++ ;
                    }
                }
                buf.append( ") values (" ) ;
                if( addId == true ) {
                    if( cnt == 0 ) {
                        buf.append( "?)" ) ;
                    }
                    else {
                        buf.append( "?," ) ;
                    }
                }
                for( int i = 0 ; i < cnt ; i ++ ) {
                    if( i != 0 ) {
                        buf.append( "," ) ;
                    }
                    buf.append( "?" ) ;
                }
                buf.append( ")" ) ;
                String sql = buf.toString() ;
                Long[] resId = new Long[ 1 ] ;
                ExecutionDao.execution( null,resId,record,modelName,meta,sql,-1,-1,pms ) ;
                if( resId[ 0 ] != null ) {
                    model.put( ExecutionDao.SEQ_COLUMN,resId[ 0 ] ) ;
                }
            }
            return model ;
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * アップデート.
     * <BR><BR>
     * 指定条件のデータをアップデートします.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param set update文の[set]以降の条件を設定します.
     * @param params 対象のパラメータ群を設定します.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public void update( DaoSession session,String modelName,String set,Object... params )
        throws MaachangDaoException {
        if( session == null || set == null || modelName == null ||
            ( set = set.trim() ).length() <= 0 || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            StringBuilder buf = new StringBuilder() ;
            if( set.toLowerCase().startsWith( "set " ) == false ) {
                buf.append( "update " ).append( meta.getTable() ).
                    append( " set " ).append( set ) ;
            }
            else {
                buf.append( "update " ).append( meta.getTable() ).append( " " ).append( set ) ;
            }
            String sql = buf.toString() ;
            buf = null ;
            if( ExecutionDao.isUpdateTime( meta ) == true ) {
                boolean[] addFlag = new boolean[ 1 ] ;
                sql = addUpdateTime( addFlag,sql ) ;
                if( addFlag[ 0 ] == true ) {
                    params = addUpdateTimeParam( params ) ;
                }
            }
            ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,params ) ;
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * 削除.
     * <BR><BR>
     * 指定Entryの情報を削除します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param plugin 削除Join用プラグインを設定します.
     * @param deleteJoinColumns 削除Join用カラム名群を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param model 対象のモデル情報が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public void delete( DaoSession session,DeleteJoinPlugin plugin,ArrayList<String> deleteJoinColumns,
        Map<String,List<String>> srcJoin,String modelName,Map<String,Object> model )
        throws MaachangDaoException {
        if( session == null || modelName == null || model == null || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            if( ExecutionDao.isSequence( meta ) == true ) {
                Object id = model.get( ExecutionDao.SEQ_COLUMN ) ;
                if( id == null ) {
                    throw new IllegalArgumentException( "IDが存在しません" ) ;
                }
                executionDeleteJoinPlugin( session,modelName,plugin,deleteJoinColumns,"where id=?",id ) ;// プラグイン結合.
                deleteBySrcJoin( session,srcJoin,modelName,"where id=?",id ) ;// テーブル結合.
                StringBuilder buf = new StringBuilder() ;
                buf.append( "delete from " ).append( meta.getTable() ).append( " where id=?;" ) ;
                String sql = buf.toString() ;
                buf = null ;
                ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,new Object[]{id} ) ;
            }
            else {
                StringBuilder buf = new StringBuilder() ;
                buf.append( "delete from " ).append( meta.getTable() ).append( " where " ) ;
                int len = meta.size() ;
                ArrayList<Object> pms = new ArrayList<Object>() ;
                for( int i = 0 ; i < len ; i ++ ) {
                    if( i != 0 ) {
                        buf.append( " and " ) ;
                    }
                    String column = meta.getColumnName( i ) ;
                    Object value = model.get( DbUtil.convertDBNameByJavaName( false,column ) ) ;
                    if( value == null ) {
                        buf.append( column ).append( " is null" ) ;
                    }
                    else {
                        buf.append( column ).append( "=?" ) ;
                        pms.add( value ) ;
                    }
                }
                if( pms.size() <= 0 ) {
                    pms = null ;
                }
                String sql = buf.toString() ;
                buf = null ;
                ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,pms ) ;
            }
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * 削除.
     * <BR><BR>
     * 指定条件のデータを削除します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param plugin 削除Join用プラグインを設定します.
     * @param deleteJoinColumns 削除Join用カラム名群を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param where 条件文を設定します.
     * @param params 対照のパラメータ群を設定します.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public void delete( DaoSession session,DeleteJoinPlugin plugin,ArrayList<String> deleteJoinColumns,
        Map<String,List<String>> srcJoin,String modelName,String where,Object... params )
        throws MaachangDaoException {
        if( session == null || modelName == null || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            executionDeleteJoinPlugin( session,modelName,plugin,deleteJoinColumns,where,params ) ;// プラグイン結合.
            deleteBySrcJoin( session,srcJoin,modelName,where,params ) ;// テーブル結合.
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            StringBuilder buf = new StringBuilder() ;
            if( where == null || ( where = where.trim() ).length() <= 0 ) {
                buf.append( "delete from " ).append( meta.getTable() ) ;
                params = null ;
            }
            else {
                buf.append( "delete from " ).append( meta.getTable() ).
                    append( " " ).append( where ) ;
            }
            String sql = buf.toString() ;
            buf = null ;
            ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,params ) ;
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * 取得.
     * <BR><BR>
     * 指定条件のデータを取得します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param join 対象のJoin条件を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param where 条件文を設定します.
     * @param params 対照のパラメータ群を設定します.
     * @exception List<Map<String,Object>> 情報が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public List<Map<String,Object>> find( DaoSession session,Map<String,String> join,
        Map<String,List<String>> srcJoin,String modelName,String where,Object... params )
        throws MaachangDaoException {
        return find( session,join,srcJoin,modelName,where,-1,-1,params ) ;
    }
    
    /**
     * 取得.
     * <BR><BR>
     * 指定条件のデータを取得します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param join 対象のJoin条件を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param where 条件文を設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param limit 対象のリミットを設定します.
     * @param params 対照のパラメータ群を設定します.
     * @exception List<Map<String,Object>> 情報が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public List<Map<String,Object>> find( DaoSession session,Map<String,String> join,
        Map<String,List<String>> srcJoin,String modelName,String where,int offset,int limit,Object... params )
        throws MaachangDaoException {
        if( session == null || modelName == null || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            String sql = null ;
            if( where == null || ( where = where.trim() ).length() <= 0 ) {
                sql = new StringBuilder().append( "select * from " ).
                    append( meta.getTable() ).toString() ;
                params = null ;
            }
            else {
                StringBuilder buf = new StringBuilder() ;
                buf.append( "select * from " ).append( meta.getTable() ).
                    append( " " ).append( where ) ;
                sql = buf.toString() ;
                buf = null ;
            }
            ArrayList<Map<String,Object>> ret = new ArrayList<Map<String,Object>>() ;
            ExecutionDao.execution( ret,null,record,modelName,meta,sql,offset,limit,params ) ;
            findByJoin( ret,session,join ) ;// １対１テーブル結合.
            findByMany( ret,session,srcJoin ) ;// １対他テーブル結合.
            return ret ;
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * 情報数を取得.
     * <BR><BR>
     * 指定条件の情報数を取得します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param modelName 対象のモデル名を設定します.
     * @exception int 情報数が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public int count( DaoSession session,String modelName )
        throws MaachangDaoException {
        return count( session,modelName,null,( Object[] )null ) ;
    }
    
    /**
     * 情報数を取得.
     * <BR><BR>
     * 指定条件の情報数を取得します.
     * <BR>
     * @param session 対象のセッションを設定します.
     * @param modelName 対象のモデル名を設定します.
     * @param where 条件文を設定します.
     * @param params 対照のパラメータ群を設定します.
     * @exception int 情報数が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public int count( DaoSession session,String modelName,String where,Object... params )
        throws MaachangDaoException {
        if( session == null || modelName == null || ( modelName = modelName.trim() ).length() <= 0 ) {
            throw new MaachangDaoException( "引数は不正です" ) ;
        }
        try {
            Record record = session.getRecord() ;
            MetaColumn meta = session.getMeta( modelName ) ;
            String sql = null ;
            if( where == null || ( where = where.trim() ).length() <= 0 ) {
                sql = new StringBuilder().append( "select count(*) from " ).
                    append( meta.getTable() ).toString() ;
                params = null ;
            }
            else {
                StringBuilder buf = new StringBuilder() ;
                buf.append( "select count(*) from " ).append( meta.getTable() ).
                    append( " " ).append( where ) ;
                sql = buf.toString() ;
                buf = null ;
            }
            return ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,params ) ;
        } catch( MaachangDaoException md ) {
            throw md ;
        } catch( Exception e ) {
            throw new MaachangDaoException( e ) ;
        }
    }
    
    /**
     * 情報存在時のチェック処理.
     */
    private boolean isEntry( Record record,boolean idFlag,MetaColumn meta,Map<String,Object> model,String modelName )
        throws Exception {
        if( idFlag == false ) {
            return false ;
        }
        Object value = model.get( ExecutionDao.SEQ_COLUMN ) ;
        if( value == null ) {
            return false ;
        }
        // 楽観的ロック対象のカラムが存在する.
        if( ExecutionDao.isOptimisticLock( meta ) ) {
            StringBuilder buf = new StringBuilder() ;
            buf.append( "select " ).append( ExecutionDao.OPTIMISTIC_LOCK_COLUMN ).append( " from " ).
                append( DbUtil.convertJavaNameByDBName( modelName ) ).
                append( " where id=?" ) ;
            String sql = buf.toString() ;
            buf = null ;
            ResultSet rs = null ;
             String optimisticLockKey = DbUtil.convertDBNameByJavaName( false,ExecutionDao.OPTIMISTIC_LOCK_COLUMN ) ;
            Long optimisticLockValue = convertObjectByLong( model.get( optimisticLockKey ) ) ;
            try {
                boolean ret = false ;
                rs = record.executeQuery( sql,new Object[]{value} ) ;
                if( rs != null ) {
                    if( rs.next() == true ) {
                        // 楽観的ロックカラム内容が存在する場合.
                        if( optimisticLockValue != null ) {
                            // 楽観的ロックが一致しない場合は、例外を出力.
                            if( optimisticLockValue.equals( rs.getLong( ExecutionDao.OPTIMISTIC_LOCK_COLUMN ) ) == false ) {
                                throw new OptimisticLockException( "[" + modelName + "] - use optimistic lock.(src:" +
                                    optimisticLockValue + " now:" + rs.getLong( ExecutionDao.OPTIMISTIC_LOCK_COLUMN ) + ")" ) ;
                            }
                            // 更新対象の楽観的ロックを１インクリメント.
                            optimisticLockValue = new Long( optimisticLockValue.longValue() + 1L ) ;
                            model.put( optimisticLockKey,optimisticLockValue ) ;
                        }
                        // 楽観的ロックカラム内容が存在しない場合は、初期値を付加.
                        else {
                            model.put( optimisticLockKey,new Long( 1 ) ) ;
                        }
                        ret = true ;
                    }
                    rs.close() ;
                    rs = null ;
                }
                return ret ;
            } finally {
                if( rs != null ) {
                    try {
                        rs.close() ;
                    } catch( Exception e ) {
                    }
                }
            }
        }
        // 楽観的ロック対象のカラムが存在しない場合.
        else {
            StringBuilder buf = new StringBuilder() ;
            buf.append( "select count(*) from " ).
                append( DbUtil.convertJavaNameByDBName( modelName ) ).
                append( " where id=?" ) ;
            String sql = buf.toString() ;
            buf = null ;
            
            ResultSet rs = null ;
            try {
                boolean ret = false ;
                rs = record.executeQuery( sql,new Object[]{value} ) ;
                if( rs != null ) {
                    if( rs.next() == true ) {
                        ret = true ;
                    }
                    rs.close() ;
                    rs = null ;
                }
                return ret ;
            } finally {
                if( rs != null ) {
                    try {
                        rs.close() ;
                    } catch( Exception e ) {
                    }
                }
            }
        }
    }
    
    /**
     * アップロード時間をSQL文に追加.
     */
    private static final String addUpdateTime( boolean[] addFlag,String sql ) throws Exception {
        addFlag[0] = false ;
        String lower = sql.toLowerCase() ;
        int s = lower.indexOf( " set " ) ;
        int e = lower.indexOf( " where " ) ;
        if( s == -1 ) {
            return sql ;
        }
        else if( e == -1 ) {
            e = lower.length() ;
        }
        s += " set ".length() ;
        int x = lower.indexOf( " "+ExecutionDao.UPDATE_TIME,s ) ;
        int y = lower.indexOf( ","+ExecutionDao.UPDATE_TIME,s ) ;
        if( x == -1 || x > y ) {
            x = y ;
        }
        lower = null ;
        if( x == -1 || x > e ) {
            sql = new StringBuilder().append( sql.substring( 0,s ) ).
                append( ExecutionDao.UPDATE_TIME ).append( " =?, " ).
                append( sql.substring( s,sql.length() ) ).toString() ;
            addFlag[0] = true ;
        }
        return sql ;
    }
    
    /**
     * アップロード時間をパラメータに追加.
     */
    private static final Object[] addUpdateTimeParam( Object[] params ) throws Exception {
        Object[] ret = null ;
        if( params == null || params.length <= 0 ) {
            ret = new Object[ 1 ] ;
        }
        else {
            int len = params.length ;
            ret = new Object[ len+1 ] ;
            System.arraycopy( params,0,ret,1,len ) ;
        }
        ret[ 0 ] = new java.sql.Timestamp( System.currentTimeMillis() ) ;
        return ret ;
    }
    
    /**
     * find結果の条件に対して、１対１のテーブル結合.
     */
    private static final void findByJoin( List<Map<String,Object>> out,DaoSession session,Map<String,String> join )
        throws Exception {
        if( join == null || join.size() <= 0 || out == null || out.size() <= 0 ) {
            return ;
        }
        int len = out.size() ;
        int lenJ = join.size() ;
        Object[] names = join.keySet().toArray() ;
        for( int i = 0 ; i < len ; i ++ ) {
            Map<String,Object> o = out.get( i ) ;
            for( int j = 0 ; j < lenJ ; j ++ ) {
                String column = ( String )names[ j ] ;
                String table = join.get( column ) ;
                Object id = o.get( column ) ;
                if( id != null ) {
                    Map<String,Object> value = getTableById( session,table,id ) ;
                    o.put( column,value ) ;
                }
            }
        }
    }
    
    /**
     * テーブルIDの内容を取得(１対１).
     */
    private static final Map<String,Object> getTableById( DaoSession session,String modelName,Object id )
        throws Exception {
        Record record = session.getRecord() ;
        MetaColumn meta = session.getMeta( modelName ) ;
        String sql = new StringBuilder().append( "select * from " ).
            append( meta.getTable() ).append( " where id=?" ).toString() ;
        ArrayList<Map<String,Object>> ret = new ArrayList<Map<String,Object>>() ;
        ExecutionDao.execution( ret,null,record,modelName,meta,sql,0,1,new Object[]{ id } ) ;
        if( ret.size() <= 0 ) {
            return null ;
        }
        return ret.get( 0 ) ;
    }
    
    /**
     * テーブル１対他結合名.
     */
    private static final String MANY_JOIN_NAME = "$" ;
    
    /**
     * find結果の条件に対して、１対他のテーブル結合.
     */
    private static final void findByMany( List<Map<String,Object>> out,DaoSession session,Map<String,List<String>> srcJoin )
        throws Exception {
        if( srcJoin == null || srcJoin.size() <= 0 || out == null || out.size() <= 0 ) {
            return ;
        }
        if( out != null && out.size() > 0 ) {
            int len = out.size() ;
            int lenJ = srcJoin.size() ;
            Object[] names = srcJoin.keySet().toArray() ;
            for( int i = 0 ; i < len ; i ++ ) {
                Map<String,Object> o = out.get( i ) ;
                Object id = o.get( "id" ) ;
                if( id != null ) {
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        String table = ( String )names[ j ] ;
                        List<String> columns = srcJoin.get( table ) ;
                        if( columns != null && columns.size() > 0 ) {
                            int lenK = columns.size() ;
                            table = DbUtil.convertJavaNameByDBName( table ) ;
                            for( int k = 0 ; k < lenK ; k ++ ) {
                                String column = columns.get( k ) ;
                                if( column != null && ( column = column.trim() ).length() > 0 ) {
                                    column = DbUtil.convertJavaNameByDBName( column ) ;
                                    ArrayList<Map<String,Object>> res = getTableByMany( session,table,column,id ) ;
                                    String manyName = new StringBuilder().
                                        append( MANY_JOIN_NAME ).
                                        append( DbUtil.convertDBNameByJavaName( true,table ) ).
                                        append( "_" ).
                                        append( DbUtil.convertDBNameByJavaName( false,column ) ).
                                        toString() ;
                                    o.put( manyName,res ) ;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    /**
     * テーブルIDの内容を結合(１対他).
     */
    private static final ArrayList<Map<String,Object>> getTableByMany( DaoSession session,String modelName,String column,Object id )
        throws Exception {
        Record record = session.getRecord() ;
        MetaColumn meta = session.getMeta( modelName ) ;
        String sql = new StringBuilder().append( "select * from " ).
            append( meta.getTable() ).append( " where "+column+"=?" ).toString() ;
        ArrayList<Map<String,Object>> ret = new ArrayList<Map<String,Object>>() ;
        ExecutionDao.execution( ret,null,record,modelName,meta,sql,0,Integer.MAX_VALUE,new Object[]{ id } ) ;
        return ret ;
    }
    
    /**
     * delete条件に対して、結合テーブル情報を削除.
     */
    private static final void deleteBySrcJoin( DaoSession session,Map<String,List<String>> srcJoin,String modelName,String where,Object... params )
        throws Exception {
        if( srcJoin == null || srcJoin.size() <= 0 ) {
            return ;
        }
        Record record = session.getRecord() ;
        MetaColumn meta = session.getMeta( modelName ) ;
        String sql = null ;
        if( where == null || ( where = where.trim() ).length() <= 0 ) {
            sql = new StringBuilder().append( "select id from " ).
                append( meta.getTable() ).toString() ;
            params = null ;
        }
        else {
            sql = new StringBuilder().append( "select id from " ).
                append( meta.getTable() ).append( " " ).append( where ).toString() ;
        }
        ArrayList<Map<String,Object>> out = new ArrayList<Map<String,Object>>() ;
        ExecutionDao.execution( out,null,record,modelName,meta,sql,0,Integer.MAX_VALUE,params ) ;
        if( out != null && out.size() > 0 ) {
            int len = out.size() ;
            int lenJ = srcJoin.size() ;
            Object[] names = srcJoin.keySet().toArray() ;
            for( int i = 0 ; i < len ; i ++ ) {
                Map<String,Object> o = out.get( i ) ;
                Object id = o.get( "id" ) ;
                if( id != null ) {
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        String table = ( String )names[ j ] ;
                        List<String> columns = srcJoin.get( table ) ;
                        if( columns != null && columns.size() > 0 ) {
                            int lenK = columns.size() ;
                            table = DbUtil.convertJavaNameByDBName( table ) ;
                            for( int k = 0 ; k < lenK ; k ++ ) {
                                String column = columns.get( k ) ;
                                if( column != null && ( column = column.trim() ).length() > 0 ) {
                                    column = DbUtil.convertJavaNameByDBName( column ) ;
                                    removeTableById( session,table,column,id ) ;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 削除Joinプラグイン内容を削除する.
     */
    private static final void executionDeleteJoinPlugin( DaoSession session,String modelName,
        DeleteJoinPlugin plugin,ArrayList<String> deleteJoinColumn,String where,Object... params )
        throws Exception {
        if( plugin == null || deleteJoinColumn == null || deleteJoinColumn.size() <= 0 ) {
            return ;
        }
        Record record = session.getRecord() ;
        MetaColumn meta = session.getMeta( modelName ) ;
        String sql = null ;
        if( where == null || ( where = where.trim() ).length() <= 0 ) {
            sql = new StringBuilder().append( "select" ).append( convertArrayListByString( deleteJoinColumn ) ).append( "from " ).
                append( meta.getTable() ).toString() ;
            params = null ;
        }
        else {
            sql = new StringBuilder().append( "select" ).append( convertArrayListByString( deleteJoinColumn ) ).append( "from " ).
                append( meta.getTable() ).append( " " ).append( where ).toString() ;
        }
        ArrayList<Map<String,Object>> out = new ArrayList<Map<String,Object>>() ;
        ExecutionDao.execution( out,null,record,modelName,meta,sql,0,Integer.MAX_VALUE,params ) ;
        if( out.size() > 0 ) {
            int len = out.size() ;
            int lenJ = deleteJoinColumn.size() ;
            for( int i = 0 ; i < len ; i ++ ) {
                Map<String,Object> o = out.get( i ) ;
                if( o != null ) {
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        Object id = o.get( deleteJoinColumn.get( j ) ) ;
                        if( id == null || ( id instanceof Long ) == false ) {
                            continue ;
                        }
                        long idLong = ( ( Long )id ).longValue() ;
                        plugin.remove( idLong ) ;
                    }
                }
            }
        }
    }
    
    /**
     * テーブルIDの内容を削除.
     */
    private static final void removeTableById( DaoSession session,String modelName,String column,Object id )
        throws Exception {
        Record record = session.getRecord() ;
        MetaColumn meta = session.getMeta( modelName ) ;
        String sql = new StringBuilder().append( "delete from " ).
            append( meta.getTable() ).append( " where "+column+"=?" ).toString() ;
        ExecutionDao.execution( null,null,record,modelName,meta,sql,-1,-1,new Object[]{ id } ) ;
    }
    
    /**
     * ArrayList情報を文字列に変換.
     */
    private static final String convertArrayListByString( ArrayList<String> lst ) {
        int len = lst.size() ;
        StringBuilder buf = new StringBuilder() ;
        buf.append( " " ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                buf.append( "," ) ;
            }
            buf.append( DbUtil.convertJavaNameByDBName( lst.get( i ) ) ) ;
        }
        buf.append( " " ) ;
        return buf.toString() ;
    }
    
    /**
     * 指定パラメータをLongで取得.
     */
    private static final Long convertObjectByLong( Object o ) {
        if( o == null ) {
            return null ;
        }
        if( o instanceof String ) {
            return new Long( ( String )o ) ;
        }
        else if( o instanceof Integer ) {
            return new Long( ( long )( ( Integer )o ).intValue() ) ;
        }
        else if( o instanceof Long ) {
            return ( Long )o ;
        }
        return new Long( o.toString() ) ;
    }
}

