    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 = "8.0.3.0 (2021/12/17)" ;
    047
    048//      // 6.4.4.2 (2016/04/01) JSP + "/image/" にする。
    049//      private static final String JSPIMG = HybsSystem.sys( "JSP" ) + "/image/" ;
    050        // 8.0.3.0 (2021/12/17) hayabusa.taglib.ViewAjaxTreeParamTag へ移動
    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変数は、&lt;td&gt; から &lt;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         * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。
    248         */
    249        private void initParam() {
    250                final String[] tmp      = StringUtil.csv2Array( getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_KEYS, "" ) );
    251                childSearchKeys = new int[tmp.length];
    252                for( int i=0; i<tmp.length; i++ ) {
    253                        childSearchKeys[i] = getDBTableModel().getColumnNo( tmp[i] );
    254                }
    255                childSearchJsp  = getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, "getChildTag.jsp" );
    256                levelClm                = getParam( ViewAjaxTreeTableParam.LVL_CLM_KEY, "LVL" );
    257                levelClmPos             = getDBTableModel().getColumnNo( levelClm );
    258//              imgCollapsed    = getParam( ViewAjaxTreeTableParam.IMG_COLLAPSED, "collapsed.gif" );
    259//              imgExpanded     = getParam( ViewAjaxTreeTableParam.IMG_EXPANDED, "expanded.gif" );
    260//              imgNoSub        = getParam( ViewAjaxTreeTableParam.IMG_NO_SUB, "nosub.gif" );
    261                imgCollapsed    = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_COLLAPSED, "collapsed.gif" ) );    // 8.0.3.0 (2021/12/17)
    262                imgExpanded     = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_EXPANDED, "expanded.gif" ) );              // 8.0.3.0 (2021/12/17)
    263                imgNoSub        = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_NO_SUB, "nosub.gif" ) );                   // 8.0.3.0 (2021/12/17)
    264                expandAll               = Boolean.valueOf( getParam( ViewAjaxTreeTableParam.EXPAND_ALL, "false" ) );            // 4.3.2.0 (2008/09/11)
    265                childViewStartNo= Integer.parseInt( getParam( ViewAjaxTreeTableParam.CHILD_VIEW_START_NO, "-1" ) );     // 6.0.2.4 (2014/10/17) メソッド間違い
    266                final String expCtrlClm = getParam( ViewAjaxTreeTableParam.EXPAND_CONTROL_CLM_KEY, "EXPAND_CONTROL" );  // 4.3.5.0 (2008/02/01)
    267                expCtrlClmPos   = getDBTableModel().getColumnNo( expCtrlClm, false );
    268        }
    269
    270        /**
    271         * JavaScriptに渡すためのパラメータをhiddenタグで出力します。
    272         *
    273         * @og.rev 4.3.3.0 (2008/10/01) 初期全展開対応
    274         * @og.rev 4.3.5.0 (2008/02/01) 全展開時の状態をコントロールするためのフラグを追加
    275         * @og.rev 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
    276         * @og.rev 6.4.3.4 (2016/03/11) forループを、２回まわさずに、１回にまとめます。
    277         * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。
    278         *
    279         * @param row 行番号
    280         *
    281         * @return HTMLタグ
    282         * @og.rtnNotNull
    283         */
    284        private String getLvlClmTag( final int row ) {
    285                // 6.4.2.0 (2016/01/29) ソースを見ていたら、共通値が結構あったので、再利用します。
    286                final String lvlClmVal = getValue( row, levelClmPos );
    287
    288                // 6.4.3.4 (2016/03/11) forループを、２回まわさずに、１回にまとめます。
    289                final StringBuilder keys = new StringBuilder( BUFFER_MIDDLE ).append( "command," ).append( levelClm );
    290                final StringBuilder vals = new StringBuilder( BUFFER_MIDDLE ).append( "NEW,"     ).append( lvlClmVal );
    291
    292                // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
    293                for( final int clm : childSearchKeys ) {
    294//              for( int i=0; i<childSearchKeys.length; i++ ) {
    295//                      final int clm = childSearchKeys[i];
    296                        keys.append( ',' ).append( getColumnName( clm ) );              // 6.0.2.5 (2014/10/31) char を append する。
    297                        vals.append( ',' ).append( getValue( row, clm ) );              // 6.0.2.5 (2014/10/31) char を append する。
    298                }
    299
    300                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    301                final String imgsrc ;
    302                final StringBuilder clazz = new StringBuilder( BUFFER_MIDDLE );
    303                clazz.append( "lvlctl unreplaceable" );
    304                if( expandAll ) { // 4.3.3.0 (2008/10/01)
    305                        if( row == getRowCount() - 1
    306                                        || Integer.parseInt( lvlClmVal ) >= Integer.parseInt( getValue( row+1, levelClmPos ) ) ) {                              // 6.4.2.0 (2016/01/29)
    307                                final boolean isExp = expCtrlClmPos > -1 && StringUtil.nval( getValue( row, expCtrlClmPos ), false ) ;
    308                                if( isExp ) {
    309//                                      imgsrc = JSPIMG + imgCollapsed;
    310                                        imgsrc = imgCollapsed;                          // 8.0.3.0 (2021/12/17)
    311                                }
    312                                else {
    313//                                      imgsrc = JSPIMG + imgNoSub;
    314                                        imgsrc = imgNoSub;                                      // 8.0.3.0 (2021/12/17)
    315                                        clazz.append( " fetched nosub" );
    316                                }
    317                        }
    318                        else {
    319//                              imgsrc = JSPIMG + imgExpanded;
    320                                imgsrc = imgExpanded;                                   // 8.0.3.0 (2021/12/17)
    321                                clazz.append( " fetched expanded" );
    322                        }
    323                }
    324                else {
    325//                      imgsrc = JSPIMG + imgCollapsed;
    326                        imgsrc = imgCollapsed;                                          // 8.0.3.0 (2021/12/17)
    327                }
    328
    329                // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
    330                final String tag = new TagBuffer( "img" )                       // 6.1.1.0 (2015/01/17) refactoring. 連結記述
    331                        .add( "class"   , clazz.toString() )
    332                        .add( "src"             , imgsrc )
    333                        .add( "alt"             , "Level " + lvlClmVal )                // 6.4.2.0 (2016/01/29) 共通値を設定する。
    334                        .add( "title"   , "Level " + lvlClmVal )                // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
    335                        .add( "lvl"             , lvlClmVal )                                   // 6.4.2.0 (2016/01/29) 共通値を設定する。
    336                        .add( "keys"    , keys.toString() )
    337                        .add( "vals"    , vals.toString() )
    338                        .makeTag();
    339
    340                return getRendererValue( row, levelClmPos ) + tag;
    341        }
    342
    343        /**
    344         * JavaScriptに渡すためのパラメーターをhiddenタグをして出力します。
    345         *
    346         * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。
    347         *
    348         * @return hiddenタグ
    349         * @og.rtnNotNull
    350         */
    351        private String getParameterTag() {
    352                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    353                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, childSearchJsp ) )
    354//                      .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_COLLAPSED, JSPIMG + imgCollapsed ) )
    355//                      .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_EXPANDED,  JSPIMG + imgExpanded ) )
    356//                      .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_NO_SUB,    JSPIMG + imgNoSub ) );
    357                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_COLLAPSED, imgCollapsed ) )                        // 8.0.3.0 (2021/12/17)
    358                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_EXPANDED,  imgExpanded  ) )                        // 8.0.3.0 (2021/12/17)
    359                        .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_NO_SUB,    imgNoSub     ) );                       // 8.0.3.0 (2021/12/17)
    360                return buf.toString();
    361        }
    362}