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