    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.plugin.develop;
    017
    018import org.opengion.fukurou.system.OgBuilder ;                          // 6.4.4.1 (2016/03/18)
    019import org.opengion.hayabusa.develop.AbstractJspCreate;
    020import org.opengion.hayabusa.develop.JspEnumeration.GROUPING_FUNCTIONS ;
    021import org.opengion.hayabusa.develop.JspEnumeration.WHERE_OPERATORS ;
    022import org.opengion.hayabusa.develop.JspConvertEntity;
    023import org.opengion.fukurou.xml.OGElement;
    024import static org.opengion.fukurou.util.StringUtil.isNull;
    025
    026import java.util.ArrayList;
    027import java.util.List;
    028import java.util.Map;
    029import java.util.HashMap;
    030import java.util.regex.Matcher;
    031import java.util.regex.Pattern;
    032
    033/**
    034 * result.jspの&lt;og:query &gt;タグを作成します。
    035 *
    036 * ●使用例
    037 *      &lt;og:query
    038 *              command     = "{&#064;command}"
    039 *              debug       = "{&#064;debug}
    040 *              dbid        = "{&#064;FROM_DBID}"
    041 *              maxRowCount = "{&#064;maxRowCount}" &gt;
    042 *          select A1.xx , A1.xx ,・・・
    043 *          from   xxx A1 inner join xxx B1
    044 *          where  ・・・
    045 *          group by  ・・・
    046 *          having    ・・・
    047 *          ORDER BY  ・・・
    048 *      &lt;/og:query&gt;
    049 *
    050 * @og.rev 5.6.1.2 (2013/02/22) 文字列連結から、XML処理するように変更します。
    051 * @author Takeshi.Takada
    052 *
    053 */
    054public class JspCreate_QUERY extends AbstractJspCreate {
    055        /** このプログラムのVERSION文字列を設定します。   {@value} */
    056        private static final String VERSION = "6.4.4.1 (2016/03/18)" ;
    057
    058        private static final String SPACE = "                              " ;  // カラムの位置合わせ用
    059
    060        // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    061        private List<JspConvertEntity> queryROWS        ;
    062        private List<JspConvertEntity> resultROWS       ;
    063        private List<JspConvertEntity> constROWS        ;
    064        private List<JspConvertEntity> joinROWS         ;
    065        private List<JspConvertEntity> joinOnROWS       ;
    066        private List<JspConvertEntity> havingROWS       ;
    067
    068        private String ns = "";         // 5.2.1.0 (2010/10/01) 名前空間
    069
    070        /**
    071         * コンストラクター
    072         *
    073         * インスタンス構築時に、タグ名(key)とファイル名(names)を指定します。
    074         *
    075         * @og.rev 6.3.9.1 (2015/11/27) コンストラクタを用意して、KEY,NAME をセットするように変更します。
    076         */
    077        public JspCreate_QUERY() {
    078                super( ":query" , "result" );
    079        }
    080
    081        /**
    082         * 初期化メソッド
    083         *
    084         * 内部で使用する JspConvertEntity の リスト のマップを受け取り、初期化を行います。
    085         *
    086         * @og.rev 5.2.1.0 (2010/10/01) 名前空間を、og 決め打ちから、名前空間指定無しに変更します。
    087         *
    088         * @param       master  JspConvertEntityのリストのマップ
    089         */
    090        @Override
    091        protected void init( final Map<String,List<JspConvertEntity>> master ) {
    092                // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    093                queryROWS       = master.get( "QUERY" );
    094                resultROWS      = master.get( "RESULT" );
    095                constROWS       = master.get( "CONST" );
    096                joinROWS        = master.get( "JOIN" );
    097                joinOnROWS      = master.get( "JOIN_ON" );
    098                havingROWS      = master.get( "HAVING" );
    099
    100        }
    101
    102        /**
    103         * JSPに出力するタグの内容を作成します。
    104         * 引数より作成前のタグの属性内容を確認するする事が出来ます。
    105         *
    106         * @og.rev 5.2.1.0 (2010/10/01) メソッドの引数を、OGAttributes から OGElement に変更します。
    107         * @og.rev 5.2.1.0 (2010/10/01) 名前空間を、og 決め打ちから、引数を使用するように変更します。
    108         * @og.rev 5.6.4.4 (2013/05/31) select カラムに、コメントを付与します。
    109         *
    110         * @param ele OGElementエレメントオブジェクト
    111         * @param       nameSpace       このドキュメントのnameSpace( og とか mis とか )
    112         *
    113         * @return      変換された文字列
    114         * @og.rtnNotNull
    115         * @throws Throwable 変換時のエラー
    116         */
    117        @Override
    118        protected String execute( final OGElement ele , final String nameSpace )  throws Throwable {
    119                ns = nameSpace.isEmpty() ? "" : nameSpace + ":" ;       // 5.2.1.0 (2010/10/01) 名前空間
    120
    121                // この OGElement の階層の深さを探ります。
    122                // ele.getText( para ) とすることでXML全体を階層表示できる。
    123        //      int para = ele.getParentCount();
    124
    125                // TODO Auto-generated method stub
    126                //書き出す文字列を作成開始。
    127
    128                final List<String> selects              = new ArrayList<>();
    129                final List<String> clmCmnt              = new ArrayList<>();            // 5.6.4.4 (2013/05/31) select カラムに、コメントを付与します。
    130                final List<String> tables               = new ArrayList<>();
    131                final List<String> orders               = new ArrayList<>();
    132                final List<String> group                = new ArrayList<>();
    133                final List<String> having_part  = new ArrayList<>();
    134                final List<String> grp_clm              = new ArrayList<>();            // 6.0.2.5 (2014/10/31) refactoring
    135
    136                //HAVING情報から<og:query>タグのテキスト部を生成する準備をします。
    137                // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    138                if( isNotEmpty(havingROWS) ){
    139                        for( final JspConvertEntity row : havingROWS ){
    140                                having_part.add(row.getRemarks());
    141                                if( GROUPING_FUNCTIONS.search( row.getRemarks() ) ){
    142                                        grp_clm.add( row.getFullColumnName() );         // 6.0.2.5 (2014/10/31) refactoring
    143                                }
    144                        }
    145                }
    146                //RESULT情報から<og:query>タグのテキスト部を生成する準備をします。
    147                boolean grouping = false;
    148                // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    149                if( isNotEmpty(resultROWS) ){
    150                        for( int i=0 ; i<resultROWS.size() ; i++ ){
    151                                final JspConvertEntity result = resultROWS.get(i);
    152                                //Select句の情報を作成
    153                                selects.add( result.getSelectPartColumnName() );
    154                                // 5.6.4.4 (2013/05/31) select カラムに、コメントを付与します。
    155                                clmCmnt.add( result.getTableName() + "." + result.getColumnCommentName() );
    156                                //テーブル名を検証して、テーブル数のみの情報にします。
    157                                if( tables != null && !tables.contains( result.getTableName() ) ) {
    158                                        tables.add( result.getFromPartTableName() );
    159                                }
    160                                //並び順に利用するカラムを取得する。
    161                                if( "1".equals( result.getUseOrder() ) ) {
    162                                        orders.add(Integer.toString( i + 1 ));
    163                                }
    164                                //GROUP BYに必要な情報を取得します。
    165                                if( GROUPING_FUNCTIONS.contains( result.getRemarks() ) ){
    166                                        grouping = true;
    167                //              }else if(having_grouping_column.indexOf( result.getFullColumnName() ) > -1 ){
    168                //                      group.add( result.getFullColumnName() );
    169                                }else{
    170                                        group.add( result.getFullColumnName() );
    171                                }
    172                        }
    173                }
    174
    175                //JOIN情報から<og:query>タグのテキスト部(join句)を生成する準備をします。
    176                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    177
    178                final JspConvertEntity join_on = isNotEmpty(joinOnROWS) ? joinOnROWS.get( 0 ) : null;           // 6.3.9.1 (2015/11/27)
    179
    180                //JOIN情報から<og:query><og:where><og:and>タグの検索句を生成する準備をします。
    181                // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    182                if( queryROWS != null && isNotEmpty( constROWS ) ){
    183                        queryROWS.addAll( constROWS );
    184                }
    185
    186                final OGElement queryEle  = new OGElement( ns + "query" );
    187                queryEle.addAttr( "command"             ,"{@command}" );
    188                queryEle.addAttr( "debug"               ,"{@debug}" );
    189                queryEle.addAttr( "dbid"                ,"{@FROM_DBID}" );
    190                queryEle.addAttr( "maxRowCount" ,"{@maxRowCount}" );
    191
    192                // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
    193                queryEle.addNode( queryText(selects , clmCmnt , tables , joinROWS  , join_on ) );       // 5.6.4.4 (2013/05/31) select カラムに、コメントを付与
    194
    195                if( isNotEmpty(queryROWS) ){            // 6.3.9.1 (2015/11/27)
    196                        final OGElement whereEle = new OGElement( ns + "where" );
    197
    198                        for( final JspConvertEntity where : queryROWS ) {               // 6.3.9.1 (2015/11/27)
    199                                if( "QUERY".equals(where.getType()) ){
    200                                        whereEle.addNode( andWhereQuery(where.getFullColumnName() , where.getRemarks() ,"{@"+ where.getColumnName() +"}" ,where.isNumber()) );
    201                                }
    202                                if( "CONST".equals(where.getType()) ) {
    203                                        whereEle.addNode( andWhereConst( where.getFullColumnName(), where.getRemarks() , where.isNumber()) );
    204                                }
    205                        }
    206                        queryEle.addNode( whereEle );
    207                }
    208                if( grouping || !grp_clm.isEmpty() ) {                                  // 6.0.2.5 (2014/10/31) refactoring
    209                        queryEle.addNode( T2 + "group by " + chainChar(group,",") + CR );
    210                }
    211                if( !grp_clm.isEmpty() ){                                                               // 6.0.2.5 (2014/10/31) refactoring
    212                        queryEle.addNode( T2 + "having " + chainChar(having_part ," and ") + CR );
    213                }
    214                if( !orders.isEmpty() ){
    215                        queryEle.addNode( apperEle( "ORDER BY" , "ORDER_BY" , orders ) );
    216                }
    217
    218                return queryEle.getText(0);
    219        }
    220
    221        /**
    222         * result.jspのog:queryタグのテキスト部を生成します。
    223         *
    224         * 補足１
    225         * 引数のjoin_onがnullでないときは、優先的にjoin_onの内容でJOIN句を生成します。
    226         *
    227         * @og.rev 5.6.4.4 (2013/05/31) select カラムに、コメントを付与します。
    228         * @og.rev 6.3.9.1 (2015/11/27) 継承されていないため、protected → private に変更します。
    229         * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
    230         *
    231         * @param       selects 検索SQLのリスト
    232         * @param       clmCmnt カラムコメントのリスト
    233         * @param       tables  テーブル名のリスト
    234         * @param       joins   JspConvertEntityのリスト
    235         * @param       join_on JspConvertEntityオブジェクト
    236         *
    237         * @return      og:queryタグのテキスト部
    238         * @og.rtnNotNull
    239         */
    240        private String queryText( final List<String> selects , final List<String> clmCmnt , final List<String> tables ,
    241                                                                final List<JspConvertEntity> joins ,final JspConvertEntity join_on ) {
    242
    243                final OgBuilder buf = new OgBuilder()
    244                                .appendCR()
    245                                .appendCR( T2 , "select" );
    246
    247                // 5.6.4.4 (2013/05/31) select カラムに、コメントを付与します。
    248                final int size = selects.size();
    249                for( int i=0; i<size; i++ ) {
    250                        final String clm = selects.get(i) ;
    251                        buf.append( T3 )
    252                                .appendCase( i == 0  , " " , "," )              // flag , true , false
    253                                .appendCR( clm , SPACE.substring( clm.length() )
    254                                                        , T3 , T3 , T3 , T3
    255                                                        , "<!-- " , clmCmnt.get(i) , " -->" );
    256                }
    257
    258                buf.append( T2 , "from " );
    259
    260                if( join_on != null ) {
    261                        //JOIN_ONが存在する場合は、直接SQLを組み立てて処理を終了する。
    262                        return buf.append( T2 , join_on.getRemarks() ).toString();
    263                }
    264
    265                if( !isNotEmpty( joins ) ) {
    266                        return buf.append( tables.get(0) ).toString();
    267                }
    268
    269                //テーブルの内容を構造化します。
    270                final TableStruct structs = createStruct(joins);
    271
    272                String beforeLeft  = "";
    273                String beforeRight = "";
    274                final StringBuilder sbPre = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring
    275
    276                final Map<String,String> joinPartsMap = new HashMap<>();
    277
    278                boolean isStartJoin = false;
    279
    280                for( int i=0 ; i<joins.size() ; i++ ){
    281                        //join句を作るのとネスト構造を作るのは処理を分離させる。
    282                        final JspConvertEntity join = joins.get( i );
    283
    284                        if( beforeLeft.equals(join.getFromPartTableName()) ){
    285                                //前の処理と左側のテーブルは同じ
    286                                if( ! beforeRight.equals(join.getJoinColumn().getFromPartTableName()) ) {
    287                                        //前の処理と右側のテーブルが違う
    288                                        sbPre.append( sqlJoinOn( "", join.getJoinColumn().getFromPartTableName() , join.getJoinType()) );
    289                                        isStartJoin = true;
    290                                }
    291                        } else {
    292                                //前の処理と左側のテーブルが違う
    293                                if( ! beforeRight.equals(join.getJoinColumn().getFromPartTableName()) ) {
    294                                        //前の処理と右側のテーブルが違う
    295                                        //前の処理のJoin句をテーブル名別にセット
    296                                        final String str = sbPre.toString();
    297                                        joinPartsMap.put( beforeLeft, str );
    298                                        //バッファを初期化
    299                                        sbPre.setLength(0);
    300                                        sbPre.append( sqlJoinOn( join.getFromPartTableName() , join.getJoinColumn().getFromPartTableName() , join.getJoinType()) );
    301                                        isStartJoin = true;
    302                                }
    303                        }
    304                        if( !isStartJoin  ) {
    305                                sbPre.append( CR ).append( T3 ).append( "and").append( T1 );
    306                        }
    307                        sbPre.append( join.getFullColumnName() )
    308                                .append( T2 ).append( '=' ).append( T1 )                // 6.0.2.5 (2014/10/31) char を append する。
    309                                .append( join.getJoinColumn().getFullColumnName() );
    310                        beforeLeft  = join.getFromPartTableName();
    311                        beforeRight = join.getJoinColumn().getFromPartTableName();
    312                        isStartJoin = false;
    313                }
    314                //最終分
    315                joinPartsMap.put( beforeLeft, sbPre.toString() );
    316
    317                final StringBuilder sbJoin = new StringBuilder( BUFFER_MIDDLE );
    318                //Join句を組み立てます。
    319                createJoinPart(structs.getJoinTables(),joinPartsMap,sbJoin);
    320
    321                return buf.append( sbJoin.toString() ).toString();
    322
    323        }
    324
    325        /**
    326         * join句の一部を作成する。
    327         *
    328         * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
    329         *
    330         * @param       left            join句のレフト
    331         * @param       right           join句のライト
    332         * @param       join_type       [1:inner join/その他:left outer join]
    333         *
    334         * @return      join句の一部
    335         * @og.rtnNotNull
    336         */
    337        private String sqlJoinOn(final String left , final String right ,final String join_type){
    338                return new OgBuilder()
    339                                .append( " " , left )
    340                                .appendCase( "1".equals( join_type )
    341                                                                , " inner join "                        // true
    342                                                                , " left outer join " )         // false
    343                                .appendCR( right )
    344                                .append( T3 , "on" , T1 )
    345                                .toString();
    346
    347        }
    348
    349        /**
    350         * JOIN句を組み立てます。
    351         *
    352         * JOIN句は、内部で再帰処理されます。引数の StringBuilder に最終的な JOIN句が格納されます。
    353         *
    354         * @param       structs テーブル内容の構造化TableStructのリスト
    355         * @param       joinMap JOIN句マップ
    356         * @param       buff    StringBuilderオブジェクト
    357         */
    358        private void createJoinPart( final List<TableStruct> structs , final Map<String,String> joinMap ,final StringBuilder buff ) {
    359                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    360                for( int i=0 ; i<structs.size() ; i++ ){
    361                        final TableStruct struct = structs.get(i);
    362                        final String part = joinMap.get(struct.getTableName());
    363                        if( part != null ){
    364                                final Matcher matcher = Pattern.compile( "join " + struct.getTableName() ).matcher( buff );
    365                                if( matcher.find()) {
    366                                        final int start = matcher.start();
    367                                        buff.delete(start,matcher.end());
    368                                        buff.insert(start , "join ( " + part + " )" );
    369                                }else{
    370                                        buff.append( part );
    371                                }
    372                        }
    373                        createJoinPart(struct.getJoinTables(),joinMap,buff);
    374                }
    375        }
    376
    377        /**
    378         * result.jspの og:query og:appear タグを生成します。
    379         *
    380         * @og.rev 5.2.1.0 (2010/10/01) 名前空間を、og 決め打ちから、引数を使用するように変更します。
    381         * @og.rev 6.3.9.1 (2015/11/27) 継承されていないため、protected → private に変更します。
    382         *
    383         * @param start_key             開始キー
    384         * @param value                 値
    385         * @param default_value 初期値リスト
    386         *
    387         * @return      og:query og:appear タグ
    388         * @og.rtnNotNull
    389         */
    390        private OGElement apperEle( final String start_key , final String value , final List<String> default_value ){
    391
    392                final OGElement apper = new OGElement( ns + "appear" );
    393                apper.addAttr( "startKey"               ,start_key );
    394                apper.addAttr( "value"                  ,"{@" + value + "}" );
    395                apper.addAttr( "defaultVal"             ,chainChar( default_value , "," ) );
    396                return apper;
    397        }
    398
    399        /**
    400         * result.jspの og:query og:where og:and タグを生成します。
    401         * 処理グループ：QUERY
    402         *
    403         * @og.rev 6.3.9.1 (2015/11/27) 継承されていないため、protected → private に変更します。
    404         *
    405         * @param       left            左側式
    406         * @param       operator        オペレーター
    407         * @param       right           右側式
    408         * @param       is_number       数字かどうか[true/false]
    409         *
    410         * @return      og:and タグ
    411         * @og.rtnNotNull
    412         */
    413        private OGElement andWhereQuery( final String left , final String operator , final String right , final boolean is_number){
    414
    415                final String ope = isNull(operator) ? "eq" : operator ;
    416
    417                final WHERE_OPERATORS wrOpe = WHERE_OPERATORS.valueOf( ope );
    418
    419                final OGElement and = new OGElement( ns + "and" );
    420                and.addAttr( "value" , wrOpe.apply( left , right , is_number ) );
    421                return and;
    422        }
    423
    424        /**
    425         * result.jspのog:query og:where og:and タグを生成します。
    426         * 処理グループ：CONST
    427         *
    428         * @og.rev 6.3.9.1 (2015/11/27) 継承されていないため、protected → private に変更します。
    429         *
    430         * @param       left            左側式
    431         * @param       right           右側式
    432         * @param       is_number       数字かどうか[true/false]
    433         *
    434         * @return      og:and タグ
    435         */
    436        private OGElement andWhereConst( final String left , final String right , final boolean is_number ){
    437                final String operator = right.indexOf( ',' ) >= 0 ? "in" : "eq";
    438                return  andWhereQuery( left , operator , right , is_number );
    439        }
    440
    441        /**
    442         * query.jspの og:column タグを生成します。
    443         *
    444         * @param name                  タグのname
    445         * @param default_value 初期値
    446         *
    447         * @return      og:columnタグ
    448         */
    449
    450        /**
    451         * テーブルの結合関係を再現する構造体につめ直すメソッド。
    452         *
    453         * @param       joins   JspConvertEntityのリスト
    454         *
    455         * @return      テーブルの結合関係を再現する構造体
    456         * @og.rtnNotNull
    457         */
    458        private TableStruct createStruct( final List<JspConvertEntity> joins ) {
    459                final TableStruct st = new TableStruct();
    460                for( int i=0 ; i<joins.size() ; i++ ){
    461                        final JspConvertEntity join = joins.get( i );
    462                        final String left_name = join.getFromPartTableName();
    463                        final String right_name = join.getJoinColumn().getFromPartTableName();
    464
    465                        TableStruct left = st.getJoinTable( left_name );
    466                        TableStruct right = st.getJoinTable( right_name );
    467
    468                        if( left == null && right == null ) {
    469                                //全くの新規。
    470                                left = new TableStruct();
    471                                left.setTableName( left_name );
    472                                right = new TableStruct();
    473                                right.setTableName(right_name);
    474                                left.addJoinTable( right );
    475                                st.addJoinTable( left );
    476                        }else{
    477                                if( left != null && right == null ){
    478                                        right = new TableStruct();
    479                                        right.setTableName(right_name);
    480                                        left.addJoinTable( right );
    481                                }
    482                        }
    483                }
    484                return st;
    485        }
    486
    487        /**
    488         * テーブルの結合状態を階層構図にする為のオブジェクト
    489         *
    490         * @author Administrator
    491         *
    492         */
    493        private static final class TableStruct {
    494                private final List<TableStruct> _joins = new ArrayList<>();
    495                private String tableName;
    496
    497                /**
    498                 * テーブル名を設定。
    499                 *
    500                 * @param table_name テーブル名
    501                 */
    502                public void setTableName( final String table_name ) {
    503                        tableName = table_name;
    504                }
    505
    506                /**
    507                 * テーブル名を取得。
    508                 *
    509                 * @return テーブル名
    510                 */
    511                public String getTableName() {
    512                        return tableName;
    513                }
    514
    515                /**
    516                 * 結合テーブルを追加。
    517                 *
    518                 * @param join_table 結合テーブル
    519                 */
    520                public void addJoinTable( final TableStruct join_table ) {
    521                        _joins.add(join_table);
    522                }
    523
    524                /**
    525                 * 結合テーブルを全て取得。
    526                 *
    527                 * @return 全ての結合テーブル
    528                 */
    529                public List<TableStruct> getJoinTables() {
    530                        return _joins;
    531                }
    532
    533                /**
    534                 * 指定したテーブルを取得。
    535                 *
    536                 * @param table_name テーブル名
    537                 * @return 指定したテーブル
    538                 */
    539                public TableStruct getJoinTable( final String table_name ) {
    540                        return search(_joins,table_name);
    541                }
    542
    543                /**
    544                 * テーブル同士が一致しているか検証する。
    545                 *
    546                 * @param table_name テーブル名
    547                 * @return 検証した結果の真偽
    548                 */
    549                public boolean equalTable( final String table_name ) {
    550                        return tableName != null && tableName.equals( table_name ) ;
    551                }
    552
    553                /**
    554                 * 指定したテーブルが存在しているか検証する。
    555                 *
    556                 * @param table_name テーブル名
    557                 * @return  検証した結果の真偽
    558                 */
    559                public boolean constains( final String table_name ) {
    560                        for( int i=0; i<_joins.size() ; i++ ){
    561                                final TableStruct join = _joins.get( i );
    562                                if( join.equalTable(table_name) ){
    563                                        return true;
    564                                }
    565                        }
    566                        return false;
    567                }
    568
    569                /**
    570                 * 結合先を含めて指定したテーブルを取得する。
    571                 *
    572                 * @param joins 結合先Listオブジェクト
    573                 * @param table_name テーブル名
    574                 * @return 指定したテーブル
    575                 */
    576                private TableStruct search( final List<TableStruct> joins , final String table_name ) {
    577                        TableStruct join =  null;
    578                        for( int i=0; i<joins.size() ; i++ ){
    579                                join = joins.get( i );
    580                                if( join.equalTable(table_name) ){
    581                                        return join;
    582                                } else {
    583                                        join = search(join.getJoinTables(),table_name);
    584                                }
    585                        }
    586                        return join;
    587                }
    588        }
    589}