/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.plugin.view;

import java.util.List;
import java.util.ArrayList;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.html.ViewTimeTableParam;

/**
 * 時間軸を持つタイムテーブルの表示を行うクラスです。
 *
 * パラメータが必要な場合は、ViewTimeTableParamTag を使用してください。
 *
 * パラメータが設定されていない場合は、ViewForm_HTMLTimeTable の初期値が使用されます。
 * (パラメータを使用するには、viewタグのuseParam 属性をtrueに設定する必要があります。)
 *
 * SELECT文は、日付、キー、備考、開始時刻、終了時刻、リンクが、必須項目で、この並び順は、
 * 完全に固定です。よって、カラム位置を指定する必要はありませんが、SELECT文を自由に
 * 設定することも出来ませんので、ご注意ください。
 * この固定化に伴い、WRITABLE 指定も使用できません。
 * なお、日付、キー、備考 に関しては、columnDisplay 属性で、表示の ON/OFF 制御は可能です。
 * また、日付ブレイク、キーブレイクの設定で、カラム自体をテーブルの外に出すことが可能です。
 * (キーと備考はセットになっています。)
 *
 * タイムテーブルが空きの場合のリンクを指定できます。(ViewTimeTableParam.NULL_LINK_CLM_ID)
 * (ViewTimeTableParam の nullLinkColumn 属性)
 * 指定しない場合は、空きのリンクは作成されません。
 * このリンクは、特殊で、引数に、パラメータを追加できますが、"($1)"、"($2)" で指定します。
 * この($1)、($2)は、開始時刻、終了時刻がセットされますが、SELECT文の固定カラムと同じ
 * 並び順ですが、DBTableModelの値を設定しているわけではありません。
 * 空きの場合は、データ自体が存在しない場合と、日付、キー のみが 外部結合で生成された
 * レコードが実際に存在する場合がありますが、外部結合で生成されたレコードには、
 * 開始時刻、終了時刻はありません。($1) と($2)には、それぞれ、最小開始時刻と最大終了時刻を
 * セットします。
 *
 * 例として、&amp;TMSTART=($1)&amp;TMEND=($2) という文字列の ($*) 部分を解析して割当ます。
 *
 * ブレーク処理を行うカラムＩＤをCSV形式でセットできます。(ViewTimeTableParam.BREAK_CLMS)
 * (ViewTimeTableParam の breakClms 属性)
 * これは、ブレイク毎にテーブルが分かれて、テーブルの先頭に、ブレイクした
 * 値が表示されます。
 * 例えば、日付カラムをブレイクカラムとして設定すると、日付がブレイクするたび、
 * 日付をヘッダーに出して、テーブルを作成します。
 * ブレークカラムは、CSV形式で複数指定できます。その場合は、複数指定のカラムの
 * 合成された値で、キーブレイクの判定を行います。(簡単に言うとＯＲ判定になります。)
 * なお、ブレイクカラムを指定した場合は、自動的に、noDisplay 属性にその値をセット
 * します。
 *
 * @og.rev 5.4.0.0 (2011/10/01) 新規追加
 * @og.group 画面表示
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ViewForm_HTMLTimeTable extends ViewForm_HTMLTable {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.4.4.2 (2012/02/03)" ;

//	private int		intval		= 5;			// 30分=5 単位に colspan を設定する。
//	private int		minStTime	= 80;			// 08:00 のこと。30分=5 換算
//	private int		maxEdTime	= 210;			// 21:00 のこと。
	private int		intval		= 30;			// 30分 単位に colspan を設定する。
	private int		minStTime	= 480;			// 08:00 のこと。8H=480M
	private int		maxEdTime	= 1260;			// 21:00 のこと。21H=1260M

	private static final int	dyClmNo		= 0;	// ヘッダー1：ただし、グループ化する場合は、外出し
	private static final int	keyClmNo	= 1;	// ヘッダー2：ただし、グループ化する場合は、外出し
	private static final int	bikoClmNo	= 2;	// ヘッダー3：(内部では使用していません。)

	private static final int	tmstClmNo	= 3;	// 時間枠の開始時刻(含む)
	private static final int	tmedClmNo	= 4;	// 時間枠の終了時刻(含まない)
	private static final int	linkClmNo	= 5;	// 時間枠に表示する予約情報(変更画面へのリンク)

	private static final int	clmCnt		= 3;	// 決め打ち。今は、dyClm,keyClm の２つだけ表示

	// 引数に、DYUSE=(dyClm)&UNITID=(keyClm)&TMSTART=(stTime)&TMEND=(edTime) を追加する。
//	private String	linkValue	= "<a href=\"../GK0101/index.jsp?command=NEW&NORESERVE="
//									+ "&DYUSE=($1)&UNITID=($2)&TMSTART=($3)&TMEND=($4)"
//									+ "&BACK_GAMENID=GK0000&GAMENID=GK0101&BACK_ADDRESS=/gk/jsp/GK0000/index.jsp\" target=\"CONTENTS\">"
//									+ "<img src=\"../customImage/gk/space.gif\" width=\"100%\" height=\"20px\" />"
//									+ "</a>" ;
	// 引数パース機能付き データが存在しない場合のリンクのベースを設定。
//	private String	linkValue		= "" ;		// null にするとエラーになるので、空文字列
	private int		nullLinkClmNo	= -1 ;		// 固定ではなく可変の場合に利用するリンクカラムNo

	private int		tdClassColumnNo	= -1 ;		// 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo

//	private int[]	breakClmNos		= null;		// ブレーク処理を行うカラムNo
	private boolean isDyBreak		= true;		// 日付でブレイクするかどうかを指定
	private boolean isBookingMerge	= false;	// 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定

	/**
	 * 内容をクリア(初期化)します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
	 * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
	 *
	 */
	@Override
	public void clear() {
		super.clear();

//		intval		= 5;			// 30分=5 単位に colspan を設定する。
//		minStTime	= 80;			// 08:00 のこと。30分=5 換算
//		maxEdTime	= 210;			// 21:00 のこと。
		intval		= 30;			// 30分 単位に colspan を設定する。
		minStTime	= 480;			// 08:00 のこと。8H=480M
		maxEdTime	= 1260;			// 21:00 のこと。21H=1260M

//		linkValue		= "" ;			// null にするとエラーになるので、空文字列
		nullLinkClmNo	= -1;			// 固定ではなく可変の場合に利用するリンクカラム
//		breakClmNos		= null;			// ブレーク処理を行うカラムNo
		tdClassColumnNo	= -1 ;			// 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
		isDyBreak		= true;			// 日付でブレイクするかどうかを指定
		isBookingMerge	= false;		// 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
	}

	/**
	 * DBTableModel から HTML文字列を作成して返します。
	 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
	 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
	 * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
	 *
	 * @param  startNo	  表示開始位置
	 * @param  pageSize   表示件数
	 *
	 * @return	DBTableModelから作成された HTML文字列
	 */
	@Override
	public String create( final int startNo, final int pageSize )  {
		if( getRowCount() == 0 ) { return ""; }	// 暫定処置

		paramInit();
		headerLine	 = null;
		int lastNo = getLastNo( startNo, pageSize );
		int hsc = getHeaderSkipCount();
		int hscCnt = 1;

		StringBuilder out = new StringBuilder( HybsSystem.BUFFER_LARGE );

		if( isDyBreak ) {
			out.append( getRendererValue( 0,dyClmNo ) ).append( HybsSystem.CR );
			setColumnDisplay( dyClmNo,false );	// 日付ブレイクなら、setColumnDisplay をfalse にセット
		}

		out.append( getHeader() );

		out.append("<tbody>").append( HybsSystem.CR );
		int bgClrCnt = 0;

		int maxColspan = (maxEdTime-minStTime)/intval ;		// この数が、TDの数になる。
		int rowColspan = 0;									// 5.5.0.3(2012/03/12) １行の累積TD数。最大は、maxColspan で、
		int stTime     = minStTime;

		String backData    = "";	// 初期値。１回目にキーブレイクさせる。

		List<String> dblBooking = new ArrayList<String>();		// 重複データがあったときのデータ格納
		String nlVal	 = null;		// 空リンクのベース値
		String tdCls	 = null;		// 5.4.3.7 (2012/01/20) tdClassColumnNo 追加
		String dyVal	 = null;		// 日付項目の値
		String keyVal	 = null;		// キー項目の値
//		String breakVal	 = null;		// キーブレイクの値
		String bk_nlVal	 = null;		// キーブレイク時の元の空リンクのベース値
		String bk_dyVal	 = "";			// キーブレイク時の元の日付
//		String bk_breakVal = "";		// キーブレイクの元の値
		for( int row=startNo; row<lastNo; row++ ) {
			// キーブレイクの判定
			bk_nlVal   = nlVal;
//			bk_dyVal   = dyVal;
//			bk_keyVal  = keyVal;

//			nlVal = (nullLinkClmNo < 0 ) ? linkValue : getRendererValue( row,nullLinkClmNo );
			nlVal = (nullLinkClmNo < 0 ) ? null : getRendererValue( row,nullLinkClmNo );
			tdCls = (tdClassColumnNo < 0 ) ? null : getValue( row,tdClassColumnNo );	// 5.4.3.7 (2012/01/20)
			dyVal   = getValue( row,dyClmNo );
			keyVal  = getValue( row,keyClmNo );
			if( row==startNo ) { bk_dyVal   = dyVal; }		// 初期データをセット。
			//
//			if( breakClmNos != null ) {
//				StringBuilder blkVal = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
//				for( int i=0; i<breakClmNos.length; i++ ) {
//					blkVal.append( getValue( row,breakClmNos[i] ) );
//				}
//				breakVal = blkVal.toString();
//			}

			String linkVal = getRendererValue( row,linkClmNo );

			// キーブレイク判定。キーブレイクは、一番初めから来る。
			if( !backData.equals( dyVal + keyVal ) ) {
				backData   = dyVal + keyVal;			// null は来ないはず

				// minStTime < stTime の時だけ、処理を行う。(最初のキーブレイクは処理しないため)
				if( minStTime < stTime ) {
					// まずは、前の td の残りを出力する。ここでは、キーブレイク前の値を使用する。
					if( stTime < maxEdTime ) {
						out.append("  ");		// td タグの出力前の段落
						// 残データの書き出しは、最大TD数-それまでにセットした数。
						int td = maxColspan - rowColspan;		// 5.5.0.3(2012/03/12)
//						appendTag( out , "td" , (maxEdTime-stTime)/intval ,
//						appendTDTag( out , null , (maxEdTime-stTime)/intval , 	// 5.4.3.7 (2012/01/20)
						appendTDTag( out , null , td , 	// 5.5.0.3(2012/03/12)
										makeLinkValue( bk_nlVal , stTime , maxEdTime ) );
//										makeLinkValue( bk_nlVal , bk_dyVal , bk_keyVal , stTime , maxEdTime ) );
					}
					out.append("</tr>").append( HybsSystem.CR );
				}
				stTime = minStTime;		// 初期化

				// データかぶりが発生したときの処理
				if( !dblBooking.isEmpty() ) {
					for( String bkdt : dblBooking ) {
						out.append("<tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append(">").append( HybsSystem.CR );
						out.append( bkdt );
						out.append("</tr>").append( HybsSystem.CR );
					}
					dblBooking.clear();
				}

				// 日付ブレイク処理
				if( isDyBreak && row > startNo && !bk_dyVal.equals( dyVal ) ) {
					bk_dyVal = dyVal;
					out.append("<tr class=\"dummy\">");
					for(int column = 0; column < clmCnt; column++) {
						if( isColumnDisplay( column ) ) {
							out.append("<td/>");
						}
					}
//					for( int i=0; i<(maxEdTime-minStTime)/intval ; i++ ) {
					for( int i=0; i<maxColspan ; i++ ) {		// 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
						out.append("<td/>");
					}
					out.append("</tr>").append( HybsSystem.CR );
					out.append("</tbody>").append( HybsSystem.CR );
					out.append("</table>").append( HybsSystem.CR );

					out.append( getRendererValue( row,dyClmNo ) ).append( HybsSystem.CR );

					out.append( getHeader() );
					out.append("<tbody>").append( HybsSystem.CR );
					hscCnt = 1;
				}

				// ヘッダー繰り返し属性( headerSkipCount )を採用
				if( hsc > 0 && hscCnt % hsc == 0 ) {
					out.append( getHeadLine() );
					hscCnt = 1;
				}
				else {
					// 特殊処理：ここの処理では、一番最初も、実行されるので、＋＋しないように加工しておく。
					if( row > startNo ) { hscCnt ++ ; }
				}

				// ここから、新しい行が始まる。
				out.append("<tr").append( getBgColorCycleClass( bgClrCnt++ ) ).append(">").append( HybsSystem.CR );
				rowColspan = 0 ;		// 5.5.0.3(2012/03/12) 初期化

				for(int column = 0; column < clmCnt; column++) {
					if( isColumnDisplay( column ) ) {
						// ヘッダー部分に加工しやすいように、class 属性を与えておく。
						out.append("  <td class=\"" ).append( getColumnName( column ) ).append( "\">" );
						out.append( getValueLabel(row,column) );
						out.append("</td>").append( HybsSystem.CR );
					}
				}
			}

			// 文字列型の時分情報を数字に変換する。
//			int clStTime = getStr2Time( getValue( row,tmstClmNo ) , minStTime );
			int clStTime = getStr2Time( getValue( row,tmstClmNo ) , -1 );
			boolean nullData = (clStTime < 0) ;	// 開始時刻が	null の場合、-1 が返されるのでフラグをセットする。
			if( clStTime < minStTime ) { clStTime = minStTime; }	// 最小値以下の場合は、最小値に合せる。

			int clEdTime = getStr2Time( getValue( row,tmedClmNo ) , maxEdTime );
			if( clEdTime > maxEdTime ) { clEdTime = maxEdTime; }	// 最大値以上の場合は、最大値に合せる。

			if( clStTime == clEdTime ) { clEdTime = clEdTime + intval ; }	// 最初と最後が同じ場合は、intval分 進めておく。

			// 最初と最後が異なる場合は、間に空欄が入る。同じ場合は、連続なので、空欄は入らない。
			if( stTime < clStTime ) {
				out.append("  ");		// td タグの出力前の段落

				// 5.5.0.3(2012/03/12) 間に空欄が入る場合、その大きさが最小TD単位より大きければ、分割する。
				// ただし、直前の TD 個数が、最小でない場合のみ。
				int td = (clStTime-stTime)/intval;		// 5.5.0.3(2012/03/12)
				rowColspan += td;
//				appendTag( out , "td" , (clStTime-stTime)/intval ,
//				appendTDTag( out , null , (clStTime-stTime)/intval , 		// 5.4.3.7 (2012/01/20)
				appendTDTag( out , null , td , 		// 5.5.0.3(2012/03/12)
										makeLinkValue( nlVal , stTime , clStTime ) ).append( HybsSystem.CR );
//										makeLinkValue( nlVal , dyVal , keyVal , stTime , clStTime ) ).append( HybsSystem.CR );
			}
			// 前のデータとかぶっている。つまり、ブッキングデータがある。
			else if( stTime > clStTime ) {
				// 5.4.4.2 (2012/02/03) ブッキングデータをマージする機能を追加
				if( isBookingMerge ) {
					if( stTime < clEdTime ) {
						int td = (clEdTime-stTime)/intval;		// 5.5.0.3(2012/03/12)
						rowColspan += td ;
//						appendTDTag( out , tdCls , (clEdTime-stTime)/intval , linkVal ).append( HybsSystem.CR );
						appendTDTag( out , tdCls , td , linkVal ).append( HybsSystem.CR );
						stTime = clEdTime;
					}
					continue;
				}

				StringBuilder buf2 = new StringBuilder( HybsSystem.BUFFER_MIDDLE );

				buf2.append("  ");		// td タグの出力前の段落
//				appendTag( buf2 , "td" , clmCnt );									// ヘッダー部分
				for(int column = 0; column < clmCnt; column++) {
					if( isColumnDisplay( column ) ) {
						// ヘッダー部分に加工しやすいように、class 属性を与えておく。
						buf2.append("<td class=\"" ).append( getColumnName( column ) ).append( "\"/>" );
					}
				}

//				appendTag( buf2 , "td" , (clStTime-minStTime)/intval );				// 最初からデータまで
//				appendTag( buf2 , "td" , (clEdTime-clStTime)/intval , linkVal );	// データ部
//				appendTag( buf2 , "td" , (maxEdTime-clEdTime)/intval );				// データから最後まで

				// 5.4.3.7 (2012/01/20)
				appendTDTag( buf2 , null  , (clStTime-minStTime)/intval );				// 最初からデータまで
				appendTDTag( buf2 , tdCls , (clEdTime-clStTime)/intval , linkVal );		// データ部: 5.4.3.7 (2012/01/20) td に class属性追加
				appendTDTag( buf2 , null  , (maxEdTime-clEdTime)/intval );				// データから最後まで

				dblBooking.add( buf2.toString() );
				continue;
			}
			// 前も後ろも最小と最大になっているのは、予約レコードが無いため。
//			else if( clStTime == minStTime && clEdTime == maxEdTime ) {
			// stTime == clStTime のケース。nullData = true で、予約レコードが無いため。
			else if( nullData ) {
//				linkVal = makeLinkValue( nlVal , dyVal , keyVal , minStTime , maxEdTime );
				linkVal = makeLinkValue( nlVal , minStTime , maxEdTime );
			}
			// 5.4.3.7 (2012/01/20) linkVal を共通に使用している箇所を修正

			out.append("  ");		// td タグの出力前の段落
//			appendTag( out , "td" , (clEdTime-clStTime)/intval , linkVal ).append( HybsSystem.CR );
			int td = (clEdTime-clStTime)/intval;		// 5.5.0.3(2012/03/12)
			rowColspan += td ;
//			appendTDTag( out , tdCls , (clEdTime-clStTime)/intval , linkVal ).append( HybsSystem.CR );	// 5.4.3.7 (2012/01/20) td に class属性追加
			appendTDTag( out , tdCls , td , linkVal ).append( HybsSystem.CR );	// 5.5.0.3(2012/03/12)

			stTime = clEdTime ;
		}

		// 残処理：データが残っている場合は、書き出す必要がある。
		if( ( minStTime < stTime && stTime < maxEdTime ) ) {
			out.append("  ");		// td タグの出力前の段落
//			appendTag( out , "td" , (maxEdTime-stTime)/intval ,
			// 残データの書き出しは、最大TD数-それまでにセットした数。
			int td = maxColspan - rowColspan;		// 5.5.0.3(2012/03/12)
//			appendTDTag( out , null , (maxEdTime-stTime)/intval ,		// 5.4.3.7 (2012/01/20)
			appendTDTag( out , null , td ,		// 5.5.0.3(2012/03/12)
										makeLinkValue( nlVal , stTime , maxEdTime ) );
//										makeLinkValue( nlVal , dyVal , keyVal , stTime , maxEdTime ) );
//			out.append("</tr>").append( HybsSystem.CR );

			// データかぶりが発生したときの処理
//			if( !dblBooking.isEmpty() ) {
//				for( String bkdt : dblBooking ) {
//					out.append("<tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append(">").append( HybsSystem.CR );
//					out.append( bkdt );
//					out.append("</tr>").append( HybsSystem.CR );
//				}
//			}
//		}
//		else {
//			out.append("</tr>").append( HybsSystem.CR );
		}
		out.append("</tr>").append( HybsSystem.CR );

		// データかぶりが発生したときの処理
		if( !dblBooking.isEmpty() ) {
			for( String bkdt : dblBooking ) {
				out.append("<tr").append( getBgColorCycleClass( bgClrCnt-1 ) ).append(">").append( HybsSystem.CR );
				out.append( bkdt );
				out.append("</tr>").append( HybsSystem.CR );
			}
		}

		// カラムの結合があるため、td タグを個別に出力しておかないと、レイアウトがずれる。
		out.append("<tr class=\"dummy\">");
		for(int column = 0; column < clmCnt; column++) {
			if( isColumnDisplay( column ) ) {
				out.append("<td/>");
			}
		}
//		for( int i=0; i<(maxEdTime-minStTime)/intval ; i++ ) {
		for( int i=0; i<maxColspan ; i++ ) {		// 5.5.0.3(2012/03/12) 空td の出力。TD の colspan の基準になる。
			out.append("<td/>");
		}
		out.append("</tr>").append( HybsSystem.CR );

		out.append("</tbody>").append( HybsSystem.CR );
		out.append("</table>").append( HybsSystem.CR );

		out.append( getScrollBarEndDiv() );	// 3.8.0.3 (2005/07/15)
		return out.toString();
	}

	/**
	 * パラメータ内容を初期化します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) tdClassColumnNo 追加。intval の実値化
	 * @og.rev 5.4.4.2 (2012/02/03) isBookingMerge 追加
	 *
	 */
	private void paramInit() {
		String s_intval		= getParam( ViewTimeTableParam.TIME_INTERVAL	, null );
		String s_minStTime	= getParam( ViewTimeTableParam.MIN_START_TIME	, null );
		String s_maxEdTime	= getParam( ViewTimeTableParam.MAX_END_TIME		, null );

//		String[] s_brkClms	= StringUtil.csv2Array( getParam( ViewTimeTableParam.BREAK_CLMS , null ) );

		isDyBreak		= Boolean.valueOf( getParam( ViewTimeTableParam.USE_DY_BREAK, "true" ) );

		// 5.4.4.2 (2012/02/03) ブッキングデータをマージするかどうかを指定
		isBookingMerge	= Boolean.valueOf( getParam( ViewTimeTableParam.USE_BOOKING_MERGE, "false" ) );

		// nullリンクのカラム名指定。nullClm が優先順位が高い。
		String s_nullClm	= getParam( ViewTimeTableParam.NULL_LINK_CLM_ID	, null );
		if( s_nullClm != null ) {
			nullLinkClmNo = getColumnNo( s_nullClm );
		}
//		else {
//			linkValue	= getParam( ViewTimeTableParam.BODY_LINK_VALUE	, linkValue );
//		}

		// 5.4.3.7 (2012/01/20) データを入れるTDタグにclass属性を付与する場合のカラムNo
		String s_tdClsClm	= getParam( ViewTimeTableParam.TD_CLASS_COLUMN_ID	, null );
		if( s_tdClsClm != null ) {
			tdClassColumnNo = getColumnNo( s_tdClsClm );
		}

		// BREAK_CLMS は、csv2Array しているので、null は来ない。
//		if( s_brkClms.length > 0 ) {
//			breakClmNos	= new int[s_brkClms.length];
//			for( int i=0; i<s_brkClms.length; i++ ) {
//				breakClmNos[i] = getColumnNo( s_brkClms[i] );
//			}
//		}

		if( s_intval != null ) {
			intval = Integer.parseInt( s_intval ) ;	// 5.4.3.7 (2012/01/20)
		}
//		intval		= getStr2Time( s_intval		, intval );				// インターバル。0030 なら、5。  30分=5 換算
		minStTime	= getStr2Time( s_minStTime	, minStTime );			// 最小開始時刻。0800 なら、80。 30分=5 換算
		maxEdTime	= getStr2Time( s_maxEdTime	, maxEdTime );			// 最大終了時刻。2100 なら、210。30分=5 換算
	}

	/**
	 * DBTableModel から テーブルのタグ文字列を作成して返します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) colgroup は不要
	 *
	 * @return	テーブルのタグ文字列
	 */
	@Override
	protected String getTableHead() {
		StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );

	//  5.4.3.7 (2012/01/20) colgroup は不要
	//	for(int column = 0; column < clmCnt; column++) {
	//		if( isColumnDisplay( column ) ) {
	//			buf.append("<colgroup class=\"" );
	//			buf.append( getColumnDbType(column) );		// 4.0.0 (2005/01/31)
	//			buf.append("\"/>");
	//			buf.append( HybsSystem.CR );
	//		}
	//	}

		// 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動
		buf.append("<thead id=\"header\">").append( HybsSystem.CR );	// 3.5.6.5 (2004/08/09)
		buf.append( getHeadLine() );
		buf.append("</thead>").append( HybsSystem.CR );

		return buf.toString();
	}

	/**
	 * ヘッダー繰り返し部を、getTableHead()メソッドから分離。
	 *
	 * @param	thTag タグの文字列
	 *
	 * @return	テーブルのタグ文字列
	 */
	@Override
	protected String getHeadLine( final String thTag ) {
		if( headerLine != null ) { return headerLine; }		// キャッシュを返す。

		StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );

		buf.append("<tr class=\"row_h\"").append(" >").append( HybsSystem.CR );

		buf.append( HybsSystem.CR );
		for(int column = 0; column < clmCnt; column++) {
			if( isColumnDisplay( column ) ) {
//				buf.append( thTag ).append(">");
				buf.append( thTag ).append(" class=\"" ).append( getColumnName( column ) ).append( "\">" );
				buf.append( getColumnLabel(column) );
				buf.append("</th>").append( HybsSystem.CR );
			}
		}

