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