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 * 例として、&TMSTART=($1)&TMEND=($2) という文字列の ($*) 部分を解析して割当ます。
052 *
053 * ブレーク処理を行うカラムIDをCSV形式でセットできます。(ViewTimeTableParam.BREAK_CLMS)
054 * (ViewTimeTableParam の breakClms 属性)
055 * これは、ブレイク毎にテーブルが分かれて、テーブルの先頭に、ブレイクした
056 * 値が表示されます。
057 * 例えば、日付カラムをブレイクカラムとして設定すると、日付がブレイクするたび、
058 * 日付をヘッダーに出して、テーブルを作成します。
059 * ブレークカラムは、CSV形式で複数指定できます。その場合は、複数指定のカラムの
060 * 合成された値で、キーブレイクの判定を行います。(簡単に言うとOR判定になります。)
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 = "7.0.1.0 (2018/10/15)" ;
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 の2つだけ表示
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         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
133         *
134         * @param  startNo        表示開始位置
135         * @param  pageSize   表示件数
136         *
137         * @return      DBTableModelから作成された HTML文字列
138         * @og.rtnNotNull
139         */
140        @Override
141        public String create( final int startNo, final int pageSize )  {
142                if( getRowCount() == 0 ) { return ""; } // 暫定処置
143
144                paramInit();
145                headerLine       = null;
146                final int lastNo = getLastNo( startNo, pageSize );
147                final int hsc = getHeaderSkipCount();
148                int hscCnt = 1;
149
150                final StringBuilder out = new StringBuilder( BUFFER_LARGE );
151
152                if( isDyBreak ) {
153                        out.append( getRendererValue( 0,DY_CLMNO ) ).append( CR );
154                        setColumnDisplay( DY_CLMNO,false );     // 日付ブレイクなら、setColumnDisplay をfalse にセット
155                }
156
157                out.append( getHeader() )
158                        .append("<tbody>").append( CR );
159
160                int bgClrCnt = 0;
161
162                final int maxColspan = (maxEdTime-minStTime)/intval ;           // この数が、TDの数になる。
163                int rowColspan = 0;                                                                     // 5.5.0.3(2012/03/12) 1行の累積TD数。最大は、maxColspan で、
164                int stTime     = minStTime;
165
166                String backData    = "";        // 初期値。1回目にキーブレイクさせる。
167
168                final StringBuilder buf2 = new StringBuilder( BUFFER_MIDDLE );  // 6.1.0.0 (2014/12/26) refactoring
169
170                final List<String> dblBooking = new ArrayList<>();              // 重複データがあったときのデータ格納
171                String nlVal     = null;                // 空リンクのベース値
172                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
173                String bkNlVal   = null;                // キーブレイク時の元の空リンクのベース値
174                String bkDyVal   = "";                  // キーブレイク時の元の日付
175                for( int row=startNo; row<lastNo; row++ ) {
176                        // キーブレイクの判定
177                        bkNlVal   = nlVal;
178
179                        nlVal  = nullLinkClmNo   < 0 ? null : getRendererValue( row,nullLinkClmNo );
180                        final String tdCls  = tdClassColumnNo < 0 ? null : getValue( row,tdClassColumnNo );             // 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
181                        final String dyVal  = getValue( row,DY_CLMNO );                                                                                 // 日付項目の値
182                        final String keyVal = getValue( row,KEY_CLMNO );                                                                                // キー項目の値
183                        if( row==startNo ) { bkDyVal = dyVal; }         // 初期データをセット。
184
185                        String linkVal = getRendererValue( row,LINK_CLMNO );
186
187                        // キーブレイク判定。キーブレイクは、一番初めから来る。
188                        if( !backData.equals( dyVal + keyVal ) ) {
189                                backData = dyVal + keyVal;                      // null は来ないはず
190
191                                // minStTime < stTime の時だけ、処理を行う。(最初のキーブレイクは処理しないため)
192                                if( minStTime < stTime ) {
193                                        // まずは、前の td の残りを出力する。ここでは、キーブレイク前の値を使用する。
194                                        if( stTime < maxEdTime ) {
195                                                out.append("  ");               // td タグの出力前の段落
196                                                // 残データの書き出しは、最大TD数-それまでにセットした数。
197                                                final int td = maxColspan - rowColspan;         // 5.5.0.3(2012/03/12)
198                                                appendTDTag( out , null , td ,  // 5.5.0.3(2012/03/12)
199                                                                                makeLinkValue( bkNlVal , stTime , maxEdTime ) );
200                                        }
201                                        out.append("</tr>").append( CR );
202                                }
203                                stTime = minStTime;             // 初期化
204
205                                // データかぶりが発生したときの処理
206                                if( !dblBooking.isEmpty() ) {
207                                        for( final String bkdt : dblBooking ) {
208                                                // 6.0.2.5 (2014/10/31) char を append する。
209                                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append('>').append( CR )
210                                                        .append( bkdt )
211                                                        .append("</tr>").append( CR );
212                                        }
213                                        dblBooking.clear();
214                                }
215
216                                // 日付ブレイク処理
217                                if( isDyBreak && row > startNo && !bkDyVal.equals( dyVal ) ) {
218                                        bkDyVal = dyVal;
219                                        out.append(" <tr class=\"dummy\">");
220                                        for( int column=0; column<CLM_CNT; column++ ) {
221                                                if( isColumnDisplay( column ) ) {
222//                                                      out.append("<td/>");
223                                                        out.append("<td></td>");                        // 7.0.1.0 (2018/10/15)
224                                                }
225                                        }
226                                        for( int i=0; i<maxColspan ; i++ ) {            // 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
227//                                              out.append("<td/>");
228                                                out.append("<td></td>");                                // 7.0.1.0 (2018/10/15)
229                                        }
230                                        out.append("</tr>").append( CR )
231                                                .append("</tbody>").append( CR )
232                                                .append("</table>").append( CR )
233                                                .append( getRendererValue( row,DY_CLMNO ) ).append( CR )
234                                                .append( getHeader() )
235                                                .append("<tbody>").append( CR );
236                                        hscCnt = 1;
237                                }
238
239                                // ヘッダー繰り返し属性( headerSkipCount )を採用
240                                if( hsc > 0 && hscCnt % hsc == 0 ) {
241                                        out.append( getHeadLine() );
242                                        hscCnt = 1;
243                                }
244                                else {
245                                        // 特殊処理:ここの処理では、一番最初も、実行されるので、++しないように加工しておく。
246                                        if( row > startNo ) { hscCnt ++ ; }
247                                }
248
249                                // ここから、新しい行が始まる。
250                                // 6.0.2.5 (2014/10/31) char を append する。
251                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt++ ) ).append('>').append( CR );
252                                rowColspan = 0 ;                // 5.5.0.3(2012/03/12) 初期化
253
254                                for( int column=0; column<CLM_CNT; column++ ) {
255                                        if( isColumnDisplay( column ) ) {
256                                                // ヘッダー部分に加工しやすいように、class 属性を与えておく。
257                                                out.append("  <td class=\"" ).append( getColumnName( column ) ).append( "\">" )
258                                                        .append( getValueLabel(row,column) )
259                                                        .append("</td>").append( CR );
260                                        }
261                                }
262                        }
263
264                        // 文字列型の時分情報を数字に変換する。
265                        int clStTime = getStr2Time( getValue( row,TMST_CLMNO ) , -1 );
266                        final boolean nullData = clStTime < 0 ;                                         // 開始時刻が        null の場合、-1 が返されるのでフラグをセットする。
267                        if( clStTime < minStTime ) { clStTime = minStTime; }    // 最小値以下の場合は、最小値に合せる。
268
269                        int clEdTime = getStr2Time( getValue( row,TMED_CLMNO ) , maxEdTime );
270                        if( clEdTime > maxEdTime ) { clEdTime = maxEdTime; }    // 最大値以上の場合は、最大値に合せる。
271
272                        if( clStTime == clEdTime ) { clEdTime = clEdTime + intval ; }   // 最初と最後が同じ場合は、intval分 進めておく。
273
274                        // 最初と最後が異なる場合は、間に空欄が入る。同じ場合は、連続なので、空欄は入らない。
275                        if( stTime < clStTime ) {
276                                out.append("  ");               // td タグの出力前の段落
277
278                                // 5.5.0.3(2012/03/12) 間に空欄が入る場合、その大きさが最小TD単位より大きければ、分割する。
279                                // ただし、直前の TD 個数が、最小でない場合のみ。
280                                final int td = (clStTime-stTime)/intval;                // 5.5.0.3(2012/03/12)
281                                rowColspan += td;
282                                appendTDTag( out , null , td ,          // 5.5.0.3(2012/03/12)
283                                                                                makeLinkValue( nlVal , stTime , clStTime ) ).append( CR );
284                        }
285                        // 前のデータとかぶっている。つまり、ブッキングデータがある。
286                        else if( stTime > clStTime ) {
287                                // 5.4.4.2 (2012/02/03) ブッキングデータをマージする機能を追加
288                                if( isBookingMerge ) {
289                                        if( stTime < clEdTime ) {
290                                                final int td = (clEdTime-stTime)/intval;                // 5.5.0.3(2012/03/12)
291                                                rowColspan += td ;
292                                                appendTDTag( out , tdCls , td , linkVal ).append( CR );
293                                                stTime = clEdTime;
294                                        }
295                                        continue;
296                                }
297
298                                buf2.setLength(0);              // 6.1.0.0 (2014/12/26) refactoring
299                                buf2.append("  ");              // td タグの出力前の段落
300                                for( int column=0; column<CLM_CNT; column++ ) {
301                                        if( isColumnDisplay( column ) ) {
302                                                // ヘッダー部分に加工しやすいように、class 属性を与えておく。
303//                                              buf2.append("<td class=\"" ).append( getColumnName( column ) ).append( "\"/>" );
304                                                buf2.append("<td class=\"" ).append( getColumnName( column ) ).append( "\"></td>" );    // 7.0.1.0 (2018/10/15)
305                                        }
306                                }
307
308                                // 5.4.3.7 (2012/01/20)
309                                appendTDTag( buf2 , null  , (clStTime-minStTime)/intval );                              // 最初からデータまで
310                                appendTDTag( buf2 , tdCls , (clEdTime-clStTime)/intval , linkVal );             // データ部: 5.4.3.7 (2012/01/20) td に class属性追加
311                                appendTDTag( buf2 , null  , (maxEdTime-clEdTime)/intval );                              // データから最後まで
312
313                                dblBooking.add( buf2.toString() );
314                                continue;
315                        }
316                        // 前も後ろも最小と最大になっているのは、予約レコードが無いため。
317                        // stTime == clStTime のケース。nullData = true で、予約レコードが無いため。
318                        else if( nullData ) {
319                                linkVal = makeLinkValue( nlVal , minStTime , maxEdTime );
320                        }
321                        // 5.4.3.7 (2012/01/20) linkVal を共通に使用している箇所を修正
322
323                        out.append("  ");               // td タグの出力前の段落
324                        final int td = (clEdTime-clStTime)/intval;              // 5.5.0.3(2012/03/12)
325                        rowColspan += td ;
326                        appendTDTag( out , tdCls , td , linkVal ).append( CR ); // 5.5.0.3(2012/03/12)
327
328                        stTime = clEdTime ;
329                }
330
331                // 残処理:データが残っている場合は、書き出す必要がある。
332                if( minStTime < stTime && stTime < maxEdTime ) {
333                        out.append("  ");               // td タグの出力前の段落
334                        // 残データの書き出しは、最大TD数-それまでにセットした数。
335                        final int td = maxColspan - rowColspan;         // 5.5.0.3(2012/03/12)
336                        appendTDTag( out , null , td ,          // 5.5.0.3(2012/03/12)
337                                                                                makeLinkValue( nlVal , stTime , maxEdTime ) );
338                }
339                out.append("</tr>").append( CR );
340
341                // データかぶりが発生したときの処理
342                if( !dblBooking.isEmpty() ) {
343                        for( final String bkdt : dblBooking ) {
344                                // 6.0.2.5 (2014/10/31) char を append する。
345                                out.append(" <tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append('>').append( CR )
346                                        .append( bkdt )
347                                        .append("</tr>").append( CR );
348                        }
349                }
350
351                // カラムの結合があるため、td タグを個別に出力しておかないと、レイアウトがずれる。
352                out.append(" <tr class=\"dummy\">");
353                for( int column=0; column<CLM_CNT; column++ ) {
354                        if( isColumnDisplay( column ) ) {
355//                              out.append("<td/>");
356                                out.append("<td></td>");                        // 7.0.1.0 (2018/10/15)
357                        }
358                }
359                for( int i=0; i<maxColspan ; i++ ) {            // 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
360//                      out.append("<td/>");
361                        out.append("<td></td>");                                // 7.0.1.0 (2018/10/15)
362                }
363                out.append("</tr>").append( CR )
364                        .append("</tbody>").append( CR )
365                        .append("</table>").append( CR )
366                        .append( getScrollBarEndDiv() );                // 3.8.0.3 (2005/07/15)
367                return out.toString();
368        }
369
370        /**
371         * パラメータ内容を初期化します。
372         *
373         * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加。intval の実値化
374         * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
375         *
376         */
377        private void paramInit() {
378                final String s_intval           = getParam( ViewTimeTableParam.TIME_INTERVAL    , null );
379                final String s_minStTime        = getParam( ViewTimeTableParam.MIN_START_TIME   , null );
380                final String s_maxEdTime        = getParam( ViewTimeTableParam.MAX_END_TIME             , null );
381
382                isDyBreak               = Boolean.valueOf( getParam( ViewTimeTableParam.USE_DY_BREAK, "true" ) );
383
384                // 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
385                isBookingMerge  = Boolean.valueOf( getParam( ViewTimeTableParam.USE_BOOKING_MERGE, "false" ) );
386
387                // nullリンクのカラム名指定。nullClm が優先順位が高い。
388                final String s_nullClm  = getParam( ViewTimeTableParam.NULL_LINK_CLM_ID , null );
389                if( s_nullClm != null ) {
390                        nullLinkClmNo = getColumnNo( s_nullClm );
391                }
392
393                // 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
394                final String s_tdClsClm = getParam( ViewTimeTableParam.TD_CLASS_COLUMN_ID       , null );
395                if( s_tdClsClm != null ) {
396                        tdClassColumnNo = getColumnNo( s_tdClsClm );
397                }
398
399                if( s_intval != null ) {
400                        intval = Integer.parseInt( s_intval ) ; // 5.4.3.7 (2012/01/20)
401                }
402                minStTime       = getStr2Time( s_minStTime      , minStTime );                  // 最小開始時刻。0800 なら、80。 30分=5 換算
403                maxEdTime       = getStr2Time( s_maxEdTime      , maxEdTime );                  // 最大終了時刻。2100 なら、210。30分=5 換算
404        }
405
406        /**
407         * DBTableModel から テーブルのタグ文字列を作成して返します。
408         *
409         * @og.rev 5.4.3.7 (2012/01/20) colgroup は不要
410         *
411         * @return      テーブルのタグ文字列
412         * @og.rtnNotNull
413         */
414        @Override
415        protected String getTableHead() {
416                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
417                        .append("<thead id=\"header\">").append( CR )
418                        .append( getHeadLine() )
419                        .append("</thead>").append( CR );
420
421                return buf.toString();
422        }
423
424        /**
425         * ヘッダー繰り返し部を、getTableHead()メソッドから分離。
426         *
427         * @og.rev 6.1.2.0 (2015/01/24) キャッシュを返すのを、#getHeadLine() に移動。
428         *
429         * @param       thTag タグの文字列
430         *
431         * @return      テーブルのタグ文字列
432         * @og.rtnNotNull
433         */
434        @Override
435        protected String getHeadLine( final String thTag ) {
436
437                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
438                        .append( "<tr class=\"row_h\" >" ).append( CR );
439
440                for( int column=0; column<CLM_CNT; column++ ) {
441                        if( isColumnDisplay( column ) ) {
442                                buf.append( thTag )
443                                        .append(" class=\"" ).append( getColumnName( column ) ).append( "\">" )
444                                        .append( getColumnLabel(column) )
445                                        .append("</th>").append( CR );
446                        }
447                }
448
449                final String thTagTmp = thTag + " class=\"timeVar\" colspan=\"" + 60/intval + "\">" ;
450
451                for( int tm=minStTime; tm<maxEdTime; tm+=60 ) { // ヘッダーは、1時間単位とする。
452                        buf.append( thTagTmp ).append( tm/60 ).append("</th>").append( CR );
453                }
454
455                buf.append("</tr>").append( CR );
456
457                return buf.toString();                          // 6.1.2.0 (2015/01/24)
458        }
459
460        /**
461         * TDタグ文字列を簡易的に合成します。
462         *
463         * ここでは、主に、class 属性、colspan 属性を設定することを目的にしています。
464         * colspan の値によって、動作を変化させています。
465         *   0: タグそのものを生成しません。これは、第一引数をそのまま返します。
466         *   1: colspan 属性を出力しません。(Default値なので)
467         *   それ以外は、colspan に引数を設定します。
468         * BODY 部も、無指定の場合は、/&gt; 止めのタグを出力します。
469         *
470         * 返り値の StringBuilder は、第一引数そのものを返します。よって、このメソッドに、
471         * append を連結していくことも可能です。
472         * (return値を使わなくても、第一引数の StringBuilder は変化しています。副作用というやつ。)
473         *
474         * @og.rev 5.4.3.7 (2012/01/20) tdタグ専用に変更。
475         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
476         *
477         * @param       buf             このStringBuilderに append します。
478         * @param       cls             追加する class 属性
479         * @param       colspan td , th タグ属性に追加する colspan値。
480         *                     0 の場合は、タグ自体を出力しません。
481         *                     1 の場合は、colspan を出力しません。
482         * @param       body    タグの BODY 部に出力する内容(String可変長引数)0件の場合は、BODYなし
483         *
484         * @return      appendされたStringBuilder(第一引数と同じオブジェクト)
485         * @og.rtnNotNull
486         */
487        private StringBuilder appendTDTag( final StringBuilder buf , final String cls , final int colspan , final String... body ) {
488                // 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)
489                if( colspan != 0 ) {
490                        buf.append( "<td" );
491                        // class 属性の追加
492                        if( cls != null && !cls.isEmpty() ) {
493                                buf.append( " class=\"" ).append( cls ).append( '"' );                  // 6.0.2.5 (2014/10/31) char を append する。
494                        }
495
496                        // colspan 属性の追加
497                        if( colspan > 1 ) {
498                                buf.append( " colspan=\"" ).append( colspan ).append( '"' );    // 6.0.2.5 (2014/10/31) char を append する。
499                        }
500
501                        // 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
502                        if( body == null || body.length == 0 ) {                // 6.3.9.1 (2015/11/27) 可変長引数でもnullは来る。
503//                              buf.append( " />" );
504                                buf.append( " ></td>" );                                        // 7.0.1.0 (2018/10/15)
505                        }
506                        else {
507                                buf.append( '>' );                                                      // 6.0.2.5 (2014/10/31) char を append する。
508                                for( final String bd : body ) {
509                                        buf.append( bd ).append( ' ' );                 // 6.3.9.1 (2015/11/27) スペースで連結追加
510                                }
511                                buf.append( "</td>" );
512                        }
513                }
514
515                return buf;
516        }
517
518        /**
519         * 時間文字列を数字に変換します。
520         *
521         * "0800" は、480に、"2100" は、1260 に変換します。
522         *
523         * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
524         *
525         * @param       val     時間文字列の値(0800 など)
526         * @param       defTm   引数の時間文字列が null の場合の初期値
527         *
528         * @return      時間文字列を数字に変換した結果( 80 など)
529         */
530        private int getStr2Time( final String val , final int defTm ) {
531                // 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)
532                final int rtn ;
533                if( val == null || val.isEmpty() ) { rtn = defTm; }
534                else if( val.length() == 4 ) {
535                        rtn = Integer.parseInt( val.substring( 0,2 ) )*60 +
536                                        Integer.parseInt( val.substring( 2 ) ) ;        // 5.4.3.7 (2012/01/20)
537                }
538                else {
539                        final String errMsg = "時間引数は、4桁必要です。" +
540                                                        "  val=[" + val + "]";
541                        throw new HybsSystemException( errMsg );
542                }
543
544                return rtn ;
545        }
546
547        /**
548         * 数字を時間文字列に変換します。
549         *
550         * 480は、"0800" に、"1260"は、2100 に変換します。
551         *
552         * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
553         *
554         * @param       timeVal 引数の時間文字列が null の場合の初期値
555         *
556         * @return      数字を時間文字列に変換した結果( "0800" など)
557         * @og.rtnNotNull
558         */
559        private String getInt2StrTime( final int timeVal ) {
560                int tmpVal = timeVal;
561                if(      tmpVal < minStTime ) { tmpVal = minStTime; }   // 最小値以下の場合は、最小値に合せる
562                else if( tmpVal > maxEdTime ) { tmpVal = maxEdTime; }   // 最大値以上の場合は、最大値に合せる。
563
564                return String.valueOf(100+ tmpVal/60).substring(1) + String.valueOf(100+tmpVal%60).substring(1);
565        }
566
567        // 引数に、 を追加する。
568
569        /**
570         * リンク文字列をパースします。
571         *
572         * データの空欄にリンクを作成するときに、元となるリンク文字列の引数を設定します。
573         * 引数は、&TMSTART=(stTime)&TMEND=(edTime) を追加するイメージです。
574         * stTime、edTime は、それぞれ、($1)、($2) の変数が割り当てられます。
575         * stTime、edTime は、#getInt2StrTime( int ) メソッドで変換した文字列を利用します。
576         *
577         * @param       lnkVal  リンクのベースとなる値
578         * @param       stTime  開始時刻の数字表記
579         * @param       edTime  終了時刻の数字表記
580         *
581         * @return      リンク文字列をパースした結果
582         */
583        private String makeLinkValue( final String lnkVal,final int stTime,final int edTime ) {
584                // 6.3.9.0 (2015/11/06) Found 'DD'-anomaly for variable(PMD)
585                return lnkVal == null ? ""
586                                                          : lnkVal.replace( "%28%241%29",getInt2StrTime( stTime ) )
587                                                                                .replace( "%28%242%29",getInt2StrTime( edTime ) ) ;
588        }
589
590        /**
591         * 表示項目の編集(並び替え)が可能かどうかを返します。
592         *
593         * @return      表示項目の編集(並び替え)が可能かどうか(false:不可能)
594         */
595        @Override
596        public boolean isEditable() {
597                return false;
598        }
599}