//		for(int tm = minStTime; tm < maxEdTime; tm+=10 ) {	// intval づつ進めるべきだが、1時間ごとの箱とする。
		for(int tm = minStTime; tm < maxEdTime; tm+=60 ) {	// ヘッダーは、１時間単位とする。
//			buf.append( thTag ).append(" class=\"timeVar\" colspan=\"2\">");
			buf.append( thTag ).append(" class=\"timeVar\" colspan=\"" ).append( 60/intval ).append( "\">");
//			buf.append( (tm/10) );
			buf.append( (tm/60) );
			buf.append("</th>").append( HybsSystem.CR );
		}

		buf.append("</tr>").append( HybsSystem.CR );

		headerLine = buf.toString();
		return headerLine;
	}

	/**
	 * TDタグ文字列を簡易的に合成します。
	 *
	 * ここでは、主に、class 属性、colspan 属性を設定することを目的にしています。
	 * colspan の値によって、動作を変化させています。
	 *   0: タグそのものを生成しません。これは、第一引数をそのまま返します。
	 *   1: colspan 属性を出力しません。(Default値なので)
	 *   それ以外は、colspan に引数を設定します。
	 * BODY 部も、無指定の場合は、/&gt; 止めのタグを出力します。
	 *
	 * 返り値の StringBuilder は、第一引数そのものを返します。よって、このメソッドに、
	 * append を連結していくことも可能です。
	 * (return値を使わなくても、第一引数の StringBuilder は変化しています。副作用というやつ。)
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) tdタグ専用に変更。
	 *
	 * @param	buf		このStringBuilderに append します。
	 * @param	cls		追加する class 属性
	 * @param	colspan	td , th タグ属性に追加する colspan値。
	 *                     0 の場合は、タグ自体を出力しません。
	 *                     1 の場合は、colspan を出力しません。
	 * @param	body	タグの BODY 部に出力する内容(String可変長引数)０件の場合は、BODYなし
	 *
	 * @return	appendされたStringBuilder(第一引数と同じオブジェクト)
	 */
