    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;
    019import java.util.ArrayList;
    020
    021import org.opengion.hayabusa.common.HybsSystemException;
    022import org.opengion.hayabusa.html.ViewTimeTableParam;
    023
    024/**
    025 * 時間軸を持つタイムテーブルの表示を行うクラスです。
    026 *
    027 * パラメータが必要な場合は、ViewTimeTableParamTag を使用してください。
    028 *
    029 * パラメータが設定されていない場合は、ViewForm_HTMLTimeTable の初期値が使用されます。
    030 * (パラメータを使用するには、viewタグのuseParam 属性をtrueに設定する必要があります。)
    031 *
    032 * SELECT文は、日付、キー、備考、開始時刻、終了時刻、リンクが、必須項目で、この並び順は、
    033 * 完全に固定です。よって、カラム位置を指定する必要はありませんが、SELECT文を自由に
    034 * 設定することも出来ませんので、ご注意ください。
    035 * この固定化に伴い、WRITABLE 指定も使用できません。
    036 * なお、日付、キー、備考 に関しては、columnDisplay 属性で、表示の ON/OFF 制御は可能です。
    037 * また、日付ブレイク、キーブレイクの設定で、カラム自体をテーブルの外に出すことが可能です。
    038 * (キーと備考はセットになっています。)
    039 *
    040 * タイムテーブルが空きの場合のリンクを指定できます。(ViewTimeTableParam.NULL_LINK_CLM_ID)
    041 * (ViewTimeTableParam の nullLinkColumn 属性)
    042 * 指定しない場合は、空きのリンクは作成されません。
    043 * このリンクは、特殊で、引数に、パラメータを追加できますが、"($1)"、"($2)" で指定します。
    044 * この($1)、($2)は、開始時刻、終了時刻がセットされますが、SELECT文の固定カラムと同じ
    045 * 並び順ですが、DBTableModelの値を設定しているわけではありません。
    046 * 空きの場合は、データ自体が存在しない場合と、日付、キー のみが 外部結合で生成された
    047 * レコードが実際に存在する場合がありますが、外部結合で生成されたレコードには、
    048 * 開始時刻、終了時刻はありません。($1) と($2)には、それぞれ、最小開始時刻と最大終了時刻を
    049 * セットします。
    050 *
    051 * 例として、&amp;TMSTART=($1)&amp;TMEND=($2) という文字列の ($*) 部分を解析して割当ます。
    052 *
    053 * ブレーク処理を行うカラムＩＤをCSV形式でセットできます。(ViewTimeTableParam.BREAK_CLMS)
    054 * (ViewTimeTableParam の breakClms 属性)
    055 * これは、ブレイク毎にテーブルが分かれて、テーブルの先頭に、ブレイクした
    056 * 値が表示されます。
    057 * 例えば、日付カラムをブレイクカラムとして設定すると、日付がブレイクするたび、
    058 * 日付をヘッダーに出して、テーブルを作成します。
    059 * ブレークカラムは、CSV形式で複数指定できます。その場合は、複数指定のカラムの
    060 * 合成された値で、キーブレイクの判定を行います。(簡単に言うとＯＲ判定になります。)
    061 * なお、ブレイクカラムを指定した場合は、自動的に、noDisplay 属性にその値をセット
    062 * します。
    063 *
    064 * @og.rev 5.4.0.0 (2011/10/01) 新規追加
    065 * @og.group 画面表示
    066 *
    067 * @version  4.0
    068 * @author       Kazuhiko Hasegawa
    069 * @since    JDK5.0,
    070 */
    071public class ViewForm_HTMLTimeTable extends ViewForm_HTMLTable {
    072        /** このプログラムのVERSION文字列を設定します。   {@value} */
    073        private static final String VERSION = "6.4.2.0 (2016/01/29)" ;
    074
    075        private int             intval          = 30;                   // 30分 単位に colspan を設定する。
    076        private int             minStTime       = 480;                  // 08:00 のこと。8H=480M
    077        private int             maxEdTime       = 1260;                 // 21:00 のこと。21H=1260M
    078
    079        // 6.4.1.1 (2016/01/16) dyClmNo → DY_CLMNO , keyClmNo → KEY_CLMNO , tmstClmNo → TMST_CLMNO , tmedClmNo → TMED_CLMNO , linkClmNo → LINK_CLMNO , clmCnt → CLM_CNT refactoring
    080        private static final int        DY_CLMNO                = 0;    // ヘッダー1：ただし、グループ化する場合は、外出し
    081        private static final int        KEY_CLMNO       = 1;    // ヘッダー2：ただし、グループ化する場合は、外出し
    082        // private static final int     bikoClmNo       = 2;    // ヘッダー3：(内部では使用していません。)
    083
    084        private static final int        TMST_CLMNO      = 3;    // 時間枠の開始時刻(含む)
    085        private static final int        TMED_CLMNO      = 4;    // 時間枠の終了時刻(含まない)
    086        private static final int        LINK_CLMNO      = 5;    // 時間枠に表示する予約情報(変更画面へのリンク)
    087
    088        private static final int        CLM_CNT         = 3;    // 決め打ち。今は、dyClm,keyClm の２つだけ表示
    089
    090        // 引数パース機能付き データが存在しない場合のリンクのベースを設定。
    091        private int             nullLinkClmNo   = -1 ;          // 固定ではなく可変の場合に利用するリンクカラムNo
    092
    093        private int             tdClassColumnNo = -1 ;          // 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
    094
    095        private boolean isDyBreak               = true;         // 日付でブレイクするかどうかを指定
    096        private boolean isBookingMerge  ;                       // 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
    097
    098        /**
    099         * デフォルトコンストラクター
    100         *
    101         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    102         */
    103        public ViewForm_HTMLTimeTable() { super(); }            // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    104
    105        /**
    106         * 内容をクリア(初期化)します。
    107         *
    108         * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
    109         * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
    110         *
    111         */
    112        @Override
    113        public void clear() {
    114                super.clear();
    115                intval          = 30;                   // 30分 単位に colspan を設定する。
    116                minStTime       = 480;                  // 08:00 のこと。8H=480M
    117                maxEdTime       = 1260;                 // 21:00 のこと。21H=1260M
    118
    119                nullLinkClmNo   = -1;                   // 固定ではなく可変の場合に利用するリンクカラム
    120                tdClassColumnNo = -1 ;                  // 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
    121                isDyBreak               = true;                 // 日付でブレイクするかどうかを指定
    122                isBookingMerge  = false;                // 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
    123        }
    124
    125        /**
    126         * DBTableModel から HTML文字列を作成して返します。
    127         * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
    128         * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
    129         *
    130         * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
    131         * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
    132         *
    133         * @param  startNo        表示開始位置
    134         * @param  pageSize   表示件数
    135         *
    136         * @return      DBTableModelから作成された HTML文字列
    137         * @og.rtnNotNull
    138         */
    139        @Override
    140        public String create( final int startNo, final int pageSize )  {
    141                if( getRowCount() == 0 ) { return ""; } // 暫定処置
    142
    143                paramInit();
    144                headerLine       = null;
    145                final int lastNo = getLastNo( startNo, pageSize );
    146                final int hsc = getHeaderSkipCount();
    147                int hscCnt = 1;
    148
    149                final StringBuilder out = new StringBuilder( BUFFER_LARGE );
    150
    151                if( isDyBreak ) {
    152                        out.append( getRendererValue( 0,DY_CLMNO ) ).append( CR );
    153                        setColumnDisplay( DY_CLMNO,false );     // 日付ブレイクなら、setColumnDisplay をfalse にセット
    154                }
    155
    156                out.append( getHeader() )
    157                        .append("<tbody>").append( CR );
    158
    159                int bgClrCnt = 0;
    160
    161                final int maxColspan = (maxEdTime-minStTime)/intval ;           // この数が、TDの数になる。
    162                int rowColspan = 0;                                                                     // 5.5.0.3(2012/03/12) １行の累積TD数。最大は、maxColspan で、
    163                int stTime     = minStTime;
    164
    165                String backData    = "";        // 初期値。１回目にキーブレイクさせる。
    166
    167                final StringBuilder buf2 = new StringBuilder( BUFFER_MIDDLE );  // 6.1.0.0 (2014/12/26) refactoring
    168
    169                final List<String> dblBooking = new ArrayList<>();              // 重複データがあったときのデータ格納
    170                String nlVal     = null;                // 空リンクのベース値
    171                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    172                String bkNlVal   = null;                // キーブレイク時の元の空リンクのベース値
    173                String bkDyVal   = "";                  // キーブレイク時の元の日付
    174                for( int row=startNo; row<lastNo; row++ ) {
    175                        // キーブレイクの判定
    176                        bkNlVal   = nlVal;
    177
    178                        nlVal  = nullLinkClmNo   < 0 ? null : getRendererValue( row,nullLinkClmNo );
    179                        final String tdCls  = tdClassColumnNo < 0 ? null : getValue( row,tdClassColumnNo );             // 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
    180                        final String dyVal  = getValue( row,DY_CLMNO );                                                                                 // 日付項目の値
    181                        final String keyVal = getValue( row,KEY_CLMNO );                                                                                // キー項目の値
    182                        if( row==startNo ) { bkDyVal = dyVal; }         // 初期データをセット。
    183
    184                        String linkVal = getRendererValue( row,LINK_CLMNO );
    185
    186                        // キーブレイク判定。キーブレイクは、一番初めから来る。
    187                        if( !backData.equals( dyVal + keyVal ) ) {
    188                                backData = dyVal + keyVal;                      // null は来ないはず
    189
    190                                // minStTime < stTime の時だけ、処理を行う。(最初のキーブレイクは処理しないため)
    191                                if( minStTime < stTime ) {
    192                                        // まずは、前の td の残りを出力する。ここでは、キーブレイク前の値を使用する。
    193                                        if( stTime < maxEdTime ) {
    194                                                out.append("  ");               // td タグの出力前の段落
    195                                                // 残データの書き出しは、最大TD数-それまでにセットした数。
    196                                                final int td = maxColspan - rowColspan;         // 5.5.0.3(2012/03/12)
    197                                                appendTDTag( out , null , td ,  // 5.5.0.3(2012/03/12)
    198                                                                                makeLinkValue( bkNlVal , stTime , maxEdTime ) );
    199                                        }
    200                                        out.append("</tr>").append( CR );
    201                                }
    202                                stTime = minStTime;             // 初期化
    203
    204                                // データかぶりが発生したときの処理
    205                                if( !dblBooking.isEmpty() ) {
    206                                        for( final String bkdt : dblBooking ) {
    207                                                // 6.0.2.5 (2014/10/31) char を append する。
    208                                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append('>').append( CR )
    209                                                        .append( bkdt )
    210                                                        .append("</tr>").append( CR );
    211                                        }
    212                                        dblBooking.clear();
    213                                }
    214
    215                                // 日付ブレイク処理
    216                                if( isDyBreak && row > startNo && !bkDyVal.equals( dyVal ) ) {
    217                                        bkDyVal = dyVal;
    218                                        out.append(" <tr class=\"dummy\">");
    219                                        for( int column=0; column<CLM_CNT; column++ ) {
    220                                                if( isColumnDisplay( column ) ) {
    221                                                        out.append("<td/>");
    222                                                }
    223                                        }
    224                                        for( int i=0; i<maxColspan ; i++ ) {            // 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
    225                                                out.append("<td/>");
    226                                        }
    227                                        out.append("</tr>").append( CR )
    228                                                .append("</tbody>").append( CR )
    229                                                .append("</table>").append( CR )
    230                                                .append( getRendererValue( row,DY_CLMNO ) ).append( CR )
    231                                                .append( getHeader() )
    232                                                .append("<tbody>").append( CR );
    233                                        hscCnt = 1;
    234                                }
    235
    236                                // ヘッダー繰り返し属性( headerSkipCount )を採用
    237                                if( hsc > 0 && hscCnt % hsc == 0 ) {
    238                                        out.append( getHeadLine() );
    239                                        hscCnt = 1;
    240                                }
    241                                else {
    242                                        // 特殊処理：ここの処理では、一番最初も、実行されるので、＋＋しないように加工しておく。
    243                                        if( row > startNo ) { hscCnt ++ ; }
    244                                }
    245
    246                                // ここから、新しい行が始まる。
    247                                // 6.0.2.5 (2014/10/31) char を append する。
    248                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt++ ) ).append('>').append( CR );
    249                                rowColspan = 0 ;                // 5.5.0.3(2012/03/12) 初期化
    250
    251                                for( int column=0; column<CLM_CNT; column++ ) {
    252                                        if( isColumnDisplay( column ) ) {
    253                                                // ヘッダー部分に加工しやすいように、class 属性を与えておく。
    254                                                out.append("  <td class=\"" ).append( getColumnName( column ) ).append( "\">" )
    255                                                        .append( getValueLabel(row,column) )
    256                                                        .append("</td>").append( CR );
    257                                        }
    258                                }
    259                        }
    260
    261                        // 文字列型の時分情報を数字に変換する。
    262                        int clStTime = getStr2Time( getValue( row,TMST_CLMNO ) , -1 );
    263                        final boolean nullData = clStTime < 0 ;                                         // 開始時刻が        null の場合、-1 が返されるのでフラグをセットする。
    264                        if( clStTime < minStTime ) { clStTime = minStTime; }    // 最小値以下の場合は、最小値に合せる。
    265
    266                        int clEdTime = getStr2Time( getValue( row,TMED_CLMNO ) , maxEdTime );
    267                        if( clEdTime > maxEdTime ) { clEdTime = maxEdTime; }    // 最大値以上の場合は、最大値に合せる。
    268
    269                        if( clStTime == clEdTime ) { clEdTime = clEdTime + intval ; }   // 最初と最後が同じ場合は、intval分 進めておく。
    270
    271                        // 最初と最後が異なる場合は、間に空欄が入る。同じ場合は、連続なので、空欄は入らない。
    272                        if( stTime < clStTime ) {
    273                                out.append("  ");               // td タグの出力前の段落
    274
    275                                // 5.5.0.3(2012/03/12) 間に空欄が入る場合、その大きさが最小TD単位より大きければ、分割する。
    276                                // ただし、直前の TD 個数が、最小でない場合のみ。
    277                                final int td = (clStTime-stTime)/intval;                // 5.5.0.3(2012/03/12)
    278                                rowColspan += td;
    279                                appendTDTag( out , null , td ,          // 5.5.0.3(2012/03/12)
    280                                                                                makeLinkValue( nlVal , stTime , clStTime ) ).append( CR );
    281                        }
    282                        // 前のデータとかぶっている。つまり、ブッキングデータがある。
    283                        else if( stTime > clStTime ) {
    284                                // 5.4.4.2 (2012/02/03) ブッキングデータをマージする機能を追加
    285                                if( isBookingMerge ) {
    286                                        if( stTime < clEdTime ) {
    287                                                final int td = (clEdTime-stTime)/intval;                // 5.5.0.3(2012/03/12)
    288                                                rowColspan += td ;
    289                                                appendTDTag( out , tdCls , td , linkVal ).append( CR );
    290                                                stTime = clEdTime;
    291                                        }
    292                                        continue;
    293                                }
    294
    295                                buf2.setLength(0);              // 6.1.0.0 (2014/12/26) refactoring
    296                                buf2.append("  ");              // td タグの出力前の段落
    297                                for( int column=0; column<CLM_CNT; column++ ) {
    298                                        if( isColumnDisplay( column ) ) {
    299                                                // ヘッダー部分に加工しやすいように、class 属性を与えておく。
    300                                                buf2.append("<td class=\"" ).append( getColumnName( column ) ).append( "\"/>" );
    301                                        }
    302                                }
    303
    304                                // 5.4.3.7 (2012/01/20)
    305                                appendTDTag( buf2 , null  , (clStTime-minStTime)/intval );                              // 最初からデータまで
    306                                appendTDTag( buf2 , tdCls , (clEdTime-clStTime)/intval , linkVal );             // データ部: 5.4.3.7 (2012/01/20) td に class属性追加
    307                                appendTDTag( buf2 , null  , (maxEdTime-clEdTime)/intval );                              // データから最後まで
    308
    309                                dblBooking.add( buf2.toString() );
    310                                continue;
    311                        }
    312                        // 前も後ろも最小と最大になっているのは、予約レコードが無いため。
    313                        // stTime == clStTime のケース。nullData = true で、予約レコードが無いため。
    314                        else if( nullData ) {
    315                                linkVal = makeLinkValue( nlVal , minStTime , maxEdTime );
    316                        }
    317                        // 5.4.3.7 (2012/01/20) linkVal を共通に使用している箇所を修正
    318
    319                        out.append("  ");               // td タグの出力前の段落
    320                        final int td = (clEdTime-clStTime)/intval;              // 5.5.0.3(2012/03/12)
    321                        rowColspan += td ;
    322                        appendTDTag( out , tdCls , td , linkVal ).append( CR ); // 5.5.0.3(2012/03/12)
    323
    324                        stTime = clEdTime ;
    325                }
    326
    327                // 残処理：データが残っている場合は、書き出す必要がある。
    328                if( minStTime < stTime && stTime < maxEdTime ) {
    329                        out.append("  ");               // td タグの出力前の段落
    330                        // 残データの書き出しは、最大TD数-それまでにセットした数。
    331                        final int td = maxColspan - rowColspan;         // 5.5.0.3(2012/03/12)
    332                        appendTDTag( out , null , td ,          // 5.5.0.3(2012/03/12)
    333                                                                                makeLinkValue( nlVal , stTime , maxEdTime ) );
    334                }
    335                out.append("</tr>").append( CR );
    336
    337                // データかぶりが発生したときの処理
    338                if( !dblBooking.isEmpty() ) {
    339                        for( final String bkdt : dblBooking ) {
    340                                // 6.0.2.5 (2014/10/31) char を append する。
    341                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append('>').append( CR )
    342                                        .append( bkdt )
    343                                        .append("</tr>").append( CR );
    344                        }
    345                }
    346
    347                // カラムの結合があるため、td タグを個別に出力しておかないと、レイアウトがずれる。
    348                out.append(" <tr class=\"dummy\">");
    349                for( int column=0; column<CLM_CNT; column++ ) {
    350                        if( isColumnDisplay( column ) ) {
    351                                out.append("<td/>");
    352                        }
    353                }
    354                for( int i=0; i<maxColspan ; i++ ) {            // 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
    355                        out.append("<td/>");
    356                }
    357                out.append("</tr>").append( CR )
    358                        .append("</tbody>").append( CR )
    359                        .append("</table>").append( CR )
    360                        .append( getScrollBarEndDiv() );        // 3.8.0.3 (2005/07/15)
    361                return out.toString();
    362        }
    363
    364        /**
    365         * パラメータ内容を初期化します。
    366         *
    367         * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加。intval の実値化
    368         * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
    369         *
    370         */
    371        private void paramInit() {
    372                final String s_intval           = getParam( ViewTimeTableParam.TIME_INTERVAL    , null );
    373                final String s_minStTime        = getParam( ViewTimeTableParam.MIN_START_TIME   , null );
    374                final String s_maxEdTime        = getParam( ViewTimeTableParam.MAX_END_TIME             , null );
    375
    376                isDyBreak               = Boolean.valueOf( getParam( ViewTimeTableParam.USE_DY_BREAK, "true" ) );
    377
    378                // 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
    379                isBookingMerge  = Boolean.valueOf( getParam( ViewTimeTableParam.USE_BOOKING_MERGE, "false" ) );
    380
    381                // nullリンクのカラム名指定。nullClm が優先順位が高い。
    382                final String s_nullClm  = getParam( ViewTimeTableParam.NULL_LINK_CLM_ID , null );
    383                if( s_nullClm != null ) {
    384                        nullLinkClmNo = getColumnNo( s_nullClm );
    385                }
    386
    387                // 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
    388                final String s_tdClsClm = getParam( ViewTimeTableParam.TD_CLASS_COLUMN_ID       , null );
    389                if( s_tdClsClm != null ) {
    390                        tdClassColumnNo = getColumnNo( s_tdClsClm );
    391                }
    392
    393                if( s_intval != null ) {
    394                        intval = Integer.parseInt( s_intval ) ; // 5.4.3.7 (2012/01/20)
    395                }
    396                minStTime       = getStr2Time( s_minStTime      , minStTime );                  // 最小開始時刻。0800 なら、80。 30分=5 換算
    397                maxEdTime       = getStr2Time( s_maxEdTime      , maxEdTime );                  // 最大終了時刻。2100 なら、210。30分=5 換算
    398        }
    399
    400        /**
    401         * DBTableModel から テーブルのタグ文字列を作成して返します。
    402         *
    403         * @og.rev 5.4.3.7 (2012/01/20) colgroup は不要
    404         *
    405         * @return      テーブルのタグ文字列
    406         * @og.rtnNotNull
    407         */
    408        @Override
    409        protected String getTableHead() {
    410                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    411                        .append("<thead id=\"header\">").append( CR )
    412                        .append( getHeadLine() )
    413                        .append("</thead>").append( CR );
    414
    415                return buf.toString();
    416        }
    417
    418        /**
    419         * ヘッダー繰り返し部を、getTableHead()メソッドから分離。
    420         *
    421         * @og.rev 6.1.2.0 (2015/01/24) キャッシュを返すのを、#getHeadLine() に移動。
    422         *
    423         * @param       thTag タグの文字列
    424         *
    425         * @return      テーブルのタグ文字列
    426         * @og.rtnNotNull
    427         */
    428        @Override
    429        protected String getHeadLine( final String thTag ) {
    430
    431                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    432                        .append( "<tr class=\"row_h\" >" ).append( CR );
    433
    434                for( int column=0; column<CLM_CNT; column++ ) {
    435                        if( isColumnDisplay( column ) ) {
    436                                buf.append( thTag )
    437                                        .append(" class=\"" ).append( getColumnName( column ) ).append( "\">" )
    438                                        .append( getColumnLabel(column) )
    439                                        .append("</th>").append( CR );
    440                        }
    441                }
    442
    443                final String thTagTmp = thTag + " class=\"timeVar\" colspan=\"" + 60/intval + "\">" ;
    444
    445                for( int tm=minStTime; tm<maxEdTime; tm+=60 ) { // ヘッダーは、１時間単位とする。
    446                        buf.append( thTagTmp ).append( tm/60 ).append("</th>").append( CR );
    447                }
    448
    449                buf.append("</tr>").append( CR );
    450
    451                return buf.toString();                          // 6.1.2.0 (2015/01/24)
    452        }
    453
    454        /**
    455         * TDタグ文字列を簡易的に合成します。
    456         *
    457         * ここでは、主に、class 属性、colspan 属性を設定することを目的にしています。
    458         * colspan の値によって、動作を変化させています。
    459         *   0: タグそのものを生成しません。これは、第一引数をそのまま返します。
    460         *   1: colspan 属性を出力しません。(Default値なので)
    461         *   それ以外は、colspan に引数を設定します。
    462         * BODY 部も、無指定の場合は、/&gt; 止めのタグを出力します。
    463         *
    464         * 返り値の StringBuilder は、第一引数そのものを返します。よって、このメソッドに、
    465         * append を連結していくことも可能です。
    466         * (return値を使わなくても、第一引数の StringBuilder は変化しています。副作用というやつ。)
    467         *
    468         * @og.rev 5.4.3.7 (2012/01/20) tdタグ専用に変更。
    469         *
    470         * @param       buf             このStringBuilderに append します。
    471         * @param       cls             追加する class 属性
    472         * @param       colspan td , th タグ属性に追加する colspan値。
    473         *                     0 の場合は、タグ自体を出力しません。
    474         *                     1 の場合は、colspan を出力しません。
    475         * @param       body    タグの BODY 部に出力する内容(String可変長引数)０件の場合は、BODYなし
    476         *
    477         * @return      appendされたStringBuilder(第一引数と同じオブジェクト)
    478         * @og.rtnNotNull
    479         */
    480        private StringBuilder appendTDTag( final StringBuilder buf , final String cls , final int colspan , final String... body ) {
    481                // 6.3.9.1 (2015/11/27) A method should have only one exit point, and that should be the last statement in the method.(PMD)
    482                if( colspan != 0 ) {
    483                        buf.append( "<td" );
    484                        // class 属性の追加
    485                        if( cls != null && !cls.isEmpty() ) {
    486                                buf.append( " class=\"" ).append( cls ).append( '"' );                  // 6.0.2.5 (2014/10/31) char を append する。
    487                        }
    488
    489                        // colspan 属性の追加
    490                        if( colspan > 1 ) {
    491                                buf.append( " colspan=\"" ).append( colspan ).append( '"' );    // 6.0.2.5 (2014/10/31) char を append する。
    492                        }
    493
    494                        if( body == null || body.length == 0 ) {                // 6.3.9.1 (2015/11/27) 可変長引数でもnullは来る。
    495                                buf.append( " />" );
    496                        }
    497                        else {
    498                                buf.append( '>' );                                                      // 6.0.2.5 (2014/10/31) char を append する。
    499                                for( final String bd : body ) {
    500                                        buf.append( bd ).append( ' ' );                 // 6.3.9.1 (2015/11/27) スペースで連結追加
    501                                }
    502                                buf.append( "</td>" );
    503                        }
    504                }
    505
    506                return buf;
    507        }
    508
    509        /**
    510         * 時間文字列を数字に変換します。
    511         *
    512         * "0800" は、480に、"2100" は、1260 に変換します。
    513         *
    514         * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
    515         *
    516         * @param       val     時間文字列の値(0800 など)
    517         * @param       defTm   引数の時間文字列が null の場合の初期値
    518         *
    519         * @return      時間文字列を数字に変換した結果( 80 など)
    520         */
    521        private int getStr2Time( final String val , final int defTm ) {
    522                // 6.3.9.1 (2015/11/27) A method should have only one exit point, and that should be the last statement in the method.(PMD)
    523                final int rtn ;
    524                if( val == null || val.isEmpty() ) { rtn = defTm; }
    525                else if( val.length() == 4 ) {
    526                        rtn = Integer.parseInt( val.substring( 0,2 ) )*60 +
    527                                        Integer.parseInt( val.substring( 2 ) ) ;        // 5.4.3.7 (2012/01/20)
    528                }
    529                else {
    530                        final String errMsg = "時間引数は、４桁必要です。" +
    531                                                        "  val=[" + val + "]";
    532                        throw new HybsSystemException( errMsg );
    533                }
    534
    535                return rtn ;
    536        }
    537
    538        /**
    539         * 数字を時間文字列に変換します。
    540         *
    541         * 480は、"0800" に、"1260"は、2100 に変換します。
    542         *
    543         * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
    544         *
    545         * @param       timeVal 引数の時間文字列が null の場合の初期値
    546         *
    547         * @return      数字を時間文字列に変換した結果( "0800" など)
    548         * @og.rtnNotNull
    549         */
    550        private String getInt2StrTime( final int timeVal ) {
    551                int tmpVal = timeVal;
    552                if(      tmpVal < minStTime ) { tmpVal = minStTime; }   // 最小値以下の場合は、最小値に合せる
    553                else if( tmpVal > maxEdTime ) { tmpVal = maxEdTime; }   // 最大値以上の場合は、最大値に合せる。
    554
    555                return String.valueOf(100+ tmpVal/60).substring(1) + String.valueOf(100+tmpVal%60).substring(1);
    556        }
    557
    558        // 引数に、 を追加する。
    559
    560        /**
    561         * リンク文字列をパースします。
    562         *
    563         * データの空欄にリンクを作成するときに、元となるリンク文字列の引数を設定します。
    564         * 引数は、&TMSTART=(stTime)&TMEND=(edTime) を追加するイメージです。
    565         * stTime、edTime は、それぞれ、($1)、($2) の変数が割り当てられます。
    566         * stTime、edTime は、#getInt2StrTime( int ) メソッドで変換した文字列を利用します。
    567         *
    568         * @param       lnkVal  リンクのベースとなる値
    569         * @param       stTime  開始時刻の数字表記
    570         * @param       edTime  終了時刻の数字表記
    571         *
    572         * @return      リンク文字列をパースした結果
    573         */
    574        private String makeLinkValue( final String lnkVal,final int stTime,final int edTime ) {
    575                // 6.3.9.0 (2015/11/06) Found 'DD'-anomaly for variable(PMD)
    576                return lnkVal == null ? ""
    577                                                          : lnkVal.replace( "%28%241%29",getInt2StrTime( stTime ) )
    578                                                                                .replace( "%28%242%29",getInt2StrTime( edTime ) ) ;
    579        }
    580
    581        /**
    582         * 表示項目の編集(並び替え)が可能かどうかを返します。
    583         *
    584         * @return      表示項目の編集(並び替え)が可能かどうか(false:不可能)
    585         */
    586        @Override
    587        public boolean isEditable() {
    588                return false;
    589        }
    590}