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 org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.StringUtil; 021import org.opengion.hayabusa.db.DBTableModel; 022import org.opengion.hayabusa.html.TableFormatter; 023import org.opengion.hayabusa.html.ViewGanttTableParam; 024 025import java.util.regex.Pattern; 026import java.util.regex.Matcher; 027 028import java.util.Locale; 029import java.util.TreeSet; 030import java.util.List; 031import java.util.Date; 032import java.util.Calendar; 033import java.text.SimpleDateFormat; 034import java.text.ParseException; 035 036/** 037 * ガントチャート(テーブル形式)を作成する、ガントチャート表示クラスです。 038 * 039 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。 040 * 各HTMLのタグに必要な setter/getterメソッドのみ,追加定義しています。 041 * 042 * AbstractViewForm を継承している為,ロケールに応じたラベルを出力させる事が出来ます。 043 * 044 * @og.group 画面表示 045 * 046 * @version 4.0 047 * @author Kazuhiko Hasegawa 048 * @since JDK5.0, 049 */ 050public class ViewForm_HTMLGanttTable extends ViewForm_HTMLTable { 051 //* このプログラムのVERSION文字列を設定します。 {@value} */ 052 private static final String VERSION = "5.5.4.4 (2012/07/20)" ; 053 054 // 3.5.4.0 (2003/11/25) TableFormatter クラス追加 055 private TableFormatter headerFormat = null; 056 private TableFormatter[] bodyFormats = null; 057 private TableFormatter footerFormat = null; 058 private int bodyFormatsCount = 0; 059 060 // 繰り返すTD用 3.5.6.0 (2004/06/18) 廃止 061 private TableFormatter[] itdFormats = null; // 追加 062 063 private String ganttHeadLine = null; 064 private int[] groupCols = null; 065 private int posDuration = -1; 066 private int posDystart = -1; 067 068 private String formatDystart = ViewGanttTableParam.DYSTART_FORMAT_VALUE; 069 private double minDuration = StringUtil.parseDouble( ViewGanttTableParam.MIN_DURATION_VALUE ) ; // 3.5.5.8 (2004/05/20) 070 private double headerDuration = minDuration ; // 3.5.5.8 (2004/05/20) 071 072 // 3.5.4.6 (2004/01/30) 初期値変更 073 private static final int BODYFORMAT_MAX_COUNT = 10; 074 075 // <(td|th)(.*)>(.*)</(td|th)>を確認する 076 private static final Pattern cpTdTh = 077 Pattern.compile("\\<(td|th)([^\\>]*)\\>(.*)\\</(td|th)\\>" 078 , Pattern.DOTALL + Pattern.CASE_INSENSITIVE); 079 // 3.5.5.9 (2004/06/07) 080 private int maxDayCnt = 0; 081 private String headerLocale = null; 082 private String[] headDays = null; 083 private int headDaysCnt = 0; 084 085 // 3.5.6.3 (2004/07/12) 行チェックによる編集用の hidden 086 private static final String CHECK_ROW = 087 "<input type=\"hidden\" name=\"" + HybsSystem.ROW_SEL_KEY + "\" value=\""; 088 089 // 3.6.1.0 (2005/01/05) 開始日付けと終了日付けの指定 090 private boolean useSeqDay = false; 091 private String startDay = null; 092 private String endDay = null; 093 094 private boolean useItd = false; // 5.0.0.3 (2009/09/22) 095 096 // 4.3.4.4 (2009/01/01) 097// /** 098// * デフォルトコンストラクター 099// * 100// */ 101// public ViewForm_HTMLGanttTable() { 102// super(); 103// } 104 105 /** 106 * 内容をクリア(初期化)します。 107 * 108 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 109 * @og.rev 3.5.0.0 (2003/09/17) Noカラムに、表示を全て消せるように、class 属性を追加。 110 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 111 * @og.rev 3.5.5.8 (2004/05/20) minDuration , headerDuration 追加。 不要な変数削除 112 * @og.rev 3.5.6.0 (2004/06/18) ithFormat , itdFormat 属性削除、itdFormats属性を追加 113 * @og.rev 3.6.1.0 (2005/01/05) startDay,endDay,useSeqDay 属性追加 114 * @og.rev 5.0.0.3 (2009/09/22) itdタグの有無でcolspan対策のtdの出力個数を調整 115 */ 116 @Override 117 public void clear() { 118 super.clear(); 119 120 headerFormat = null; 121 bodyFormats = null; 122 footerFormat = null; 123 bodyFormatsCount = 0; 124 125 ganttHeadLine = null; 126 itdFormats = null; // 3.5.6.0 (2004/06/18) 127 groupCols = null; 128 posDuration = -1 ; 129 posDystart = -1; 130 formatDystart = ViewGanttTableParam.DYSTART_FORMAT_VALUE; 131 minDuration = StringUtil.parseDouble( ViewGanttTableParam.MIN_DURATION_VALUE ) ; // 3.5.5.8 (2004/05/20) 132 headerDuration = minDuration ; // 3.5.5.8 (2004/05/20) 133 134 maxDayCnt = 0; // 3.5.5.9 (2004/06/07) 135 headerLocale = null; // 3.5.5.9 (2004/06/07) 136 headDays = null; // 3.5.5.9 (2004/06/07) 137 headDaysCnt = 0; // 3.5.5.9 (2004/06/07) 138 139 useSeqDay = false; // 3.6.1.0 (2005/01/05) 140 startDay = null; // 3.6.1.0 (2005/01/05) 141 endDay = null; // 3.6.1.0 (2005/01/05) 142 143 useItd = false; // 5.0.0.3 (2009/09/22) 144 } 145 146 /** 147 * DBTableModel から HTML文字列を作成して返します。 148 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。 149 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。 150 * 151 * @og.rev 3.5.0.0 (2003/09/17) BODY要素の noClass 属性を追加。 152 * @og.rev 3.5.0.0 (2003/09/17) <tr>属性は、元のフォーマットのまま使用します。 153 * @og.rev 3.5.2.0 (2003/10/20) ヘッダー繰り返し属性( headerSkipCount )を採用 154 * @og.rev 3.5.3.1 (2003/10/31) skip属性を採用。headerLine のキャッシュクリア 155 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 156 * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応 157 * @og.rev 3.5.5.9 (2004/06/07) IEの colspan が上手く動かない対策。 158 * @og.rev 3.5.5.9 (2004/06/07) [#カラム名] , [$カラム名] に対応 159 * @og.rev 3.5.6.0 (2004/06/18) itdFormat を、BODY毎のFormatを使用するように修正 160 * @og.rev 3.5.6.0 (2004/06/18) '!' 値のみ 追加 既存の '$' は、レンデラー 161 * @og.rev 3.5.6.3 (2004/07/12) 行チェックによる編集が出来るように機能を追加 162 * @og.rev 3.5.6.4 (2004/07/16) ヘッダーとボディー部をJavaScriptで分離 163 * @og.rev 3.6.1.0 (2005/01/05) 行チェックによる編集が、検索即登録時も可能なようにします。 164 * @og.rev 4.0.0.0 (2005/01/31) 新規作成(getColumnClassName ⇒ getColumnDbType) 165 * @og.rev 3.7.0.1 (2005/01/31) E の colspan バグ対応で入れた最終行の 空タグを消す為の修正 166 * @og.rev 4.3.1.0 (2008/09/08) フォーマットが設定されていない場合のエラー追加 167 * @og.rev 4.3.7.4 (2009/07/01) tbodyタグの入れ子を解消(FireFox対応) 168 * @og.rev 5.0.0.3 (2009/09/22) itdタグの有無でcolspan対策のtdの出力個数を調整 169 * @og.rev 5.5.4.4 (2012/07/20) 二重チェック状態になってしまう対策 170 * 171 * @param stNo 表示開始位置 172 * @param pgSize 表示件数 173 * 174 * @return DBTableModelから作成された HTML文字列 175 */ 176 @Override 177 public String create( final int stNo, final int pgSize ) { 178 // ガントは、キーブレイクがあるため、全件表示します。 179 int startNo = 0; 180 int pageSize = getRowCount() ; 181 if( pageSize == 0 ) { return ""; } // 暫定処置 182 boolean outputCheck = true; // 5.5.4.4. (2012/07/20) チェックボックス出力判定 183 184 // 4.3.1.0 (2008/09/08) 185 if( headerFormat == null ) { 186 String errMsg = "ViewTagで canUseFormat() = true の場合、Formatter は必須です。"; 187 throw new HybsSystemException( errMsg ); 188 } 189 190 headerLine = null; // 3.5.3.1 (2003/10/31) キャッシュクリア 191 192 int lastNo = getLastNo( startNo, pageSize ); 193 int blc = getBackLinkCount(); 194 int hsc = getHeaderSkipCount(); // 3.5.2.0 (2003/10/20) 195 int hscCnt = 1; // 3.5.2.0 (2003/10/20) 196 197 // このビューの特有な属性を初期化 198 paramInit(); 199 200 StringBuilder out = new StringBuilder( HybsSystem.BUFFER_LARGE ); 201 202 out.append( getCountForm( startNo,pageSize ) ); 203 204 if( posDuration < 0 ) { ganttHeadLine = getGanttHeadNoDuration(startNo, lastNo); } 205 else { ganttHeadLine = getGanttHead(startNo, lastNo); } 206 207 out.append( getHeader() ); 208 209 if( bodyFormatsCount == 0 ) { 210 bodyFormats[0] = headerFormat ; 211 bodyFormatsCount ++ ; 212 } 213 else { 214 for( int i=0; i<bodyFormatsCount; i++ ) { 215 bodyFormats[i].makeFormat( getDBTableModel() ); 216 if( itdFormats[i] != null ) { 217 itdFormats[i].makeFormat( getDBTableModel() ); 218 } 219 } 220 } 221// out.append("<tbody>").append( HybsSystem.CR ); // 4.3.7.4 (2009/07/01) 222 223 String[] astrOldGroupKeys = new String[groupCols.length]; 224 for( int nIndex =0; nIndex < astrOldGroupKeys.length; nIndex++) { 225 astrOldGroupKeys[nIndex] = ""; 226 } 227 228 StringBuilder bodyBuf = null, itdBuf = null; 229 boolean checked = false; // 3.5.6.3 (2004/07/12) 230 int bgClrCnt = 0; 231 TableFormatter itdFormat = null; 232 for( int row=startNo; row<lastNo; row++ ) { 233 outputCheck = true; // 5.5.4.4. (2012/07/20) 234 // ガントでは、データのスキップは行いません。 235 236 if(! isSameGroup(row, astrOldGroupKeys)) { 237 // 3.5.6.3 (2004/07/12) キーブレイク時にチェック行かどうかを記録 238 checked = getDBTableModel().isRowChecked( row ); 239 240 if( row != startNo ) { 241 // ヘッダー日付けが残っているのに、データがなくなった場合 242 while( headDays != null && headDaysCnt < headDays.length ) { 243 itdBuf.append( "<td></td>" ); 244 headDaysCnt++; 245 } 246 out.append(StringUtil.replace(bodyBuf.toString(), TableFormatter.HYBS_ITD_MARKER, itdBuf.toString())); 247 itdBuf = null; 248 headDaysCnt = 0; 249 } 250 251 bodyBuf = new StringBuilder( HybsSystem.BUFFER_LARGE ); 252 253 // カラムのグループがブレイクするまで、BodyFormatは同一である前提 254 for( int i=0; i<bodyFormatsCount; i++ ) { 255 TableFormatter bodyFormat = bodyFormats[i]; 256 if( ! bodyFormat.isUse( row,getDBTableModel() ) ) { continue; } // 3.5.4.0 (2003/11/25) 257 itdFormat = itdFormats[i]; // 258 259 bodyBuf.append("<tbody").append( getBgColorCycleClass( bgClrCnt++ ) ).append(">"); 260 bodyBuf.append( bodyFormat.getTrTag() ); 261 262 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 263 if( isNumberDisplay() ) { 264 String ckboxTD = "<td" + bodyFormat.getRowspan() + ">"; 265 bodyBuf.append( makeCheckbox( ckboxTD,row,blc ) ); 266 outputCheck = false; // 5.5.4.4. (2012/07/20) 267 } 268 269 int cl = 0; 270 for( ; cl < bodyFormat.getLocationSize(); cl++ ) { 271 String fmt = bodyFormat.getFormat(cl); 272 int loc = bodyFormat.getLocation(cl); // 3.5.5.0 273 if( ! bodyFormat.isNoClass() ) { 274 StringBuilder newtg = new StringBuilder( HybsSystem.BUFFER_LARGE ); 275 newtg.append("<td class=\""); 276 if( loc >= 0 ) { newtg.append(getColumnDbType(loc)); } // 4.0.0 (2005/01/31) 277 newtg.append("\" "); 278 String tdclass = newtg.toString(); 279 fmt = StringUtil.replace( bodyFormat.getFormat(cl) ,"<td", tdclass ); 280 } 281 bodyBuf.append( fmt ); // 3.5.0.0 282 // 3.5.5.9 (2004/06/07) #,$ 対応 283 if( loc >= 0 ) { 284 switch( bodyFormat.getType(cl) ) { 285 case '#' : bodyBuf.append( getColumnLabel(loc) ); break; 286 case '$' : bodyBuf.append( getRendererValue(row,loc) ); break; 287 case '!' : bodyBuf.append( getValue(row,loc) ); break; 288 default : bodyBuf.append( getValueLabel(row,loc) ); break; 289 } 290 } 291 else { 292 bodyBuf.append( bodyFormat.getSystemFormat(row,loc) ); 293 } 294 } 295 bodyBuf.append( bodyFormat.getFormat(cl) ); 296 bodyBuf.append("</tbody>").append( HybsSystem.CR ); 297 } 298 299 // 3.5.2.0 (2003/10/20) ヘッダー繰り返し属性( headerSkipCount )を採用 300 if( hsc > 0 && hscCnt % hsc == 0 ) { 301 bodyBuf.append("<tbody class=\"row_h\"").append(" >"); 302 bodyBuf.append( getHeadLine() ); 303 bodyBuf.append("</tbody>"); 304 hscCnt = 1; 305 } 306 else { 307 hscCnt ++ ; 308 } 309 } 310 311 // 3.5.6.3 (2004/07/12) キーブレイク時のチェック行の状態を繰り返し行に反映 312 // 3.6.1.0 (2005/01/05) さらに、外部でチェックを入れている場合は、個別対応する。 313// if( checked || isChecked( row ) ) { 314 if( (checked || isChecked( row )) && outputCheck ) { // 5.5.4.4. (2012/07/20) 315 getDBTableModel().setRowWritable( row,true ); 316 out.append( CHECK_ROW ); 317 out.append( row ); 318 out.append( "\" />" ); 319 } 320 321 itdBuf = formatItd(row, itdFormat, itdBuf); 322 } 323 324 // 残ったデータを出力 325 if( null != itdBuf ) { 326 // ヘッダー日付けが残っているのに、データがなくなった場合 327 while( headDays != null && headDaysCnt < headDays.length ) { 328 itdBuf.append( "<td></td>" ); 329 headDaysCnt++; 330 } 331 out.append(StringUtil.replace(bodyBuf.toString(), TableFormatter.HYBS_ITD_MARKER, itdBuf.toString())); 332 } 333 334 // 3.5.5.9 (2004/06/07) IEの colspan が上手く動かない対策。 335 // minDuration が、1.0 以下の場合のみ実行 336 if( minDuration < 1.0d ) { 337 // int tdCount = (int)Math.round( maxDayCnt / minDuration ); 338 // 5.0.0.3 (2009/09/22) itdタグの有無でtdCountを変える。 339 int tdCount = useItd ? (int) Math.round( maxDayCnt / minDuration ) : headerFormat.getLocationSize(); 340 341 // 3.7.0.1 (2005/01/31) E の colspan バグ対応で入れた最終行の 空タグを消す為の修正 342 out.append("<tbody><tr class=\"dummy\">").append( HybsSystem.CR ); 343 for( int i=0; i<tdCount; i++ ) { 344 out.append( "<td/>" ); 345 } 346 out.append("</tr></tbody>").append( HybsSystem.CR ); 347 } 348 349 if( footerFormat != null ) { 350 out.append( getTableFoot() ); 351 } 352 353// out.append("</tbody>").append( HybsSystem.CR ); 4.3.7.4 (2009/07/01) 354 out.append("</table>").append( HybsSystem.CR ); 355 356 out.append( getScrollBarEndDiv() ); // 3.8.0.3 (2005/07/15) 357 return out.toString(); 358 } 359 360 /** 361 * このビーに対する特別な初期化を行う。 362 * 363 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 364 * @og.rev 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更 365 * @og.rev 3.6.1.0 (2005/01/05) startDay,endDay,useSeqDay 属性追加 366 */ 367 private void paramInit() { 368 369 String strGroupCols = getParam( ViewGanttTableParam.GROUP_COLUMNS_KEY ,ViewGanttTableParam.GROUP_COLUMNS_VALUE ); 370 String strColDuration = getParam( ViewGanttTableParam.DURATION_COLUMN_KEY ,null ); 371 String strColDystart = getParam( ViewGanttTableParam.DYSTART_COLUMN_KEY ,ViewGanttTableParam.DYSTART_COLUMN_VALUE ); 372 formatDystart = getParam( ViewGanttTableParam.DYSTART_FORMAT_KEY ,ViewGanttTableParam.DYSTART_FORMAT_VALUE ); 373 String strMinDuration = getParam( ViewGanttTableParam.MIN_DURATION_KEY ,ViewGanttTableParam.MIN_DURATION_VALUE ); 374 String strHeadDuration = getParam( ViewGanttTableParam.HEADER_DURATION_KEY ,strMinDuration ); 375 headerLocale = getParam( ViewGanttTableParam.HEADER_LOCALE_KEY ,ViewGanttTableParam.HEADER_LOCALE_VALUE ); 376 startDay = getParam( ViewGanttTableParam.START_DAY_KEY ,null ); // 3.6.1.0 (2005/01/05) 377 endDay = getParam( ViewGanttTableParam.END_DAY_KEY ,null ); // 3.6.1.0 (2005/01/05) 378 379 String seqDay = getParam( ViewGanttTableParam.USE_SEQ_DAY_KEY ,ViewGanttTableParam.USE_SEQ_DAY_VALUE ); // 3.6.1.0 (2005/01/05) 380 useSeqDay = Boolean.valueOf( seqDay ).booleanValue() ; 381 382 DBTableModel table = getDBTableModel(); 383 384 // 3.5.5.9 (2004/06/07) durationColumn を指定しない場合の処理を追加 385 if( strColDuration != null ) { 386 posDuration = table.getColumnNo( strColDuration ); 387 } 388 389 posDystart = table.getColumnNo( strColDystart ); 390 391 String[] groupKeys = StringUtil.csv2Array(strGroupCols); 392 groupCols = new int[groupKeys.length]; 393 for( int nIndex = 0; nIndex < groupCols.length ; nIndex++) { 394 groupCols[nIndex] = table.getColumnNo( groupKeys[nIndex] ); 395 } 396 397 minDuration = StringUtil.parseDouble( strMinDuration ); 398 if( minDuration <= 0.0d ) { 399 String errMsg = "最小期間単位(minDuration)が、0 かそれ以下です。"; 400 throw new HybsSystemException( errMsg ); 401 } 402 403 headerDuration = StringUtil.parseDouble( strHeadDuration ); 404 if( headerDuration <= 0.0d ) { 405 String errMsg = "ヘッダーの表示期間(headerDuration)が、0 かそれ以下です。"; 406 throw new HybsSystemException( errMsg ); 407 } 408 409 // 3.5.5.9 (2004/06/07) エラーチェックの強化 410 // 4.0.0 (2005/01/31) Equality checks with floating point numbers can lead to unexpected behavior. 411 if( posDuration < 0 && ( 412 Double.compare( minDuration,1.0d ) != 0 || 413 Double.compare( headerDuration,1.0d ) != 0 ) ) { 414 415 String errMsg = "期間カラム(durationColumn)を指定しない場合は、" 416 + "最小期間単位(minDuration)および、" 417 + "ヘッダーの表示期間(headerDuration)を '1' 以外に設定できません。"; 418 throw new HybsSystemException( errMsg ); 419 } 420 } 421 422 /** 423 * 上下行のデータが同じグルプかどうかをチェックする。 424 * 425 * @param nRowIndex テーブルモデルの行番号 426 * @param astrOldValues 古いグルプデータ 427 * 428 * @return 使用可能(true)/ 使用不可能 (false) 429 */ 430 private boolean isSameGroup(final int nRowIndex, final String[] astrOldValues) { 431 boolean bRet = (groupCols.length > 0); 432 if( bRet ) { 433 for( int nIndex = 0; bRet && ( nIndex < groupCols.length ); nIndex++) { 434 bRet = ( astrOldValues[nIndex].equals(getValue(nRowIndex, groupCols[nIndex]))); 435 } 436 437 // 不一致時に astrOldValues に 新しい値を設定しておきます。 438 if(!bRet) { 439 for( int nIndex = 0; nIndex < groupCols.length; nIndex++) { 440 astrOldValues[nIndex] = getValue(nRowIndex, groupCols[nIndex]); 441 } 442 } 443 } 444 445 return bRet; 446 } 447 448 /** 449 * DBTableModel から テーブルのタグ文字列を作成して返します。 450 * 451 * @og.rev 3.5.0.0 (2003/09/17) <tr>属性は、元のフォーマットのまま使用します。 452 * @og.rev 3.5.1.0 (2003/10/03) Noカラムに、numberType 属性を追加 453 * @og.rev 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動 454 * @og.rev 3.5.3.1 (2003/10/31) VERCHAR2 を VARCHAR2 に修正。 455 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 456 * @og.rev 3.5.6.5 (2004/08/09) thead に、id="header" を追加 457 * @og.rev 4.0.0.0 (2005/01/31) DBColumn の 属性(CLS_NM)から、DBTYPEに変更 458 * @og.rev 5.9.1.2 (2015/10/23) 自己終了警告対応 459 * @og.rev 5.9.3.3 (2015/12/26) colgroup対応 460 * 461 * @return テーブルのタグ文字列 462 */ 463 @Override 464 protected String getTableHead() { 465 headerFormat.makeFormat( getDBTableModel() ); 466 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 467 468 // 5.9.3.3 (2015/12/26) HTML5 で colgroup が効かない対応 469 if( !useIE7Header ) { 470 buf.append( "<style type=\"text/css\">" ) 471 .append( HybsSystem.CR ); 472 if( isNumberDisplay() ) { 473 makeNthChild( buf,2,"BIT" ); 474 makeNthChild( buf,3,"S9" ); 475 } 476 buf.append( "</style>" ) 477 .append( HybsSystem.CR ); 478 } 479 480 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 481 if( isNumberDisplay() ) { 482// buf.append("<colgroup class=\"X\" />"); // 4.0.0 (2005/01/31) 483// buf.append("<colgroup class=\"BIT\" />"); 484// buf.append("<colgroup class=\"S9\" />"); // 4.0.0 (2005/01/31) 485 buf.append("<colgroup class=\"X\" ><!-- --></colgroup>"); // 4.0.0 (2005/01/31) 486 buf.append("<colgroup class=\"BIT\" ><!-- --></colgroup>"); 487 buf.append("<colgroup class=\"S9\" ><!-- --></colgroup>"); // 4.0.0 (2005/01/31) 488 buf.append(HybsSystem.CR); 489 } 490 491 // 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動 492 buf.append("<thead id=\"header\">").append( HybsSystem.CR ); // 3.5.6.5 (2004/08/09) 493 buf.append( getHeadLine() ); 494 buf.append("</thead>").append( HybsSystem.CR ); 495 496 return buf.toString(); 497 } 498 499 /** 500 * ヘッダー繰り返し部を、getTableHead()メソッドから分離。 501 * 502 * @og.rev 3.5.2.0 (2003/10/20) 新規作成 503 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 504 * @og.rev 3.5.4.3 (2004/01/05) useCheckControl 属性の機能を追加 505 * @og.rev 3.5.4.6 (2004/01/30) numberType="none" 時の処理を追加(Noラベルを出さない) 506 * @og.rev 3.5.4.7 (2004/02/06) ヘッダーにソート機能用のリンクを追加します。 507 * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応 508 * @og.rev 3.7.0.1 (2005/01/31) 全件チェックコントロール処理変更 509 * @og.rev 5.0.0.3 (2009/09/22) itdの有無を取得します。 510 * 511 * @return テーブルのタグ文字列 512 */ 513 @Override 514 protected String getHeadLine() { 515 if( headerLine != null ) { return headerLine; } // キャッシュを返す。 516 517 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 518 519 buf.append( headerFormat.getTrTag() ).append( HybsSystem.CR ); 520 521 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 522 if( isNumberDisplay() ) { 523 // 3.5.4.3 (2004/01/05) 追加分 524 if( isUseCheckControl() && "checkbox".equals( getSelectedType() ) ) { 525 buf.append(" <th" ).append( headerFormat.getRowspan() ).append("></th>"); 526 buf.append(" <th" ).append( headerFormat.getRowspan() ); 527 buf.append(">").append( getAllCheckControl() ).append( "</th>"); 528 buf.append(" <th" ).append( headerFormat.getRowspan() ); 529 buf.append(">").append( getNumberHeader() ).append("</th>"); // 3.5.4.6 (2004/01/30) 530 } 531 else { 532 buf.append(" <th colspan=\"3\""); 533 buf.append( headerFormat.getRowspan() ); 534 buf.append(">").append( getNumberHeader() ).append("</th>"); // 3.5.4.6 (2004/01/30) 535 } 536 } 537 538 int cl = 0; 539 for( ; cl < headerFormat.getLocationSize(); cl++ ) { 540 buf.append( headerFormat.getFormat(cl) ); 541 int loc = headerFormat.getLocation(cl); 542 if( loc >= 0 ) { buf.append( getSortedColumnLabel(loc) ); } 543 } 544 buf.append( headerFormat.getFormat(cl) ).append( HybsSystem.CR ); 545 546 // 5.0.0.3 (2009/09/22) ITD_MARKERの条件判断追加 547 if( buf.indexOf( TableFormatter.HYBS_ITD_MARKER ) >= 0 ){ 548 useItd = true; 549 } 550 headerLine = StringUtil.replace(buf.toString(), TableFormatter.HYBS_ITD_MARKER, ganttHeadLine); 551 552 return headerLine; 553 } 554 555 /** 556 * ガントチャートヘッダー繰り返し部を、getTableHead()メソッドから分離。 557 * このメソッドは、durationColumn を利用して、連続日付けデータを作成します。 558 * データは、開始日と期間データを持ち、ヘッダーは連続日付けになります。 559 * ヘッダーの期間(headerDuration)とデータの最小期間(minDuration)が異なる為、 560 * 最初の行データより、最終日を求め、headerDuration で割り算した個数の連続日数を 561 * 表示させています。 562 * 563 * @og.rev 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更 564 * @og.rev 3.5.6.0 (2004/06/18) ithFormat 変数削除 565 * 566 * @param startNo 開始行番号 567 * @param lastNo 最終行番号 568 * 569 * @return テーブルのタグ文字列 570 */ 571 private String getGanttHead(final int startNo, final int lastNo) { 572 String[] astrOldGroupKeys = new String[groupCols.length]; 573 for( int nIndex =0; nIndex < astrOldGroupKeys.length; nIndex++) { 574 astrOldGroupKeys[nIndex] = ""; 575 } 576 577 // 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更 578 Locale local = new Locale( headerLocale ); 579 SimpleDateFormat fmtDate = new SimpleDateFormat( formatDystart,local ); 580 581 double nSumDuration = 0.0d ; 582 Date dFirst = null ; 583 584 for( int nRowUp = startNo; nRowUp < lastNo; nRowUp++ ) { 585 // ガントでは、データのスキップは行いません。 586 587 // 最初の行 または、ブレイクするまで 588 if( isSameGroup(nRowUp, astrOldGroupKeys) || (nRowUp == startNo) ) { 589 nSumDuration += StringUtil.parseDouble( getValue(nRowUp, posDuration) ); 590 try { 591 if( dFirst == null ) { 592 dFirst = fmtDate.parse(getValue(nRowUp, posDystart)); 593 } 594 } 595 catch ( ParseException ex) { 596 String errMsg = "DYSTARTに指定した日付形式と異なるデータが存在しています。" 597 + "[" + getValue(nRowUp , posDystart) + "] => [" 598 + fmtDate.toPattern() + "]" ; 599 throw new HybsSystemException( errMsg,ex ); 600 } 601 } 602 else { break; } 603 } 604 605 String thVal = "" ; // <td ・・・> の <td 以下の ・・・部分の属性文字列。 606 String ymdForm = "MM/dd" ; // td タグの BODY 部 の 日付けフォーマット 607 608 if( headerFormat != null ) { 609 String format = headerFormat.getItdBody().trim() ; 610 Matcher matcher = cpTdTh.matcher(format); 611 if( matcher.find() ) { 612 thVal = matcher.group(2); 613 ymdForm = matcher.group(3); 614 } 615 } 616 617 try { 618 fmtDate.applyPattern(ymdForm); 619 } 620 catch(IllegalArgumentException eArg) { 621 String errMsg = "theadの内のitdの内側に指定された日付の形式が不正です。(" + ymdForm + ")"; 622 throw new HybsSystemException( errMsg,eArg ); 623 } 624 625 int colspan = (int)Math.round( headerDuration / minDuration ); 626 final String th ; 627 if( colspan == 1 ) { th = "<th " + thVal + ">"; } 628 else { th = "<th colspan=\"" + colspan + "\" " + thVal + ">" ; } 629 630 Calendar cal = Calendar.getInstance() ; 631 cal.setTime( dFirst ); 632 633 maxDayCnt = (int)Math.round(nSumDuration / headerDuration) ; // 3.5.5.9 (2004/06/07) 634 int addDate ; 635 int field ; 636 if( headerDuration >= 1.0d ) { 637 addDate = (int)Math.round( headerDuration ); 638 field = Calendar.DATE ; 639 } 640 else { 641 addDate = (int)Math.round( 24.0d * headerDuration ); 642 field = Calendar.HOUR_OF_DAY ; 643 } 644 645 // 端数を指定すると、積算誤差がでます。 646 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 647 648 for( int nIndex = 0; nIndex < maxDayCnt; nIndex++ ) { 649 buf.append( th ); 650 buf.append( fmtDate.format( cal.getTime() ) ); 651 buf.append( "</th>" ); 652 cal.add( field ,addDate ); 653 } 654 655 return buf.toString(); 656 } 657 658 /** 659 * ガントチャートヘッダー繰り返し部を、getTableHead()メソッドから分離。 660 * このメソッドは、durationColumn が指定されていない場合の処理を行います。 661 * データは、すべての行に関して、同じ日付けのデータとして扱われます。 662 * よって、行間で、日付け違いの並び順になっているとずれることがあります。 663 * ヘッダーは、最初の行の日付けをそのまま表示させます。よって、データと 664 * 日付けが同期されていれば、不連続な日付けのヘッダーを表示させることも可能です。 665 * ヘッダーの期間(headerDuration)とデータの最小期間(minDuration)は、 666 * ともに、'1' であることが前提です。 667 * useSeqDay 属性に、"true" を設定すると、開始日(startDay)と終了日(endDay) 668 * の日付けを連続した日付けとします。開始日(startDay)や終了日(endDay)が指定 669 * されていない場合は、dystartColumn 属性で指定されたカラムの値の最大値、最小値を 670 * 自動セットします。 671 * 672 * @og.rev 3.5.5.9 (2004/06/07) 新規作成 673 * @og.rev 3.5.6.0 (2004/06/18) ithFormat 変数削除 674 * @og.rev 3.6.1.0 (2005/01/05) startDay,endDay,useSeqDay 属性追加 675 * 676 * @param startNo 開始行番号 677 * @param lastNo 最終行番号 678 * 679 * @return テーブルのタグ文字列 680 */ 681 private String getGanttHeadNoDuration(final int startNo, final int lastNo) { 682 String[] astrOldGroupKeys = new String[groupCols.length]; 683 for( int nIndex =0; nIndex < astrOldGroupKeys.length; nIndex++) { 684 astrOldGroupKeys[nIndex] = ""; 685 } 686 687 String thVal = "" ; // <td ・・・> の <td 以下の ・・・部分の属性文字列。 688 String ymdForm = "MM/dd" ; // td タグの BODY 部 の 日付けフォーマット 689 690 if( headerFormat != null ) { 691 String format = headerFormat.getItdBody().trim() ; 692 Matcher matcher = cpTdTh.matcher(format); 693 if( matcher.find() ) { 694 thVal = matcher.group(2); 695 ymdForm = matcher.group(3); 696 } 697 } 698 String th = "<th " + thVal + ">"; 699 700 // 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更 701 Locale local = new Locale( headerLocale ); 702 SimpleDateFormat dataFmt = new SimpleDateFormat( formatDystart,local ); 703 SimpleDateFormat headFmt = new SimpleDateFormat( ymdForm,Locale.JAPAN ); 704 Calendar cal = Calendar.getInstance() ; 705 706 TreeSet<String> daySet = new TreeSet<String>(); 707 for( int nRowUp = startNo; nRowUp < lastNo; nRowUp++ ) { 708 // ガントでは、データのスキップは行いません。 709 710 String day = getValue(nRowUp, posDystart); 711 daySet.add( day ); 712 } 713 // 3.6.1.0 (2005/01/05) 714 if( useSeqDay ) { 715 if( startDay == null ) { startDay = daySet.first() ; } 716 if( endDay == null ) { endDay = daySet.last() ; } 717 718 try { 719 Calendar startCal = Calendar.getInstance() ; 720 Date dStart = dataFmt.parse( startDay ); 721 startCal.setTime( dStart ); 722 723 Calendar endCal = Calendar.getInstance() ; 724 Date dEnd = dataFmt.parse( endDay ); 725 endCal.setTime( dEnd ); 726 endCal.set( Calendar.HOUR_OF_DAY,12 ); // 日付け比較する為、12時間進めておく。 727 728 while( startCal.before( endCal ) ) { 729 daySet.add( dataFmt.format( startCal.getTime() ) ); 730 startCal.add( Calendar.DATE ,1 ); 731 } 732 } 733 catch ( ParseException ex) { 734 String errMsg = "startDay,endDayに指定した日付形式と異なるデータが存在しています。" 735 + "[" + startDay + "],[" 736 + "[" + endDay + "] => [" 737 + dataFmt.toPattern() + "]" ; 738 throw new HybsSystemException( errMsg,ex ); 739 } 740 } 741 742 headDays = daySet.toArray( new String[daySet.size()] ); // 4.0.0 (2005/01/31) 743 744 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 745 for( int i=0; i<headDays.length; i++ ) { 746 747 try { 748 cal.setTime( dataFmt.parse(headDays[i]) ); 749 buf.append( th ); 750 buf.append( headFmt.format( cal.getTime() ) ); 751 buf.append( "</th>" ); 752 } 753 catch ( ParseException ex) { 754 String errMsg = "DYSTARTに指定した日付形式と異なるデータが存在しています。" 755 + "[" + headDays[i] + "] => [" 756 + dataFmt.toPattern() + "]" ; 757 throw new HybsSystemException( errMsg,ex ); 758 } 759 } 760 761 return buf.toString(); 762 } 763 764 /** 765 * DBTableModel から テーブルのタグ文字列を作成して返します。 766 * 767 * @og.rev 3.5.0.0 (2003/09/17) <tr>属性は、元のフォーマットのまま使用します。 768 * @og.rev 3.5.1.0 (2003/10/03) Noカラムに、numberType 属性を追加 769 * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。 770 * @og.rev 3.5.4.7 (2004/02/06) ヘッダーにソート機能用のリンクを追加します。 771 * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応 772 * 773 * @return テーブルのタグ文字列 774 */ 775 protected String getTableFoot() { 776 footerFormat.makeFormat( getDBTableModel() ); 777 778 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 779 780 buf.append("<tfoot>").append( HybsSystem.CR ); 781 buf.append( footerFormat.getTrTag() ).append( HybsSystem.CR ); 782 783 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 784 if( isNumberDisplay() ) { 785 buf.append(" <th"); 786 buf.append(" colspan=\"3\""); 787 buf.append( footerFormat.getRowspan() ); 788 buf.append("></th>"); 789 } 790 791 int cl = 0; 792 for( ; cl < footerFormat.getLocationSize(); cl++ ) { 793 int loc = footerFormat.getLocation(cl); 794 if( loc >= 0 ) { buf.append( getSortedColumnLabel(loc) ); } 795 } 796 buf.append( footerFormat.getFormat(cl) ).append( HybsSystem.CR ); 797 buf.append("</tfoot>").append( HybsSystem.CR ); 798 799 return buf.toString(); 800 } 801 802 /** 803 * itaタグの中身を形式化する。 804 * 805 * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応 806 * @og.rev 3.5.5.9 (2004/06/07) durationColumn を指定しない場合の処理を追加 807 * @og.rev 3.5.6.0 (2004/06/18) itdタグの[$xx] , [#xx]対応 808 * @og.rev 3.5.6.0 (2004/06/18) '!' 値のみ 追加 既存の '$' は、レンデラー 809 * 810 * @param nTblRow テーブルモデルの行番号 811 * @param myIteFormat TableFormatteオブジェクト 812 * @param inputBuf 出力データバーファ 813 * 814 * @return StringBuilder戻り値 815 */ 816 StringBuilder formatItd(final int nTblRow, final TableFormatter myIteFormat, final StringBuilder inputBuf ) { 817 if( myIteFormat == null ) { return new StringBuilder( "" ); } 818 819 final StringBuilder strBuf ; 820 if( inputBuf != null ) { strBuf = inputBuf; } 821 else { strBuf = new StringBuilder( HybsSystem.BUFFER_LARGE ); } 822 823 int colspan = 1; 824 if( posDuration >= 0 ) { 825 // 3.7.0.0 (2005/01/18) 小数点の桁落ち対策 826 colspan = (int)Math.round( StringUtil.parseDouble( getValue(nTblRow, posDuration) ) / minDuration ); 827 } 828 else { // 日付けヘッダー未満は、空タグを出力しておく 829 String today = getValue(nTblRow, posDystart); 830 int comp = headDays[headDaysCnt].compareTo( today ); 831 headDaysCnt++ ; 832 while( comp < 0 && headDaysCnt < headDays.length ) { 833 strBuf.append( "<td></td>" ); 834 comp = headDays[headDaysCnt].compareTo( today ); 835 headDaysCnt++ ; 836 } 837 if( comp != 0 ) { // 見つからなかった(先に日付けが無くなった) 838 String errMsg = "日付けヘッダーと日付けデータに矛盾が発生しています。" 839 + HybsSystem.CR 840 + "groupColumns で日付けがユニークになっていない可能性があります。" 841 + "row=[" + (nTblRow +1) + "] , today=[" + today + "]" ; 842 throw new HybsSystemException( errMsg ); 843 } 844 } 845 846 int cl = 0; 847 for( ; cl < myIteFormat.getLocationSize(); cl++ ) { 848 String fmt = myIteFormat.getFormat(cl) ; 849 int loc = myIteFormat.getLocation(cl); // 3.5.6.0 850 if( cl == 0 && colspan != 1 ) { 851 fmt = StringUtil.replace(fmt , "<td", "<td colspan=\"" + colspan + "\""); 852 } 853 strBuf.append( fmt ); // 3.5.6.0 854 if( loc >= 0 ) { 855 switch( myIteFormat.getType(cl) ) { 856 case '#' : strBuf.append( getColumnLabel(loc) ); break; 857 case '$' : strBuf.append( getRendererValue(nTblRow,loc) ); break; 858 case '!' : strBuf.append( getValue(nTblRow,loc) ); break; 859 default : strBuf.append( getValueLabel(nTblRow,loc) ); break; 860 } 861 } 862 else { 863 strBuf.append( myIteFormat.getSystemFormat(nTblRow,loc) ); 864 } 865 } 866 strBuf.append(myIteFormat.getFormat(cl)); 867 868 return strBuf; 869 } 870 871 /** 872 * フォーマットを設定します。 873 * 874 * @og.rev 3.5.4.0 (2003/11/25) 新規作成 875 * @og.rev 3.5.4.4 (2004/01/16) 配列の最大数を変更 876 * @og.rev 3.5.6.0 (2004/06/18) ithFormat , itdFormat 変数削除 877 * 878 * @param list TableFormatterのリスト 879 */ 880 @Override 881 public void setFormatterList( final List<TableFormatter> list ) { // 4.3.3.6 (2008/11/15) Generics警告対応 882 bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT]; 883 884 bodyFormatsCount = 0; 885 for( int i=0; i<list.size(); i++ ) { 886 TableFormatter format = list.get( i ); // 4.3.3.6 (2008/11/15) Generics警告対応 887 888 switch( format.getFormatType() ) { 889 case TYPE_HEAD : headerFormat = format; break; 890 case TYPE_BODY : bodyFormats[bodyFormatsCount++] = format; break; 891 case TYPE_FOOT : footerFormat = format; break; 892 default : String errMsg = "FormatterType の定義外の値が指定されました。"; 893 // 4.3.4.4 (2009/01/01) 894 throw new HybsSystemException( errMsg ); 895 } 896 // 3.5.6.0 (2004/06/18) 廃止 897 } 898 899 // 3.5.6.0 (2004/06/18) itdFormats 処理追加 900 // tbody 配列分だけ設定しておきます。 901 itdFormats = new TableFormatter[bodyFormatsCount]; 902 for( int i=0; i<bodyFormatsCount; i++ ) { 903 String format = bodyFormats[i].getItdBody().trim(); 904 itdFormats[i] = new TableFormatter(); 905 itdFormats[i].setFormat( format ); 906 } 907 } 908 909 /** 910 * フォーマットメソッドを使用できるかどうかを問い合わせます。 911 * 912 * @return 使用可能(true)/ 使用不可能 (false) 913 */ 914 @Override 915 public boolean canUseFormat() { 916 return true; 917 } 918 919 /** 920 * 表示項目の編集(並び替え)が可能かどうかを返します 921 * 922 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 923 * 924 * @return 表示項目の編集(並び替え)が可能かどうか(false:不可能) 925 */ 926 @Override 927 public boolean isEditable() { 928 return false; 929 } 930}