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