/*
 * 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.hayabusa.taglib;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.resource.CalendarData;
import org.opengion.hayabusa.resource.CalendarFactory;
import org.opengion.fukurou.util.StringUtil;

import static org.opengion.fukurou.util.StringUtil.nval ;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale ;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * ガントチャート用のヘッダー情報（日付け等）を出力するタグです。
 *
 * スライドバー式ガントチャートの表示時に、スライドバーの長さと日付とを関連付けるための
 * ヘッダー情報（日付け等）を出力します。スライドバーと日付間隔は、zoom 属性によって、
 * CSSファイルのフォントサイズと、JavaScript の内部配列に依存しています。
 * また、このヘッダーでは、日付の休日情報を、カレンダテーブルを参照することで取得しています。
 * calDB 属性への指定は、CalendarFactory で生成されるテーブル名(CalendarDataの実装クラス)
 * を指定します。
 *
 * @og.formSample
 * ●形式：&lt;og:ganttHeader startDate="…" endDate="…" zoom="[…]" /&gt;
 * ●body：なし
 *
 * ●使用例
 *    参照：jsp/GANTT06 フォルダ
 *
 *     &lt;og:ganttHeader
 *         startDate = "{&#064;VIEW_DATE}"
 *         endDate   = "{&#064;DYMAX}"
 *         zoom      = "{&#064;VIEW_ZOOM}"
 *         calDB     = "GE13"
 *         arg1      = "{&#064;CDJGS}"
 *         arg2      = ""
 *     /og:ganttHeader&gt;
 *
 *     &lt;og:ganttHeader
 *         startDate  = "{&#064;VIEW_DATE}"
 *         endDate    = "{&#064;DYMAX}"
 *         daySpan    = "0700-2000"                               HHmm-HHmm 形式で１日の時間範囲を指定
 *         breakTimes = "1200-1245,1645-1650,200508200700-1200;"  休憩時間は、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm形式、または、yyyyMMddHHmm-yyyyMMddHHmm形式です。
 *         breakTimeStyle = "top:20px;background-color:yellow;"   休憩時間のスタイルに追加します。
 *         zoom       = "20"
 *     /og:ganttHeader&gt;
 *
 * ●定義・解説
 *   PL/SQL の VIEW_ZOOM メソッドを用いて、実際の日付けから画面表示ピクセルへ
 *   変換する割合を求めます。これと、jsp\GANTT\image の ganttBGx.gif(x は ZOOM変数)
 *   を対応付けて、日付けのヘッダーを求めます。
 *   jsp\custom\custom.css の th.zoomx(x は ZOOM変数) により、日付けヘッダー文字の
 *   表示間隔を制御していますので、これらの関係を正確に把握しておいて下さい。
 *   日付けヘッダーは、イメージ変数を用いている関係上、固定の日数を加算して、
 *   求めます。（週:7日, 半月:15日, 月:30日, 4半期:90日, 年:360日）
 *
 * ヘッダー部のカレンダに、事業所カレンダを使用することが出来ます。
 * 使用する場合は、事業所コード( arg1 )属性のセットと、システムパラメータ の
 * USE_CALENDAR_DATABASE 属性を true に設定しておく必要があります。
 * このフラグは、事業所カレンダテーブル(GE13)を使用するために必要です。
 * zoom="10"(週単位) を使用の週数の計算は、年初の最初の７日間が含まれる週を、第１週と数えます。
 *
 * zoom="20"(時)間 では、１日の時間範囲をdaySpanでHHmm-HHmm形式で指定します。
 * 開始終了は、startDate、endDateで指定しますが、yyyyMMdd または、yyyyMMddHHmm 形式とします。
 * calDB で指定された カレンダテーブルを見て、休日の場合は、表示をスキップします。
 * 休憩時間は、breakTimes で指定します。(HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、
 * または、yyyyMMddHHmm-yyyyMMddHHmm形式)
 * カンマ区切りで複数指定できます。日付指定がない場合は、毎日、日付指定がある場合は、
 * その日のみ有効です。休憩時間は、日をまたぐ事は可能です。
 * breakTimeStyle として、休憩時間の指定に追加するスタイル属性を追加します。
 * 通常は、バックカラーと上からの位置です。初期値は、"top:20px;background-color:yellow;" です。
 * ヘッダー表示は、１時間単位です。
 *
 * zoom="21"(時)間 では、一時間のピクセルを"20"(時)の半分に変更します。
 *
 * システムリソースに USE_GANTTHEADER_MULTIPLE_COLUMN パラメータを設定すると、
 * ガントヘッダーにおいて、時間ヘッダーの上段に日付を、下段の時間軸の(h)の削除を行います。
 * 日付は、表示開始時刻の上と、それの６時間ごとに表示します。
 * ２段組を使用するかどうかを、システムリソースの USE_GANTTHEADER_MULTIPLE_COLUMN で指定します。
 *
 * システムリソースに USE_GANTTHEADER_MONTH_DATE パラメータを設定すると、
 * ガントヘッダーにおいて、30日、90日、180日のヘッダー表示で、日付まで
 * 表示するか、月のみ表示するかを指定します。
 * ガントヘッダーの繰返し表示でバックグラウンドイメージは固定日なので、一月表示を
 * 30日単位で表示していますが、先頭日付が 1日でないため、判りにくいと苦情がありました。
 * そこで、30日、90日、180日のヘッダー表示時に、日付を表示しないようにするフラグです。
 * 月表示は、その月が 20日以降の場合、翌月を表示します。
 * 初期値は、互換性の為、false です。
 *
 * @og.rev 3.5.4.8 (2004/02/23) 新規作成
 * @og.group 画面部品
 *
 * @version  4.0
 * @author	Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class GanttHeaderTag extends CommonTagSupport {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "4.0.0 (2005/08/31)" ;

	private static final long serialVersionUID = 4000 ;	// 4.0.0 (2005/01/31)

	private String	startDate	= null;
	private String	endDate		= null;
	private String	daySpan		= "0700-2000";	// １日の時間範囲(HHmm-HHmm) 3.8.0.5 (2005/08/26)
	private int		zoom		= 2;
	private String	breakTimes	= null;		// 休憩時間は、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、または、yyyyMMddHHmm-HHmmyyyyMMdd形式です。
	private String	breakTimeStyle	= "top:20px;background-color:yellow;" ;
	private String	calDB	= null;	// 3.6.0.0 (2004/09/17) DB検索するＤＢのキー
	private String	arg1	= null;	// 3.6.0.0 (2004/09/17) DB検索の第１キー(事業所コードなど）
	private String	arg2	= null;	// 3.6.0.0 (2004/09/17) DB検索の第２キー(ＷＣコードなど）
	private String	arg3	= null;	// 3.6.0.0 (2004/09/17) DB検索の第３キー(事業所コードなど）
	private String	arg4	= null;	// 3.6.0.0 (2004/09/17) DB検索の第４キー(ＷＣコードなど）
	private boolean skipHoliday	= HybsSystem.sysBool( "USE_GANTTHEADER_SKIP_HOLIDAY" );		// 4.0.0.0 (2007/09/07) 休日をスキップするかどうか
	private String 	calStr	= null; // 5.5.4.0 (2012/07/02) 出勤日・休日のフラグ

	private static final int	 TIME_PIXEL		= 48;	// 3.8.1.1 (2005/11/21) 時間ヘッダー時の１時間のピクセル数
	private static final boolean USE_MULTI_CLM	= HybsSystem.sysBool( "USE_GANTTHEADER_MULTIPLE_COLUMN" );	// 3.8.1.4 (2006/03/13)
	private static final boolean USE_MONTH_DATE	= HybsSystem.sysBool( "USE_GANTTHEADER_MONTH_DATE" );	// 3.8.5.2 (2006/06/09)
	private static final int	 MONTH_DATE_LIMITER = 20;	// 当月 20 日以降は、翌月扱いとなる。

	private static final String BLUE_COLOR	= "<span style=\"color:Blue;\">" ;
	private static final String RED_COLOR	= "<span style=\"color:Red;\">" ;
	private static final String END_SPAN	= "</span>" ;
	private static final String BR			= "<br/>" ;

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.6.0.0 (2004/09/17) zoom 3 の時の計算値を変更
	 * @og.rev 3.7.1.0 (2005/04/15) zoom 10 の追加
	 * @og.rev 3.8.1.6 (2006/04/25) zoom 21 の追加
	 * @og.rev 3.8.5.2 (2006/06/09) makeZoom メソッドの USE_MONTH_DATE パラメータ追加
	 * @og.rev 3.8.9.2 (2007/07/28) makeZoomTime メソッドの 時間間隔パラメータ追加
	 * @og.rev 5.5.4.0 (2012/07/02) ヘッダー属性情報の出力追加
	 *
	 * @return  int 後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		if( startDate == null || endDate == null ||
			 startDate.length() < 8 || endDate.length() < 8 ) {
				String errMsg = "開始日と終了日は、どちらも yyyyMMdd 形式で、必須です。"
						+ " startDate=[" + startDate + "] endDate=[" + endDate + "]" ;
				throw new HybsSystemException( errMsg );
		}

		String header = "";
		String md = (USE_MONTH_DATE) ? "MM   " : "MM/dd";
		switch( zoom ) {
			case  0: header = makeZoom( "MM/   "     ,"dd(EE)"     ,  1 ,false ) ; break;
			case  1: header = makeZoom( "MM/"        ,"dd "        ,  1 ,false ) ; break;
			case  2: header = makeZoom2( 1 ) ; break;
			case  3: header = makeZoom( "yyyy/  "    ,"MM/dd  "    ,  7 ,false ) ; break;
			case  4: header = makeZoom( "yyyy/  "    ,"MM/dd  "    , 14 ,false ) ; break;
			case  5: header = makeZoom( "yyyy/     " ,md + "     " , 30 ,USE_MONTH_DATE ) ; break;
			case  6: header = makeZoom( "yyyy/ "     ,md + " "     , 30 ,USE_MONTH_DATE ) ; break;
			case  7: header = makeZoom( "yyyy/     " ,md + "     " , 90 ,USE_MONTH_DATE ) ; break;
			case  8: header = makeZoom( "yyyy/     " ,md + "     " ,180 ,USE_MONTH_DATE ) ; break;
			case  9: header = makeZoom( "yyyy/ "     ,md + " "     ,180 ,USE_MONTH_DATE ) ; break;
			case 10: header = makeZoom( "ww'W'"      ,"'<span title=\"'MM/dd'\">'dd '</span>'" ,7 ,false ) ; break;
	//		case 11: break;	// 他物件予約済み
	//		case 12: break;	// 他物件予約済み
			case 20: header = makeZoomTime( TIME_PIXEL  ,  1,true  ) ; break;	// 3.8.1.1 (2005/11/21)
			case 21: header = makeZoomTime( TIME_PIXEL/2,  1,false ) ; break;	// 3.8.1.6 (2006/04/25)
			case 22: header = makeZoomTime( TIME_PIXEL  ,  2,true  ) ; break;	// 3.8.9.2 (2007/07/28)
			case 23: header = makeZoomTime( TIME_PIXEL/2,  2,false ) ; break;	// 3.8.9.2 (2007/07/28)
			case 24: header = makeZoomTime( TIME_PIXEL  ,  4,true  ) ; break;	// 3.8.9.2 (2007/07/28)
			case 25: header = makeZoomTime( TIME_PIXEL/2,  4,false ) ; break;	// 3.8.9.2 (2007/07/28)
			case 26: header = makeZoomTime( TIME_PIXEL  ,  6,true  ) ; break;	// 3.8.9.2 (2007/07/28)
			case 27: header = makeZoomTime( TIME_PIXEL/2,  6,false ) ; break;	// 3.8.9.2 (2007/07/28)
			case 28: header = makeZoomTime( TIME_PIXEL  ,  8,true  ) ; break;	// 3.8.9.2 (2007/07/28)
			case 29: header = makeZoomTime( TIME_PIXEL/2,  8,false ) ; break;	// 3.8.9.2 (2007/07/28)
			case 30: header = makeZoomTime( TIME_PIXEL  , 12,true  ) ; break;	// 3.8.9.2 (2007/07/28)
			case 31: header = makeZoomTime( TIME_PIXEL/2, 12,false ) ; break;	// 3.8.9.2 (2007/07/28)
			default: break;
		}
		
		jspPrint( header );
		
		jspPrint(makeHeaderData()); // 5.5.4.0 (2012/07/02)
		
		return(EVAL_PAGE);
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 *
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) 事業所コード cdjgs 属性を追加
	 * @og.rev 3.8.0.2 (2005/07/11) startTime,timePeriod,breakTimes 属性を追加
	 * @og.rev 3.8.0.5 (2005/08/26) daySpan,breakTimes,breakTimeStyle 属性を追加
	 * @og.rev 5.5.4.0 (2012/07/02) calStr追加
	 */
	@Override
	protected void release2() {
		super.release2();
		startDate	= null;
		endDate		= null;
		zoom		= 2;
		calDB		= null;	// 3.6.0.0 (2004/09/17) DB検索するＤＢのキー
		arg1		= null;	// 3.6.0.0 (2004/09/17) DB検索の第１キー(事業所コードなど）
		arg2		= null;	// 3.6.0.0 (2004/09/17) DB検索の第２キー(ＷＣコードなど）
		arg3		= null;	// 3.6.0.0 (2004/09/17) DB検索の第３キー
		arg4		= null;	// 3.6.0.0 (2004/09/17) DB検索の第４キー
		daySpan		= "0700-2000";	// 開始時刻(HHmm-HHmm) 3.8.0.5 (2005/08/26)
		breakTimes	= null;		// 休憩時間は、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、または、yyyyMMddHHmm-HHmmyyyyMMdd形式です。
		breakTimeStyle	= "top:20px;background-color:yellow;" ;
		skipHoliday = HybsSystem.sysBool( "USE_GANTTHEADER_SKIP_HOLIDAY" ); // 4.0.0.0 (2007/09/10)
		calStr		= null; // 5.5.4.0 (2012/07/02)
	}

	/**
	 * 上段・下段のフォーマットと、加算日に応じたヘッダー文字列を作成します。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) 休日判断を、事業所カレンダを使用
	 * @og.rev 3.7.1.0 (2005/04/15) zoom == 10 週単位の場合の特殊処理を追加
	 * @og.rev 3.7.1.1 (2005/05/23) 本日は、青色で示します。
	 * @og.rev 3.8.5.2 (2006/06/09) makeZoom メソッドの USE_MONTH_DATE パラメータ追加
	 * @og.rev 5.5.4.0 (2012/07/02) カレンダーフラグ対応
	 *
	 * @param upper String 上段の表示フォーマット
	 * @param lower String 下段の表示フォーマット
	 * @param add   int    表示日付けの加算
	 * @param useMonth boolean  月日表示でなく、月表示のみにする。(20日以降を翌月表示する。）
	 * @return  ヘッダー文字列
	 */
	private String makeZoom( final String upper ,final String lower ,final int add, final boolean useMonth ) {
		DateFormat format1 = new SimpleDateFormat( upper,Locale.JAPAN );
		DateFormat format2 = new SimpleDateFormat( lower,Locale.JAPAN );

		Calendar str = HybsSystem.getCalendar( startDate );
		Calendar end = HybsSystem.getCalendar( endDate );

		// zoom == 10 週単位の場合の特殊処理。
		// 年の第一週を完全な一週間分(７日間)として、設定します。
		if( zoom == 10 ) {
			str.setMinimalDaysInFirstWeek( 7 );
			format1.setCalendar( str );
			format2.setCalendar( str );
		}

		StringBuilder buf1 = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder buf2 = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder bufcal = new StringBuilder( HybsSystem.BUFFER_SMALL ); // 5.5.4.0 (2012/07/02) 休日判定用

		// 3.6.0.0 (2004/09/17) カレンダDB検索機能を追加
		CalendarData calData = CalendarFactory.getCalendarData( calDB,arg1,arg2,arg3,arg4 );

		boolean modifyFlag = false;
		while( str.before( end ) ) {
			Date dt = str.getTime();
			// 3.8.5.2 (2006/06/09) useMonth ﾌﾗｸﾞにより 月表示のみに変更します。
			// MONTH_DATE_LIMITER で指定の日以降は、翌月扱いとなる。
			if( useMonth && str.get( Calendar.DATE ) >= MONTH_DATE_LIMITER ) {
				Calendar calPlus = (Calendar)str.clone();
				calPlus.set( Calendar.DATE , 1 );		// １日にセットします。
				calPlus.add( Calendar.MONTH , 1 );		// 月を１ヶ月プラスします。
				dt = calPlus.getTime();		
			}

			// 3.7.1.1 (2005/05/31) 本日は、青色で示します。
			if( calData.isContainedToday( str,add ) ) {
				buf1.append( BLUE_COLOR );
				buf2.append( BLUE_COLOR );
				bufcal.append( "0" ); // 5.5.4.0 (2012/07/02) 
				modifyFlag = true;
			}

			// 3.6.0.0 (2004/09/17) 休日判断を、事業所カレンダを使用
			// modifyFlag が立っていない場合 を条件に追加します。
			if( !modifyFlag && add == 1 && calData.isHoliday( str ) ) {
				buf1.append( RED_COLOR );
				buf2.append( RED_COLOR );
				bufcal.append( "1" ); // 5.5.4.0 (2012/07/02) 休日
				modifyFlag = true;
			}

			buf1.append( format1.format( dt ) );
			buf2.append( format2.format( dt ) );

			if( modifyFlag ) {
				buf1.append( END_SPAN );
				buf2.append( END_SPAN );
				modifyFlag = false;
			}
			else{
				bufcal.append( "0" ); // 5.5.4.0 (2012/07/02) 
			}
			str.add(Calendar.DATE, add);
		}
		
		calStr = bufcal.toString(); // 5.5.4.0 (2012/07/02)
		
		return "<pre>" + buf1.toString() + BR + buf2.toString() + "</pre>";
	}

	/**
	 * zoom = 2 の場合のヘッダーを出力します。
	 * このヘッダーだけ特別なので、別メソッドを作成します。
	 *
	 * <del>[0000 ･･･]<br/>[1234 ･･･] というヘッダーを出力します。</del>
	 * [ 0   ･･･]<br/>[ 2   ･･･]<br/>[ /   ･･･]<br/>[0000 ･･･]<br/>[1234 ･･･]
	 *
	 * という月情報を毎週月曜日にヘッダーに出力します。(上記は、２月１日～２月４日まで）
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) 休日判断を、事業所カレンダを使用
	 * @og.rev 3.6.0.7 (2004/11/06) 月の情報も出力します。
	 * @og.rev 3.7.1.1 (2005/05/23) 本日は、青色で示します。
	 * @og.rev 5.5.4.0 (2012/07/02) カレンダーフラグ対応
	 *
	 * @param add   int    表示日付けの加算
	 * @return  ヘッダー文字列
	 */
	private String makeZoom2( final int add ) {
		Calendar str = HybsSystem.getCalendar( startDate );
		Calendar end = HybsSystem.getCalendar( endDate );

		StringBuilder buf1 = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 月の１桁目
		StringBuilder buf2 = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 月の２桁目
		StringBuilder buf3 = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 区切り記号
		StringBuilder buf4 = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 日の１桁目
		StringBuilder buf5 = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 日の２桁目
		StringBuilder bufcal = new StringBuilder( HybsSystem.BUFFER_SMALL );	// 5.5.4.0 (2012/07/02) 休日判定用

		// 3.6.0.0 (2004/09/17) カレンダDB検索機能を追加
		CalendarData calData = CalendarFactory.getCalendarData( calDB,arg1,arg2,arg3,arg4 );

		boolean modifyFlag = false;
		while( str.before( end ) ) {
			int mt = str.get( Calendar.MONTH ) + 1;
			int dt = str.get( Calendar.DATE );
			boolean monday = ( Calendar.MONDAY == str.get( Calendar.DAY_OF_WEEK ) );

			// 3.7.1.1 (2005/05/31) 本日は、青色で示します。
			if( calData.isContainedToday( str,add ) ) {
				buf1.append( BLUE_COLOR );
				buf2.append( BLUE_COLOR );
				buf3.append( BLUE_COLOR );
				buf4.append( BLUE_COLOR );
				buf5.append( BLUE_COLOR );
				bufcal.append("0"); // 5.5.4.0 (2012/07/02)
				modifyFlag = true;
			}

			// 3.6.0.0 (2004/09/17) 休日判断を、事業所カレンダを使用
			// modifyFlag が立っていない場合 を条件に追加します。
			if( !modifyFlag && add == 1 && calData.isHoliday( str ) ) {
				buf1.append( RED_COLOR );
				buf2.append( RED_COLOR );
				buf3.append( RED_COLOR );
				buf4.append( RED_COLOR );
				buf5.append( RED_COLOR );
				bufcal.append("1"); // 5.5.4.0 (2012/07/02)
				modifyFlag = true;
			}

			if( monday ) {
				buf1.append( (mt/10) );
				buf2.append( mt%10 );
				buf3.append( "/" );
			}
			else {
				buf1.append( " " );
				buf2.append( " " );
				buf3.append( " " );
			}
			buf4.append( (dt/10) );
			buf5.append( dt%10 );

			if( modifyFlag ) {
				buf1.append( END_SPAN );
				buf2.append( END_SPAN );
				buf3.append( END_SPAN );
				buf4.append( END_SPAN );
				buf5.append( END_SPAN );
				modifyFlag = false;
			}
			else{
				bufcal.append("0"); // 5.5.4.0 (2012/07/02)
			}
			str.add(Calendar.DATE, add);
		}

		buf1.append( BR ).append( buf2 );
		buf1.append( BR ).append( buf3 );
		buf1.append( BR ).append( buf4 );
		buf1.append( BR ).append( buf5 );
		
		calStr = bufcal.toString(); // 5.5.4.0 (2012/07/02)

		return "<pre>" + buf1.toString() + "</pre>";
	}

	/**
	 * 時間軸でのヘッダー表示を行います。
	 *
	 * 時間軸の為、ヘッダーの上下段組は行いません。
	 * ヘッダーは、１時間単位に表示します。また、休憩時刻も求めています。
	 *
	 * @og.rev 3.8.0.5 (2005/08/26) 新規追加
	 * @og.rev 3.8.1.1 (2005/11/21) 日付title,範囲class,
	 * @og.rev 3.8.1.4 (2006/03/13) 日付表示、時刻単位(h)削除、２段組(USE_MULTI_CLM)
	 * @og.rev 3.8.1.6 (2006/04/25) 日や時間の単位表示を止め、２文字分(24ピクセル)化する。
	 * @og.rev 3.8.9.2 (2007/07/28) 時間間隔パラメータ追加
	 * @og.rev 4.0.0.0 (2007/09/03) 休日の場合でも表示されるように対応(システムリソースで切り替え)
	 * @og.rev 5.5.4.0 (2012/07/02) ガントの一日表示枠の計算を切り上げするようにする。カレンダーフラグ対応。
	 *
	 * @param  int hourOfPixcel １時間あたりのピクセル数。
	 * @param  int step  時間間隔の指定(1:１時間間隔、2:２時間間隔････)
	 * @param  boolean useTani  日や時間の単位表示を行う(true)か、行わない(false)か
	 * @return  ヘッダー文字列
	 */
