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 = "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 の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         *
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) 1行の累積TD数。最大は、maxColspan で、
163                int stTime     = minStTime;
164
165                String backData    = "";        // 初期値。1回目にキーブレイクさせる。
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 ) { // ヘッダーは、1時間単位とする。
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可変長引数)0件の場合は、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 = "時間引数は、4桁必要です。" +
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}