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