//	private StringBuilder appendTag( final StringBuilder buf , final String tag , final int colspan , final String... body ) {
	private StringBuilder appendTDTag( final StringBuilder buf , final String cls , final int colspan , final String... body ) {
		if( colspan == 0 ) { return buf; }

//		buf.append( "<" ).append( tag );
//		if( colspan > 1 ) {
//			buf.append( " colspan=\"" ).append( colspan ).append( "\"" );
//		}
//
//		int cnt = body.length;
//		if( cnt == 0 ) { buf.append( " />" ); }
//		else {
//			 buf.append( ">" );
//			for( int i=0; i<cnt; i++ ) {
//				buf.append( body[i] );
//			}
//			 buf.append( "</" ).append( tag ).append( ">" );
//		}

		buf.append( "<td" );
		// class 属性の追加
		if( cls != null && !cls.isEmpty() ) {
			buf.append( " class=\"" ).append( cls ).append( "\"" );
		}

		// colspan 属性の追加
		if( colspan > 1 ) {
			buf.append( " colspan=\"" ).append( colspan ).append( "\"" );
		}

		int cnt = body.length;
		if( cnt == 0 ) { buf.append( " />" ); }
		else {
			 buf.append( ">" );
			for( int i=0; i<cnt; i++ ) {
				buf.append( body[i] );
			}
			 buf.append( "</td>" );
		}

		return buf;
	}

	/**
	 * 時間文字列を数字に変換します。
	 *
	 * "0800" は、480に、"2100" は、1260 に変換します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
	 *
	 * @param	val  	時間文字列の値(0800 など)
	 * @param	defTm	引数の時間文字列が null の場合の初期値
	 *
	 * @return	時間文字列を数字に変換した結果( 80 など)
	 */
	private int getStr2Time( final String val , final int defTm ) {
		if( val == null || val.isEmpty() ) { return defTm; }
		else if( val.length() != 4 ) {
			String errMsg = "時間引数は、４桁必要です。" +
							"  val=[" + val + "]";
			throw new HybsSystemException( errMsg );
		}

//		return Integer.parseInt( val.substring( 0,2 ) )*10 +
//					Integer.parseInt( val.substring( 2 ) )/6 ;	// 30分=5 になるように
		return Integer.parseInt( val.substring( 0,2 ) )*60 +
					Integer.parseInt( val.substring( 2 ) ) ;	// 5.4.3.7 (2012/01/20)

	}

	/**
	 * 数字を時間文字列に変換します。
	 *
	 * 480は、"0800" に、"1260"は、2100 に変換します。
	 *
	 * @og.rev 5.4.3.7 (2012/01/20) 計算方法の変更
	 *
	 * @param	timeVal	引数の時間文字列が null の場合の初期値
	 *
	 * @return	数字を時間文字列に変換した結果( "0800" など)
	 */
	private String getInt2StrTime( final int timeVal ) {
		int tmpVal = timeVal;
		if(      tmpVal < minStTime ) { tmpVal = minStTime; }	// 最小値以下の場合は、最小値に合せる
		else if( tmpVal > maxEdTime ) { tmpVal = maxEdTime; }	// 最大値以上の場合は、最大値に合せる。

		//        桁合せ    85→8→800 の変換         85→5→30 の変換      足して、10830 になる。
//		int rtn = 10000 + ( tmpVal / 10 ) * 100 + ( ( tmpVal % 10 ) * 6 ) ;

//		return String.valueOf( rtn ).substring(1);			// "0830" 部分を返す。
		return String.valueOf(100+ tmpVal/60).substring(1) + String.valueOf(100+tmpVal%60).substring(1);
	}

	// 引数に、 を追加する。

	/**
	 * リンク文字列をパースします。
	 *
	 * データの空欄にリンクを作成するときに、元となるリンク文字列の引数を設定します。
	 * 引数は、&TMSTART=(stTime)&TMEND=(edTime) を追加するイメージです。
	 * stTime、edTime は、それぞれ、($1)、($2) の変数が割り当てられます。
	 * stTime、edTime は、#getInt2StrTime( int ) メソッドで変換した文字列を利用します。
	 *
	 * @param	lnkVal 	リンクのベースとなる値
	 * @param	stTime 	開始時刻の数字表記
	 * @param	edTime 	終了時刻の数字表記
	 *
	 * @return	リンク文字列をパースした結果
	 */
	private String makeLinkValue( final String lnkVal,final int stTime,final int edTime ) {
//	private String makeLinkValue( final String lnkVal,final String dyClm,final String keyClm,final int stTime,final int edTime ) {
		String rtn = "";
		if( lnkVal != null ) {
	//		rtn = lnkVal.replace( "($1)",getInt2StrTime( stTime ) )
	//						.replace( "($2)",getInt2StrTime( edTime ) ) ;
//							.replace( "($3)",dyClm )
//							.replace( "($4)",keyClm ) ;

			// URLエンコードがかかっている場合。なお、分解して処理しないのは、他にURLエンコードされている箇所を考慮して。
			rtn = lnkVal.replace( "%28%241%29",getInt2StrTime( stTime ) )
							.replace( "%28%242%29",getInt2StrTime( edTime ) ) ;
//							.replace(  "%28%243%29",dyClm )
//							.replace( "%28%244%29",keyClm ) ;
		}

		return rtn;
	}

	/**
	 * 表示項目の編集(並び替え)が可能かどうかを返します
	 *
	 * @return	表示項目の編集(並び替え)が可能かどうか(false:不可能)
	 */
	@Override
	public boolean isEditable() {
		return false;
	}
}