//	private String makeZoomTime( final int hourOfPixcel,final boolean useTani ) {
	private String makeZoomTime( final int hourOfPixcel,final int step,final boolean useTani ) {
		DateFormat format1 = new SimpleDateFormat( "yyyy/MM/dd",Locale.JAPAN );

		StringBuilder buf1 = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder buf2 = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder bufH = new StringBuilder( HybsSystem.BUFFER_SMALL );		// 3.8.1.4 (2006/03/13)
		StringBuilder bufcal = new StringBuilder( HybsSystem.BUFFER_SMALL );	// 5.5.4.0 (2012/07/02) 休日判定用

		// daySpan	= "0700-2000";	// 開始時刻(HHmm-HHmm) 3.8.0.5 (2005/08/26)
		// 分の少数計算を行います。加算は１時間(0100)単位です。
		int startTime = ym2int( daySpan.substring( 0,2 ),daySpan.substring( 2,4 ) ) ;
		int endTime   = ym2int( daySpan.substring( 5,7 ),daySpan.substring( 7 ) ) ;
		// 開始時刻より終了時刻が同じか小さい場合は、翌日なので2400加算します。
		if( endTime <= startTime ) { endTime += 2400; }

		// 休憩時間を管理する BreakTimes オブジェクトを作成します。
//		BreakTimes breakTimeObj = new BreakTimes( hourOfPixcel,breakTimes,breakTimeStyle,startTime,endTime );
		BreakTimes breakTimeObj = new BreakTimes( hourOfPixcel/step,breakTimes,breakTimeStyle,startTime,endTime );

		// 現在時刻を求めておきます。 現在時刻に５分加算しておきます。(余計なお世話?)
		Calendar rightNow = Calendar.getInstance();
		rightNow.add( Calendar.MINUTE,5 );		// ５分 加算
		int now = rightNow.get( Calendar.HOUR_OF_DAY ) ;

		// 3.8.5.0 (2006/02/06)
		boolean isDebug = isDebug();
		if( isDebug ) {
			System.out.println( "startDate=" + startDate );
			System.out.println( "endDate  =" + endDate );
		}

		Calendar str = HybsSystem.getCalendar( startDate );
		Calendar end = HybsSystem.getCalendar( endDate );

		// 3.6.0.0 (2004/09/17) カレンダDB検索機能を追加
		CalendarData calData = CalendarFactory.getCalendarData( calDB,arg1,arg2,arg3,arg4 );

		// １時間=hourOfPixcel(px) なので、１日分のピクセルを求めておく。(休憩時刻表示用)
//		int dayOfPixel = (int)(hourOfPixcel * (endTime-startTime)/100.0 );
//		int dayOfPixel = (int)(hourOfPixcel/step * (endTime-startTime)/100.0 );
		int dayOfPixel = (int)(hourOfPixcel * Math.ceil((endTime-startTime)/100.0/step) ); // 5.5.4.0 (2012/07/02) 切り上げ
		
		// 休憩表示における画面上の表示日数。これが、左のバイアス値として１日分と乗算される。
		int totalDay = 0;

		// １日分の表示範囲の開始時刻とデータの表示開始時刻との差(オフセット)ピクセル
//		int offsetPixel = (int)(hourOfPixcel * (startTime - (int)(str.get( Calendar.HOUR_OF_DAY )*100 + str.get( Calendar.MINUTE )*10.0/6.0))/100.0 );
		int offsetPixel = (int)(hourOfPixcel/step * (startTime - (int)(str.get( Calendar.HOUR_OF_DAY )*100 + str.get( Calendar.MINUTE )*10.0/6.0))/100.0 );

		// 3.8.1.4 (2006/03/13) 時間単位(h)の表示有無を USE_MULTI_CLM と関係させます。
	 	// 3.8.1.6 (2006/04/25) 日や時間の単位表示を止め、２文字分(24ピクセル)化する。
		final String tani ;
		if( useTani ) { tani = ( USE_MULTI_CLM ) ? "  " : "h " ; }
		else {			tani = "";								 }

		boolean modifyFlag = false;
		int cnt = 0;	// 3.8.1.1 (2005/11/21)
		while( str.before( end ) ) {
			if( isDebug ) { System.out.println( "Calendar str =" + str.getTime() ); }	// 3.8.5.0 (2006/02/06)
			
			// 5.5.4.0 (2012/07/02) この場合は枠単位ではなく、日単位で出力する
			// skipHolidayは無視して、範囲内の全てのカレンダーを出力
			if(calData.isHoliday( str )){
				bufcal.append("1"); 
			}
			else{
				bufcal.append("0"); 
			}
			
			// 休日判断を、事業所カレンダを使用:休日なら、日付を進める。
			if( calData.isHoliday( str ) && skipHoliday ) {
				if( isDebug ) { System.out.println( "Holiday =" + str.getTime() ); }	// 3.8.5.0 (2006/02/06)
				str.add(Calendar.DATE, 1);
				continue;
			}

			// 3.8.1.1 (2005/11/21) 日付のツールチップ表示
			String ganttClass = "class=\"GNT_" + cnt++%2 + "\"" ;

			Date dt = str.getTime();
			String title = format1.format( dt );
			buf1.append( "<span " ).append( ganttClass );
			buf1.append( " title=\"" ).append( title ).append( "\">" );
			if( isDebug ) { System.out.println( "title =" + title ); }	// 3.8.5.0 (2006/02/06)

			// 本日かどうかを判断する。
			boolean isToday = calData.isContainedToday( str,1 );

			int tmHd = startTime/100 ;
			int dayOut = tmHd;		// 3.8.1.4 (2006/03/13)
			boolean isTodayNow = false;		// 3.8.9.2 (2007/07/28) 時間ステップ対応

			for( int h=startTime; h<endTime; h+=100 ) {		// 加算は１時間単位
				if( isDebug ) {	// 3.8.5.0 (2006/02/06)
					System.out.println( "hour =" + h );
					System.out.println( "tmHd =" + tmHd );
				}
				// 3.8.9.2 (2007/07/28) 時間ステップ対応
				isTodayNow = isTodayNow || ( isToday && tmHd == now );
				if( ( dayOut - tmHd ) % step != 0 ) { tmHd++; continue; }

				if( tmHd >= 24 ) {
					tmHd = 0;
					// 3.8.1.1 (2005/11/21) 日付のツールチップを次の日に設定します。
					Date dt2 = new Date( str.getTimeInMillis() + 24*3600*1000L );
					title = format1.format( dt2 );
					buf1.append( "</span><span " ).append( ganttClass );
					buf1.append( " title=\"" ).append( title ).append( "\">" );
				}

				// 本日の現在時刻は、青色で示します。
//				if( isToday && tmHd == now ) {
				if( isTodayNow ) {
					buf1.append( BLUE_COLOR );
					bufH.append( BLUE_COLOR );
					modifyFlag = true;
					isTodayNow = false;		// 初期化
				}
				// 4.0.0.0 休日の場合は赤色で表示
				else if( calData.isHoliday( str ) && !skipHoliday ) {
					buf1.append( RED_COLOR );
					bufH.append( RED_COLOR );
					modifyFlag = true;
				}

				// 実質表示日数(totalDay) * １日分のピクセル(dayOfPixel) - オフセット(offsetPixel)
//				String bt = breakTimeObj.makeSpan( totalDay*dayOfPixel-offsetPixel,str,h );
				String bt = breakTimeObj.makeSpan( totalDay*dayOfPixel-offsetPixel,str,h,step );
				if( bt != null ) { buf2.append( bt ); }

				// 時間当たりの文字数は、４文字： 文字列を詰める。
				if( tmHd < 10 ) { buf1.append( '0' ); }		// 桁数あわせ
				buf1.append( tmHd ).append( tani );			// 3.8.1.4 (2006/03/13)

				// 3.8.1.4 (2006/03/13) 日付表示、時刻単位(h)削除、２段組(USE_MULTI_CLM)
			 	// 3.8.1.6 (2006/04/25) 日や時間の単位表示を止め、２文字分(24ピクセル)化する。
				if( USE_MULTI_CLM ) {
					if( ( dayOut - tmHd ) % 6 == 0 ) {
						if( useTani ) { bufH.append( title.substring( 8 ) ).append( "日" ); }
						else {			bufH.append( title.substring( 8 ) );				}
					}
					else {
						if( useTani ) { bufH.append( "    " ); }
						else {			bufH.append( "  " );   }
					}
				}

				if( modifyFlag ) {
					buf1.append( END_SPAN );
					bufH.append( END_SPAN );
					modifyFlag = false;
				}
				tmHd++;	// ヘッダーに記述する時刻
			}
			str.add(Calendar.DATE, 1);
			totalDay++ ;
			buf1.append( END_SPAN );
			bufH.append( END_SPAN );
		}

		// 3.8.1.4 (2006/03/13) 日付表示、時刻単位(h)削除、２段組(USE_MULTI_CLM)
		buf2.append( "<pre>" );
		buf1.append( "</pre>" );

		if( USE_MULTI_CLM ) {
			buf2.append( bufH.toString() ).append( "<br />" );
		}
		
		calStr = bufcal.toString(); // 5.5.4.0 (2012/07/02)

		return buf2.append( buf1.toString() ).toString() ;
	}
	
	/**
	 * ganttHeaderの属性情報をdisplay:noneで作成します。
	 * ganttHeaderというタグで、id,class共にganttHeaderDataという名称で出力します。
	 *
	 * @og.rev 5.5.4.0 (2012/07/02) 新規作成
	 *
	 * @return  ヘッダー情報の文字列
	 */
	private String makeHeaderData() {
		
		StringBuilder buf1 = new StringBuilder( HybsSystem.BUFFER_SMALL );	

		buf1.append( "\n" )
			.append( "<ganttHeader style='display:none' id='ganttHeaderData' class='ganttHeaderData' " )
			.append( "startDate='" ).append( startDate ).append( "' " )
			.append( "endDate='" ).append( endDate ).append( "' " )
			.append( "zoom='" ).append( zoom ).append( "' " )
			.append( "daySpan='" ).append( daySpan ).append( "' " )
			.append( "calDB='" ).append( calStr ).append( "' " )
			.append( "breakTimes='" ).append( breakTimes ).append( "' " )
			.append( "skipHoliday='" ).append( skipHoliday ).append( "' " )
			.append( " />" );
		

		return buf1.toString();
	}

	/**
	 * 【TAG】表示開始日付けを設定します（yyyyMMdd または、yyyyMMddHHmm 形式）。
	 *
	 * @og.tag
	 * この値は、ヘッダーの先頭に表示する日付けを指定します。
	 * 通常の日付ヘッダーの場合は、 yyyyMMdd 形式ですが、時間ヘッダーの場合は、
	 * yyyyMMddHHmm 形式で指定します。
	 *
	 * @param	val String
	 */
	public void setStartDate( final String val ) {
		startDate = nval( getRequestParameter( val ),startDate );
	}

	/**
	 * 【TAG】表示最終日を設定します（YYYYMMDD または、yyyyMMddHHmm 形式）。
	 *
	 * @og.tag
	 * 表示開始日から終了日までを、zoom 変数の値に応じてヘッダーラベルを
	 * 作成します。
	 * 通常の日付ヘッダーの場合は、 yyyyMMdd 形式ですが、時間ヘッダーの場合は、
	 * yyyyMMddHHmm 形式で指定します。
	 *
	 * @param	val String
	 */
	public void setEndDate( final String val ) {
		endDate = nval( getRequestParameter( val ),endDate );
	}

	/**
	 * 【TAG】１日の開始終了時間を24時間形式(HHmm-HHmm)で設定します（初期値:0700-2000）
	 *
	 * @og.tag
	 * この値は、１日の開始終了時間を指定します。
	 * 開始時刻より終了時刻が小さい場合は、翌日への日またぎとして計算します。
	 * たとえば、夜勤等で、17:00-07:00 という指定が可能です。
	 * 初期値は、朝の7:00から夜の20:00までです。(0700-2000)です。
	 *
	 * @og.rev 3.8.0.5 (2005/08/26) 本日は、青色で示します。
	 *
	 * @param	val String
	 */
	public void setDaySpan( final String val ) {
		daySpan = nval( getRequestParameter( val ),daySpan );
		if( daySpan.length() != 9 || daySpan.charAt(4) != '-' ) {
			String errMsg = "１日の開始終了時間は24時間制で(HHmm-HHmm)形式で指定してください。"
					+ " value=[" + val + "]" 
					+ " daySpan=[" + daySpan + "]" ;		// 5.1.8.0 (2010/07/01) errMsg 修正
			throw new HybsSystemException( errMsg );
		}
	}

	/**
	 * 【TAG】ズーム変数（0 ～ 9)を設定します(初期値:2)。
	 *
	 * @og.tag
	 * CSSファイルでの定義と、背景画像との関係で、作成します。
	 * ズーム変数は、CSSファイルや、JavaScript、PL/SQLと連動して、日付間隔
	 * あたりのピクセルを計算して、ヘッダーを作成しています。
	 * また、現在の標準的なクライアントでの換算になる為、高解像度のディスプレイや
	 * 特殊設定された環境では、表示間隔にずれが発生する可能性があります。
	 *
	 *    ZOOM  ラベル        日数ピクセル変換率          ヘッダ上      ヘッダ下    1文字(CSS)
	 *  ==================================================================================
	 *      0 : １週間 日数 * 12 * 4    48   48px =  1日  [02/   ]      [23(EE)]       8px
	 *      1 : ２週間 日数 * 12 * 2    24   24px =  1日  [02/]         [23 ]          8px
	 *      2 : １ヶ月 日数 * 12        12   12px =  1日  [2]           [3]           12px
	 *      3 : ２ヶ月 日数 * 12 / 1.5   8   56px =  7日  [2004/  ]     [02/03  ]      8px
	 *      4 : ３ヶ月 日数 * 12 / 3     4   56px = 14日  [2004/  ]     [02/03  ]      8px
	 *      5 : ４ヶ月 日数 * 12 / 4     3   90px = 30日  [2004/     ]  [02/03     ]   9px
	 *      6 : ６ヶ月 日数 * 12 / 6     2   60px = 30日  [2004/ ]      [02/03 ]      10px
	 *      7 : １年   日数 * 12 / 12    1   90px = 90日  [2004/     ]  [02/03     ]   9px
	 *      8 : ２年   日数 * 12 / 24   0.5  90px =180日  [2004/     ]  [02/03     ]   9px
	 *      9 : ３年   日数 * 12 / 36   0.33 60px =180日  [2004/ ]      [02/03 ]      10px
	 *     10 : 週単位 日数 * 12 / 3.5  3.43 24px =  7日  [19W]         [12 ]          8px
	 *     11 : 予約済み(他物件使用済み)
	 *     12 : 予約済み(他物件使用済み)
	 *     20 :  1時間   時間 * 12 * 4        48 48px = 1時間 [02日]      [07h ]        12px
	 *     21 :  1時間2  時間 * 12 * 2        24 24px = 1時間 [02]        [07]          12px
	 *     22 :  2時間   時間 * 12 * 4  /  2  24 48px = 2時間 [02日]      [07h ]        12px
	 *     23 :  2時間2  時間 * 12 * 2  /  2  12 24px = 2時間 [02]        [07]          12px
	 *     24 :  4時間   時間 * 12 * 4  /  4  12 48px = 4時間 [02日]      [07h ]        12px
	 *     25 :  4時間2  時間 * 12 * 2  /  4   6 24px = 4時間 [02]        [07]          12px
	 *     26 :  6時間   時間 * 12 * 4  /  6   8 48px = 6時間 [02日]      [07h ]        12px
	 *     27 :  6時間2  時間 * 12 * 2  /  6   4 24px = 6時間 [02]        [07]          12px
	 *     28 :  8時間   時間 * 12 * 4  /  8   6 48px = 8時間 [02日]      [07h ]        12px
	 *     29 :  8時間2  時間 * 12 * 2  /  8   3 24px = 8時間 [02]        [07]          12px
	 *     30 : 12時間   時間 * 12 * 4  / 12   4 48px =12時間 [02日]      [07h ]        12px
	 *     31 : 12時間2  時間 * 12 * 2  / 12   2 24px =12時間 [02]        [07]          12px
	 *
	 * @param	val String
	 */
	public void setZoom( final String val ) {
		zoom = nval( getRequestParameter( val ),zoom );
	}

	/**
	 * 【TAG】DB検索するＤＢを指定します。
	 *
	 * @og.tag
	 * カレンダデータは、システムパラメータ の CalendarQuery_XXXX で
	 * 指定する、CalendarQuery クラスの QUERY 文で、実際に取得します。
	 * 上記の XXXX 部分に、ここで指定するキーワードを設定して置きます。
	 * 通常は、データベースID (GE13, ZY01 など)を指定します。
	 * このキーワードに対する実装クラスを、先のシステムパラメータ の
	 * キーワード以下に記述しておきます。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) DB検索するＤＢのキー
	 *
	 * @param	db String DB検索するＤＢを指定
	 */
	public void setCalDB( final String db ) {
		calDB = nval( getRequestParameter( db ),calDB );
	}

	/**
	 * 【TAG】時間軸(zoom=20)での休憩時間の表示スタイルを設定します。
	 *
	 * @og.tag
	 * 通常は、ヘッダーのトップからの位置と、色です。
	 * それ以外にも、スタイルシート属性(キー:値;) というセットを指定できます。
	 * 初期値は、top:20px;background-color:yellow; です。
	 * 設定の後ろに、; を付ける様にしてください。
	 *
	 * @og.rev 3.8.0.5 (2005/08/26) 新規追加
	 *
	 * @param	style String 休憩時間の表示スタイル
	 */
	public void setBreakTimeStyle( final String style ) {
		breakTimeStyle = nval( getRequestParameter( style ),breakTimeStyle );
	}

	/**
	 * 【TAG】時間軸(zoom=20)での休憩時間の開始-終了時刻をカンマ区切りで複数指定します。
	 *
	 * @og.tag
	 * 休憩時間は、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、または、
	 * yyyyMMddHHmm-yyyyMMddHHmm形式で複数指定できます。
	 * これは、開始-終了というハイフン形式でつなげます。
	 * このセットを、カンマで複数指定できます。
	 * 例えば、200508201200-1245,200508201300-1315 という感じです。
	 * - の前後が、４桁の場合は、HHmm-HHmm 形式として、日付に関係なく、常にこの時分
	 * で休憩が入るという認識をします。つまり、開始は、12桁か、4桁の日付形式、
	 * 終了も、12桁か、4桁の日付形式になります。
	 *
	 * @og.rev 3.8.0.5 (2005/08/26) 新規追加
	 * @og.rev 3.8.0.7 (2005/09/20) 日付またがり追加追加
	 *
	 * @param	times String 休憩時間の表示スタイル
	 */
	public void setBreakTimes( final String times ) {
		breakTimes = nval( getRequestParameter( times ),null );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第１のキーを指定します。
	 *
	 * @og.tag
	 * 例えば、GE13, ZY01 では、事業所コード(CDJGS)を設定します。
	 * この値は、システムパラメータ の CalendarQuery_XXXX で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) DB検索の第１キー(事業所コードなど）を追加
	 *
	 * @param	arg String DB検索の第１キー(事業所コードなど）
	 */
	public void setArg1( final String arg ) {
		arg1 = nval( getRequestParameter( arg ),arg1 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第２のキーを指定します。
	 *
	 * @og.tag
	 * 例えば、TP652 では、事業部コード(CDJGS) と 物件工程コード(CDKTEI)の２つの
	 * キーで、カレンダを取得します。（＋年月で、行を決定）
	 * 引数が、２つまで対応しています
	 * この値は、システムパラメータ の CalendarQuery_XXXX で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) DB検索の第２キー(ＷＣコードなど）を追加
	 *
	 * @param	arg String DB検索の第２キー(ＷＣコードなど）
	 */
	public void setArg2( final String arg ) {
		arg2 = nval( getRequestParameter( arg ),arg2 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第３のキーを指定します。
	 *
	 * @og.tag
	 * この値は、システムパラメータ の CalendarQuery_XXXX で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) DB検索の第３キーを追加
	 *
	 * @param	arg String DB検索の第３キー
	 */
	public void setArg3( final String arg ) {
		arg3 = nval( getRequestParameter( arg ),arg3 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第４のキーを指定します。
	 *
	 * @og.tag
	 * この値は、システムパラメータ の CalendarQuery_XXXX で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 * @og.rev 3.6.0.0 (2004/09/17) DB検索の第４キーを追加
	 *
	 * @param	arg String DB検索の第４キー
	 */
	public void setArg4( final String arg ) {
		arg4 = nval( getRequestParameter( arg ),arg4 );
	}


	/**
	 * 【TAG】時間ガントで休日を表示する/しないを設定します。
	 *
	 * @og.tag
	 * この値をfalseにすると、時間ガントでも休日が赤色で表示
	 * されるようになります。trueにした場合は、休日は表示され
	 * ません。初期値はシステムリソースのUSE_GANTTHEADER_SKIP_HOLIDAYで設定します。
	 *
	 * @og.rev 4.0.0.0 (2007/09/07) 新規作成
	 *
	 * @param	skipHol String 休日をスキップするかどうか。
	 */
	public void setSkipHoliday( final String skipHol) {
		skipHoliday = nval( getRequestParameter( skipHol ),skipHoliday );
	}

	/**
	 * 日付、時刻の文字列を時間整数に変換します。
	 *
	 * 時間を扱う場合は、厄介なのは、時間と分の単位の違いです。
	 * 分に換算してしまうと、時間の取得に６０進数で扱う必要が出てきます。
	 * そこで、単純に時間に換算するのではなく、１時間を100として、
	 * 分を０から９９までの整数に換算して計算しています。
	 * これにより時間の足し算や引き算が可能になり、かつ、時間を求めるのに
	 * 100で割るだけで求めることが可能になります。
	 *
	 * @param yy String 時間の文字列
	 * @param mm String 分の文字列
	 * @return １時間を100として、分を０から９９までの整数に換算した値
	 */
	private static int ym2int( final String yy,final String mm ) {
		return (int)(Integer.parseInt( yy ) * 100 + Integer.parseInt( mm )*10.0/6.0 ) ;
	}

	/**
	 * 休憩時間のデータを管理する内部クラスです。
	 *
	 * @og.rev 3.8.0.7 (2005/09/20) 内部クラス新規作成
	 * @og.group 画面部品
	 *
	 * @version  4.0
	 * @author	Kazuhiko Hasegawa
	 * @since    JDK5.0,
	 */
	private static class BreakTimeData {
		private final String startDay	;		// 開始日付。null の場合は、毎日
		private final int	 startTime	;		// 開始時刻のym2int変換済み値
		private final int	 timeSpan	;		// 開始時刻と終了時刻の時間差(ym2int変換済み)
		private final String viewTime	;		// ツールチップ表示用の文字列(HHmm-HHmm)

		/**
		 * コンストラクター
		 *
		 * 開始日付(startDay)、開始時刻(startTime)、時間差(timeSpan)、表示ラベル(viewTime)
		 * を指定して、休憩時間のデータオブジェクトを構築します。
		 * 開始日付(startDay)は、null の場合は、毎日、つまり、定常的な休憩時刻として
		 * 認識します。日付が指定された場合は、その日のみの特別な休憩時間と認識します。
		 * 開始時刻は、ym2int変換済み値で、１時間が100となり、分は、10進数に変換されている
		 * 値として認識します。7：30 の場合は、750 という値になります。
		 * 開始時刻と終了時刻の時間差(timeSpan)も ym2int変換済み値です。
		 * 表示用の文字列(viewTime)は、休憩時間の HTML表記時の title 属性として
		 * 設定される為、マウスオーバー時のツールチップに現れます。
		 *
		 * @param startDay String yyyyMMdd 形式の開始日付。null の場合は、毎日
		 * @param startTime int 開始時刻のym2int変換済み値
		 * @param timeSpan  int 開始時刻と終了時刻の時間差(ym2int変換済み)
		 * @param viewTime String ツールチップ表示用の文字列(HHmm-HHmm)
		 * @return 含まれる(対象)場合は、true そうでない場合は、false
		 */
		BreakTimeData( final String startDay,final int startTime,final int timeSpan,final String viewTime ) {
			this.startDay	= startDay;
			this.startTime	= startTime;
			this.timeSpan	= timeSpan;
			this.viewTime	= viewTime;
		}

		/**
		 * 指定の日付と時刻が、この BreakTimeData の開始日時とマッチするかどうかをチェックします。
		 *
		 * 開始日時とマッチするとは、内部日付がマッチし、開始時刻が、指定の time の
		 * １時間(+100)*stepの範囲に含まれている場合を指します。(true を返します。)
		 * 内部の日付(startDay)が null の場合は、毎日が対象となります。
		 * 開始時刻がマッチした場合は、timeSpan 分だけの幅を持った休憩時間を認識します。
		 * 境界条件の都合で、timeSpan == 0 の状態も存在しますが、これはマッチしないと判断します。
		 *
		 * @og.rev 3.8.1.1 (2005/11/21) timeSpan が 0 より大きい場合 条件追加
		 * @og.rev 3.8.9.2 (2007/07/28) 時間間隔パラメータ追加
		 *
		 * @param inDay String yyyyMMdd 形式の日付。
		 * @param time int 休憩時間の開始時刻のym2int変換済み値
		 * @param step int 時間間隔
		 * @return 含まれる(対象)場合は、true そうでない場合は、false
		 */
//		boolean startsWith( final String inDay,final int time ) {
		boolean startsWith( final String inDay,final int time,final int step ) {
			return ( timeSpan > 0 &&
					 time <= startTime &&
					 startTime < time+100*step &&
					 ( startDay == null || inDay.equals( startDay ) ) );
		}

		/**
		 * 休憩時間を表す SPANタグを作成します。
		 *
		 * １時間=hourOfPixcel(px) で換算します。分は、すでに分数計算(ym2int)済み
		 *
		 * @og.rev 3.8.8.4 (2007/02/17) class="breaktime"  追加
		 *
		 * @param offset int 表示開始ピクセル数(実質表示日数 * １日分のピクセル - オフセット)
		 * @param hourOfPixcel int １時間あたりのピクセル数
		 * @param breakTimeStyle String 休憩時間の指定に追加するスタイル属性
		 * @return 休憩時間を表す SPANタグ
		 */
		String getBreakTime( final int offset,final int hourOfPixcel,final String breakTimeStyle ) {
			int left  = offset + (startTime*hourOfPixcel/100) ;
			int width = (timeSpan*hourOfPixcel/100) ;

			return "<span class=\"breaktime\" title=\"" + viewTime + "\""
					+ " style=\"position:absolute;z-index:100;height:500000px;"
					+ "left:"	+ left	+ "px;"
					+ "width:"	+ width	+ "px;"
					+ breakTimeStyle	+ "\" ></span>"
					+ HybsSystem.CR ;
		}
	}

	/**
	 * 休憩時間を管理する内部クラスです。
	 *
	 * @og.rev 3.8.0.7 (2005/09/20) 内部クラスBreakTimeDataを使用するように修正
	 * @og.rev 3.8.1.6 (2006/04/25) 変数の final 化
	 * @og.group 画面部品
	 *
	 * @version  4.0
	 * @author	Kazuhiko Hasegawa
	 * @since    JDK5.0,
	 */
	private static class BreakTimes {
		private final List<BreakTimeData>	timeList	= new ArrayList<BreakTimeData>();
		private final int		hourOfPixcel	; // １時間あたりのピクセル数(例:TIME_PIXEL)
		private final String	breakTimeStyle	; // 休憩時間の指定に追加するスタイル属性
		private final int		viewStartTime	; // 数値化(ym2int)された表示開始時刻(例:0700)
		private final int		viewEndTime		; // 数値化(ym2int)された表示終了時刻(例:2000)

		/**
		 * コンストラクター
		 * 必要な情報を設定して、オブジェクトを構築します。
		 * 表示終了時刻は、表示開始時刻 >= 表示終了時刻 の場合、2400加算されている。
		 *
		 * @og.rev 3.8.0.7 (2005/09/20) breakTimesの日付またがり追加
		 * @og.rev 3.8.1.1 (2005/11/21) timeSpan の計算方法を見直し
		 *
		 * @param hOfpx int １時間あたりのピクセル数
		 * @param breakTimes String 休憩時間を、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、または、yyyyMMddHHmm-yyyyMMddHHmm形式で指定します。
		 * @param style String 休憩時間の指定に追加するスタイル属性
		 * @param vStartTime int 数値化(ym2int)された表示開始時刻
		 * @param vEndTime int 数値化(ym2int)された表示終了時刻
		 */
		BreakTimes( final int hOfpx,final String breakTimes,final String style,
					final int vStartTime,final int vEndTime ) {
			hourOfPixcel	= hOfpx;
			breakTimeStyle	= ( style == null ) ? "" : style ;
			viewStartTime	= vStartTime;
			viewEndTime		= vEndTime;
			boolean time24		= ( viewEndTime > 2400 );	// 3.8.1.1 (2005/11/21) 開始終了が逆転している場合 true

		 	// 3.8.1.1 (2005/11/21) timeSpan の計算方法を見直し
			DateFormat format = new SimpleDateFormat( "yyyyMMdd",Locale.JAPAN );
			String[] startEnds = StringUtil.csv2Array( breakTimes );
			int len = startEnds.length;

			String	startDay	;		// null の場合は、毎日
			String	endDay		;		// 一時変数
			int		startTime	;		// 開始時刻のym2int変換済み値
			int		endTime		;		// 終了時刻のym2int変換済み値
			int		timeSpan	;		// 開始時刻と終了時刻の時間差(ym2int変換済み値)
			String	viewTime	;		// ツールチップ表示用の文字列(HH:mm-HH:mm)

			Calendar str ;
			Calendar end ;

			for( int i=0; i<len; i++ ) {
				String startEnd = startEnds[i];
				if( startEnd != null ) {
					// startEnd	= "0700-2000";	// 開始-終了時刻(HHmm-HHmm)
					if( startEnd.length() == 9 && startEnd.charAt(4) == '-' ) {
						startDay	= null;
						endDay		= null;
						startTime	= ym2int( startEnd.substring( 0,2 ),startEnd.substring( 2,4 ) );
						endTime		= ym2int( startEnd.substring( 5,7 ),startEnd.substring( 7 ) );
					}
					// startEnd	= "200508200700-2000";	// 開始-終了時刻(yyyyMMddHHmm-HHmm)
					else if( startEnd.length() == 17 && startEnd.charAt(12) == '-' ) {
						startDay	= startEnd.substring( 0,8 );
						endDay		= null;
						startTime	= ym2int( startEnd.substring( 8,10  ),startEnd.substring( 10,12 ) );
						endTime		= ym2int( startEnd.substring( 13,15 ),startEnd.substring( 15 ) );
					}
					// startEnd	= "200508200700-200508212000";	// 開始-終了時刻(yyyyMMddHHmm-yyyyMMddHHmm)
					else if( startEnd.length() == 25 && startEnd.charAt(12) == '-' ) {
						startDay	= startEnd.substring( 0,8 );
						endDay		= startEnd.substring( 13,21 );
						startTime	= ym2int( startEnd.substring( 8,10  ),startEnd.substring( 10,12 ) );
						endTime		= ym2int( startEnd.substring( 21,23 ),startEnd.substring( 23 ) );
					}
					else {
						String errMsg = "休憩時間は、HHmm-HHmm形式、または、yyyyMMddHHmm-HHmm、または、yyyyMMddHHmm-yyyyMMddHHmm形式です。"
									+ HybsSystem.CR
									+ "breakTimes=[" + breakTimes + "] Bad=[" + startEnd + "]" ;
						throw new HybsSystemException( errMsg );
					}

					str = null;
					end = null;
					if( startDay != null ) { str = HybsSystem.getCalendar( startDay ); }
					if( endDay   != null ) { end = HybsSystem.getCalendar( endDay   ); }

					// 表示終了時刻 が 2400 より大きい場合、表示開始時刻 >= 表示終了時刻 である。
					if( time24 ) {
						// 開始時刻 <  表示終了時刻 => 開始時刻に2400加算する。
						if( startTime < viewEndTime-2400 ) {
							if( str != null ) {
								str.add(Calendar.DATE, -1 );	// -1 日しておく
								startDay = format.format( str.getTime() );
							}
							startTime += 2400;
						}

						// 終了時刻 <= 表示終了時刻 => 終了時刻に2400加算する。
						if( endTime <= viewEndTime-2400 ) {
							if( end != null ) {
								end.add(Calendar.DATE, -1 );	// -1 日しておく
								endDay = format.format( end.getTime() );
							}
							endTime += 2400;
						}
					}

					// 3.8.1.1 (2005/11/21) 判定条件修正
					// 開始時刻 < 表示開始時刻 => 開始時刻に表示開始時刻をセット。
					if( startTime < viewStartTime ) { startTime = viewStartTime; }

					// 開始時刻 > 表示終了時刻 => 開始時刻に表示終了時刻をセット。
					if( startTime > viewEndTime ) { startTime = viewEndTime; }

					// 終了時刻 < 表示開始時刻 => 終了時刻に表示開始時刻をセット。
					if( endTime < viewStartTime ) { endTime = viewStartTime; }

					// 終了時刻 > 表示終了時刻 => 終了時刻に表示終了時刻をセット。
					if( endTime > viewEndTime ) { endTime = viewEndTime; }
					timeSpan = endTime - startTime ;
					viewTime = startEnd ;
				}
				// null の場合は、カンマが連続したり、最後に余分に付いたケースなので、無視する。
				else {
					continue;
				}

				// 終了日が入っていないか、開始日と終了日が同じ日の場合。
				if( timeSpan >= 0 && (
					endDay == null || ( startDay != null && startDay.equals( endDay ) ) ) ) {
						timeSpan = endTime - startTime ;
						timeList.add( new BreakTimeData( startDay,startTime,timeSpan,viewTime ) );
				}
				else {
					// 終了日が入っていると複数日に分かれているので、分解する必要がある。

					// 初日の timeSpan は、startTime から viewEndTime まで
					timeSpan = viewEndTime - startTime ;
					timeList.add( new BreakTimeData( startDay,startTime,timeSpan,viewTime ) );

					// 最終日の timeSpan は、viewStartTime から endTime まで
					timeSpan = endTime - viewStartTime ;
					timeList.add( new BreakTimeData( endDay,viewStartTime,timeSpan,viewTime ) );

					// 中日の timeSpan は、viewStartTime から viewEndTime まで
					timeSpan = viewEndTime - viewStartTime ;
					if( str != null && end != null ) {
						str.add(Calendar.DATE, 1 );		// +1 日しておく
						while( str.before( end ) ) {
							Date dt = str.getTime();
							String tempStartDay = format.format( dt );
							timeList.add( new BreakTimeData( tempStartDay,viewStartTime,timeSpan,viewTime ) );
							str.add(Calendar.DATE, 1);
						}
					}
				}
			}
		}

		/**
		 * その日のその時間の休憩時間を表す SPANタグ群(複数ありうる)を作成します。
		 *
		 * @og.rev 3.8.9.2 (2007/07/28) 時間間隔パラメータ追加
		 *
		 * @param offset int 表示開始ピクセル数(実質表示日数 * １日分のピクセル - オフセット)
		 * @param day Calendar 処理すべき日付のカレンダ
		 * @param time int 処理すべき時間
		 * @param step int 時間間隔
		 * @return その日のその時間の休憩時間を表す SPANタグ群(複数ありうる)
		 */
//		String makeSpan( final int offset,final Calendar day,final int time ) {
		String makeSpan( final int offset,final Calendar day,final int time,final int step ) {
			DateFormat format1 = new SimpleDateFormat( "yyyyMMdd",Locale.JAPAN );
			String inDay = format1.format( day.getTime() );

			StringBuilder buf = new StringBuilder();
			int size = timeList.size();
			for( int i=0; i<size; i++ ) {
				BreakTimeData timeData = timeList.get(i);
//				if( timeData.startsWith( inDay,time ) ) {
				if( timeData.startsWith( inDay,time,step ) ) {
					buf.append( timeData.getBreakTime( offset,hourOfPixcel,breakTimeStyle ) );
				}
			}

			String rtn = null;

			if( buf.length() > 0 ) { rtn = buf.toString(); }
			return rtn;
		}
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 */
	public String toString() {
		return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
				.println( "VERSION"			,VERSION		)
				.println( "startDate"		,startDate		)
				.println( "endDate"			,endDate		)
				.println( "daySpan"			,daySpan		)
				.println( "zoom"			,zoom			)
				.println( "breakTimes"		,breakTimes		)
				.println( "breakTimeStyle"	,breakTimeStyle	)
				.println( "calDB"			,calDB			)
				.println( "arg1"			,arg1			)
				.println( "arg2"			,arg2			)
				.println( "arg3"			,arg3			)
				.println( "arg4"			,arg4			)
				.println( "skipHoliday"		,skipHoliday	)
				.println( "TIME_PIXEL" 		,TIME_PIXEL 	)
				.println( "Other..."		,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
