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