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.view;
017
018import java.util.List;
019
020import org.opengion.fukurou.util.StringUtil;
021import org.opengion.fukurou.util.TagBuffer;
022import org.opengion.fukurou.util.XHTMLTag;
023import org.opengion.hayabusa.common.HybsSystem;
024import org.opengion.hayabusa.common.HybsSystemException;
025import org.opengion.hayabusa.html.FormatterType;
026import org.opengion.hayabusa.html.TableFormatter;
027import org.opengion.hayabusa.html.ViewAjaxTreeTableParam;
028
029/**
030 * JavaScript のツリー階層を持ったテーブル表示を行う、ツリーテーブル表示クラスです。
031 *
032 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。
033 * 各HTMLのタグに必要な setter/getterメソッドのみ,追加定義しています。
034 *
035 * AbstractViewForm を継承している為,ロケールに応じたラベルを出力させる事が出来ます。
036 *
037 * @og.rev 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
038 * @og.group 画面表示
039 *
040 * @version  4.0
041 * @author   Hiroki Nakamura
042 * @since    JDK5.0,
043 */
044public class ViewForm_HTMLAjaxTreeTable extends ViewForm_HTMLCustomTable  {
045        /** このプログラムのVERSION文字列を設定します。   {@value} */
046        private static final String VERSION = "6.8.1.1 (2017/07/22)" ;
047
048//      // 6.4.4.2 (2016/04/01) JSP + "/image/" にする。
049//      private static final String JSPIMG = HybsSystem.sys( "JSP" ) + "/image/" ;
050        // 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
051        private static final String JSPIMG = HybsSystem.sys( "JSP_IMG" ) + "/" ;        // 互換性の関係で最後に"/"を追加
052
053        private int[]                   childSearchKeys ;
054        private String                  childSearchJsp  ;
055        private String                  levelClm                ;
056        private int                             levelClmPos             = -1;
057        private String                  imgCollapsed    ;
058        private String                  imgExpanded     ;
059        private String                  imgNoSub        ;
060        private boolean                 expandAll               ;               // 4.3.3.0 (2008/10/01)
061        private int                             childViewStartNo= -1;   // 4.3.3.0 (2008/10/01)
062        private int                             expCtrlClmPos   = -1;   // 4.3.5.0 (2008/02/01)
063
064        /**
065         * デフォルトコンストラクター
066         *
067         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
068         */
069        public ViewForm_HTMLAjaxTreeTable() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
070
071        /**
072         * DBTableModel から HTML文字列を作成して返します。
073         * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
074         * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
075         *
076         * @og.rev 4.3.3.0 (2008/10/01) noTransition属性,childViewStartNo属性対応
077         * @og.rev 4.3.7.4 (2009/07/01) tbodyタグの入れ子を解消(FireFox対応)
078         * @og.rev 6.4.3.4 (2016/03/11) tdに、[カラム]が無いケースで、次の[カラム]のクラス属性が、前方すべてのtdにセットされてしまう対応。
079         * @og.rev 6.4.4.2 (2016/04/01) TableFormatterのタイプ別値取得処理の共通部をまとめる。
080         * @og.rev 6.4.5.0 (2016/04/08) メソッド変更( getColumnDbType(int) → getClassName(int) )
081         * @og.rev 6.8.1.1 (2017/07/22) ckboxTD変数は、<td> から <td に変更します(タグの最後が記述されていない状態でもらう)。
082         *
083         * @param  strNo     表示開始位置
084         * @param  pageSize  表示件数
085         *
086         * @return  DBTableModelから作成された HTML文字列
087         * @og.rtnNotNull
088         */
089        @Override
090        public String create( final int strNo, final int pageSize )  {
091                if( getRowCount() == 0 ) { return ""; } // 暫定処置
092
093                initParam();
094
095                // 4.3.3.0 (2008/10/01) 子データ差分取得用
096                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
097                final int startNo = childViewStartNo >= 0 ? childViewStartNo : strNo;
098
099                if( headerFormat == null ) {
100                        makeDefaultFormat();
101                }
102
103                headerFormat.makeFormat( getDBTableModel() );
104
105                final StringBuilder out = new StringBuilder( BUFFER_LARGE )
106                        .append( getCountForm( startNo,pageSize ) )
107                        .append( getHeader() );
108
109                if( bodyFormatsCount == 0 ) {
110                        bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT];
111                        bodyFormats[0] = headerFormat ;
112                        bodyFormatsCount ++ ;
113                }
114                else {
115                        for( int i=0; i<bodyFormatsCount; i++ ) {
116                                bodyFormats[i].makeFormat( getDBTableModel() );
117                        }
118                }
119
120                int bgClrCnt = 0;
121                final int lastNo = getLastNo( startNo, pageSize );                              // 6.3.9.1 (2015/11/27) forループの近くに移動
122                for( int row=startNo; row<lastNo; row++ ) {
123                        if( isSkip( row ) || isSkipNoEdit( row ) ) { continue; }        // 4.3.1.0 (2008/09/08)
124                        for( int i=0; i<bodyFormatsCount; i++ ) {
125                                final TableFormatter bodyFormat = bodyFormats[i];
126                                if( ! bodyFormat.isUse( row,getDBTableModel() ) ) { continue; }         // 3.5.4.0 (2003/11/25)
127                                out.append("<tbody").append( getBgColorCycleClass( bgClrCnt++,row ) );
128                                if( isNoTransition() ) { // 4.3.3.0 (2008/10/01)
129                                        out.append( getHiddenRowValue( row ) );
130                                }
131                                out.append('>')         // 6.0.2.5 (2014/10/31) char を append する。
132                                        .append( bodyFormat.getTrTag() );
133
134                                if( isNumberDisplay() ) {
135                                        final String ckboxTD = "<td" + bodyFormat.getRowspan();         // 6.8.1.1 (2017/07/22)
136                                        out.append( makeCheckbox( ckboxTD,row,0 ) );
137                                }
138
139                                int cl = 0;
140                                for( ; cl<bodyFormat.getLocationSize(); cl++ ) {
141                                        String fmt = bodyFormat.getFormat(cl);
142                                        final int loc = bodyFormat.getLocation(cl);
143                                        if( ! bodyFormat.isNoClass() && loc >= 0 ) {
144                                                // 6.4.3.4 (2016/03/11) tdに、[カラム]が無いケースで、次の[カラム]のクラス属性が、前方すべてのtdにセットされてしまう対応。
145                                                final int idx = fmt.lastIndexOf( "<td" );
146                                                if( idx >= 0 ) {        // matchしてるので、あるはず
147                                                        final String tdclass = " class=\"" + getClassName(loc) + "\" ";                 // 6.4.5.0 (2016/04/08)
148                                                        fmt = fmt.substring( 0,idx+3 ) + tdclass + fmt.substring( idx+3 ) ;
149                                                }
150                                        }
151                                        out.append( fmt );
152                                        if( loc >= 0 ) {
153                                                if( levelClm != null && levelClm.equals( getDBColumn( loc ).getName() ) ) {
154                                                        out.append( getLvlClmTag( row ) );
155                                                }
156                                                else {
157                                                        // 6.4.4.2 (2016/04/01) 処理の共通部をまとめる。
158                                                        out.append( getTypeCaseValue( bodyFormat.getType(cl),row,loc ) );
159                                                }
160                                        }
161                                        else {
162                                                out.append( bodyFormat.getSystemFormat(row,loc) );
163                                        }
164                                }
165                                out.append( bodyFormat.getFormat(cl) )
166                                        .append("</tbody>").append( CR );
167                        }
168                }
169
170                if( footerFormat != null ) {
171                        // 6.3.9.0 (2015/11/06) 引数にTableFormatterを渡して、処理の共有化を図る。
172                        out.append( getTableFoot( footerFormat ) );
173                }
174
175                out.append("</table>").append( CR )
176                        .append( getScrollBarEndDiv() )
177                        .append( getParameterTag() );
178
179                return out.toString();
180        }
181
182        /**
183         * フォーマットを設定します。
184         *
185         * @param       list    TableFormatterのリスト
186         */
187        @Override
188        public void setFormatterList( final List<TableFormatter> list ) {               // 4.3.3.6 (2008/11/15) Generics警告対応
189                bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT];
190
191                bodyFormatsCount = 0;
192                // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
193                for( final TableFormatter format : list ) {
194//              for( int i=0; i<list.size(); i++ ) {
195//                      final TableFormatter format = list.get( i );            // 4.3.3.6 (2008/11/15) Generics警告対応
196                        switch( format.getFormatType() ) {
197                                case TYPE_HEAD : headerFormat = format; break;
198                                case TYPE_BODY : bodyFormats[bodyFormatsCount++] = format; break;
199                                case TYPE_FOOT : footerFormat = format; break;
200                                default : final String errMsg = "FormatterType の定義外の値が指定されました。";
201                                // 4.3.4.4 (2009/01/01)
202                                  throw new HybsSystemException( errMsg );
203                        }
204                }
205        }
206
207        /**
208         * フォーマッターが設定されていない場合は、DBTableModelの情報からデフォルトの
209         * フォーマッターを作成します。
210         *
211         * @og.rev 4.3.3.6 (2008/11/15) columnDisplay,noDisplay対応
212         * @og.rev 4.3.5.0 (2008/02/01) 全展開コントロール用カラムへの対応
213         */
214        private void makeDefaultFormat() {
215                final String[] clms = getDBTableModel().getNames();
216                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
217                                        .append( "<tr>" );
218                for( int i=0; i<clms.length; i++ ) {
219                        if( isColumnDisplay( i ) && i != expCtrlClmPos ) {      // 4.3.3.6 (2008/11/15) // 4.3.5.0 (2008/02/01)
220                                buf.append( "<td>[" ).append( clms[i] ).append( "]</td>" );     // 6.4.4.2 (2016/04/01)
221                        }
222                }
223                buf.append( "</tr>" );
224
225                final TableFormatter formatter = new TableFormatter();
226                formatter.setFormat( buf.toString() );
227                formatter.setFormatType( FormatterType.TYPE_HEAD );
228
229                headerFormat = formatter;
230        }
231
232        /**
233         * フォーマットメソッドを使用できるかどうかを問い合わせます。
234         *
235         * @return      フォーマットメソッドを使用できるか
236         */
237        @Override
238        public boolean canUseFormat() {
239                return true;
240        }
241
242        /**
243         * 初期パラメーターを設定します。
244         *
245         * @og.rev 4.3.3.0 (2008/10/01) 初期全展開の属性追加
246         * @og.rev 4.3.5.0 (2008/02/01) 全展開時の状態をコントロールするためのフラグを追加
247         */
248        private void initParam() {
249                final String[] tmp      = StringUtil.csv2Array( getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_KEYS, "" ) );
250                childSearchKeys = new int[tmp.length];
251                for( int i=0; i<tmp.length; i++ ) {
252                        childSearchKeys[i] = getDBTableModel().getColumnNo( tmp[i] );
253                }
254                childSearchJsp  = getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, "getChildTag.jsp" );
255                levelClm                = getParam( ViewAjaxTreeTableParam.LVL_CLM_KEY, "LVL" );
256                levelClmPos             = getDBTableModel().getColumnNo( levelClm );
257                imgCollapsed    = getParam( ViewAjaxTreeTableParam.IMG_COLLAPSED, "collapsed.gif" );
258                imgExpanded     = getParam( ViewAjaxTreeTableParam.IMG_EXPANDED, "expanded.gif" );
259                imgNoSub        = getParam( ViewAjaxTreeTableParam.IMG_NO_SUB, "nosub.gif" );
260                expandAll               = Boolean.valueOf( getParam( ViewAjaxTreeTableParam.EXPAND_ALL, "false" ) );            // 4.3.2.0 (2008/09/11)
261                childViewStartNo= Integer.parseInt( getParam( ViewAjaxTreeTableParam.CHILD_VIEW_START_NO, "-1" ) );     // 6.0.2.4 (2014/10/17) メソッド間違い
262                final String expCtrlClm = getParam( ViewAjaxTreeTableParam.EXPAND_CONTROL_CLM_KEY, "EXPAND_CONTROL" );  // 4.3.5.0 (2008/02/01)
263                expCtrlClmPos   = getDBTableModel().getColumnNo( expCtrlClm, false );
264        }
265
266        /**
267         * JavaScriptに渡すためのパラメータをhiddenタグで出力します。
268         *
269         * @og.rev 4.3.3.0 (2008/10/01) 初期全展開対応
270         * @og.rev 4.3.5.0 (2008/02/01) 全展開時の状態をコントロールするためのフラグを追加
271         * @og.rev 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
272         * @og.rev 6.4.3.4 (2016/03/11) forループを、2回まわさずに、1回にまとめます。
273         *
274         * @param row 行番号
275         *
276         * @return HTMLタグ
277         * @og.rtnNotNull
278         */
279        private String getLvlClmTag( final int row ) {
280                // 6.4.2.0 (2016/01/29) ソースを見ていたら、共通値が結構あったので、再利用します。
281                final String lvlClmVal = getValue( row, levelClmPos );
282
283                // 6.4.3.4 (2016/03/11) forループを、2回まわさずに、1回にまとめます。
284                final StringBuilder keys = new StringBuilder( BUFFER_MIDDLE ).append( "command," ).append( levelClm );
285                final StringBuilder vals = new StringBuilder( BUFFER_MIDDLE ).append( "NEW,"     ).append( lvlClmVal );
286
287                // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
288                for( final int clm : childSearchKeys ) {
289//              for( int i=0; i<childSearchKeys.length; i++ ) {
290//                      final int clm = childSearchKeys[i];
291                        keys.append( ',' ).append( getColumnName( clm ) );              // 6.0.2.5 (2014/10/31) char を append する。
292                        vals.append( ',' ).append( getValue( row, clm ) );              // 6.0.2.5 (2014/10/31) char を append する。
293                }
294
295                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
296                final String imgsrc ;
297                final StringBuilder clazz = new StringBuilder( BUFFER_MIDDLE );
298                clazz.append( "lvlctl unreplaceable" );
299                if( expandAll ) { // 4.3.3.0 (2008/10/01)
300                        if( row == getRowCount() - 1
301                                        || Integer.parseInt( lvlClmVal ) >= Integer.parseInt( getValue( row+1, levelClmPos ) ) ) {                              // 6.4.2.0 (2016/01/29)
302                                final boolean isExp = expCtrlClmPos > -1 && StringUtil.nval( getValue( row, expCtrlClmPos ), false ) ;
303                                if( isExp ) {
304                                        imgsrc = JSPIMG + imgCollapsed;
305                                }
306                                else {
307                                        imgsrc = JSPIMG + imgNoSub;
308                                        clazz.append( " fetched nosub" );
309                                }
310                        }
311                        else {
312                                imgsrc = JSPIMG + imgExpanded;
313                                clazz.append( " fetched expanded" );
314                        }
315                }
316                else {
317                        imgsrc = JSPIMG + imgCollapsed;
318                }
319
320                // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
321                final String tag = new TagBuffer( "img" )                       // 6.1.1.0 (2015/01/17) refactoring. 連結記述
322                        .add( "class"   , clazz.toString() )
323                        .add( "src"             , imgsrc )
324                        .add( "alt"             , "Level " + lvlClmVal )                // 6.4.2.0 (2016/01/29) 共通値を設定する。
325                        .add( "title"   , "Level " + lvlClmVal )                // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
326                        .add( "lvl"             , lvlClmVal )                                   // 6.4.2.0 (2016/01/29) 共通値を設定する。
327                        .add( "keys"    , keys.toString() )
328                        .add( "vals"    , vals.toString() )
329                        .makeTag();
330
331                return getRendererValue( row, levelClmPos ) + tag;
332        }
333
334        /**
335         * JavaScriptに渡すためのパラメーターをhiddenタグをして出力します。
336         *
337         * @return hiddenタグ
338         * @og.rtnNotNull
339         */
340        private String getParameterTag() {
341                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
342                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, childSearchJsp ) )
343                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_COLLAPSED, JSPIMG + imgCollapsed ) )
344                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_EXPANDED,  JSPIMG + imgExpanded ) )
345                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_NO_SUB,    JSPIMG + imgNoSub ) );
346                return buf.toString();
347        }
348}