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) &lt;tr&gt;属性は、元のフォーマットのまま使用します。
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) &lt;tr&gt;属性は、元のフォーマットのまま使用します。
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         *
460         * @return      テーブルのタグ文字列
461         */
462        @Override
463        protected String getTableHead() {
464                headerFormat.makeFormat( getDBTableModel() );
465
466                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
467                // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
468                if( isNumberDisplay() ) {
469//                      buf.append("<colgroup class=\"X\" />");           // 4.0.0 (2005/01/31)
470//                      buf.append("<colgroup class=\"BIT\" />");
471//                      buf.append("<colgroup class=\"S9\" />");          // 4.0.0 (2005/01/31)
472                        buf.append("<colgroup class=\"X\" ><!-- --></colgroup>");             // 4.0.0 (2005/01/31)
473                        buf.append("<colgroup class=\"BIT\" ><!-- --></colgroup>");
474                        buf.append("<colgroup class=\"S9\" ><!-- --></colgroup>");            // 4.0.0 (2005/01/31)
475                        buf.append(HybsSystem.CR);
476                }
477
478                // 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動
479                buf.append("<thead id=\"header\">").append( HybsSystem.CR );      // 3.5.6.5 (2004/08/09)
480                buf.append( getHeadLine() );
481                buf.append("</thead>").append( HybsSystem.CR );
482
483                return buf.toString();
484        }
485
486        /**
487         * ヘッダー繰り返し部を、getTableHead()メソッドから分離。
488         *
489         * @og.rev 3.5.2.0 (2003/10/20) 新規作成
490         * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。
491         * @og.rev 3.5.4.3 (2004/01/05) useCheckControl 属性の機能を追加
492         * @og.rev 3.5.4.6 (2004/01/30) numberType="none" 時の処理を追加(Noラベルを出さない)
493         * @og.rev 3.5.4.7 (2004/02/06) ヘッダーにソート機能用のリンクを追加します。
494         * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応
495         * @og.rev 3.7.0.1 (2005/01/31) 全件チェックコントロール処理変更
496         * @og.rev 5.0.0.3 (2009/09/22) itdの有無を取得します。
497         *
498         * @return      テーブルのタグ文字列
499         */
500        @Override
501        protected String getHeadLine() {
502                if( headerLine != null ) { return headerLine; }         // キャッシュを返す。
503
504                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
505
506                buf.append( headerFormat.getTrTag() ).append( HybsSystem.CR );
507
508                // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
509                if( isNumberDisplay() ) {
510                        // 3.5.4.3 (2004/01/05) 追加分
511                        if( isUseCheckControl() && "checkbox".equals( getSelectedType() ) ) {
512                                buf.append("  <th" ).append( headerFormat.getRowspan() ).append("></th>");
513                                buf.append("  <th" ).append( headerFormat.getRowspan() );
514                                buf.append(">").append( getAllCheckControl() ).append( "</th>");
515                                buf.append("  <th" ).append( headerFormat.getRowspan() );
516                                buf.append(">").append( getNumberHeader() ).append("</th>");   // 3.5.4.6 (2004/01/30)
517                        }
518                        else {
519                                buf.append(" <th colspan=\"3\"");
520                                buf.append( headerFormat.getRowspan() );
521                                buf.append(">").append( getNumberHeader() ).append("</th>");   // 3.5.4.6 (2004/01/30)
522                        }
523                }
524
525                int cl = 0;
526                for( ; cl < headerFormat.getLocationSize(); cl++ ) {
527                        buf.append( headerFormat.getFormat(cl) );
528                        int loc = headerFormat.getLocation(cl);
529                        if( loc >= 0 ) { buf.append( getSortedColumnLabel(loc) ); }
530                }
531                buf.append( headerFormat.getFormat(cl) ).append( HybsSystem.CR );
532
533                // 5.0.0.3 (2009/09/22) ITD_MARKERの条件判断追加
534                if( buf.indexOf( TableFormatter.HYBS_ITD_MARKER ) >= 0 ){
535                        useItd = true;
536                }
537                headerLine = StringUtil.replace(buf.toString(), TableFormatter.HYBS_ITD_MARKER, ganttHeadLine);
538
539                return headerLine;
540        }
541
542        /**
543         * ガントチャートヘッダー繰り返し部を、getTableHead()メソッドから分離。
544         * このメソッドは、durationColumn を利用して、連続日付けデータを作成します。
545         * データは、開始日と期間データを持ち、ヘッダーは連続日付けになります。
546         * ヘッダーの期間(headerDuration)とデータの最小期間(minDuration)が異なる為、
547         * 最初の行データより、最終日を求め、headerDuration で割り算した個数の連続日数を
548         * 表示させています。
549         *
550         * @og.rev 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更
551         * @og.rev 3.5.6.0 (2004/06/18) ithFormat 変数削除
552         *
553         * @param       startNo 開始行番号
554         * @param       lastNo  最終行番号
555         *
556         * @return      テーブルのタグ文字列
557         */
558        private String getGanttHead(final int startNo, final int lastNo) {
559                String[] astrOldGroupKeys = new String[groupCols.length];
560                for( int nIndex =0; nIndex < astrOldGroupKeys.length; nIndex++) {
561                        astrOldGroupKeys[nIndex] = "";
562                }
563
564                // 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更
565                Locale local = new Locale( headerLocale );
566                SimpleDateFormat fmtDate = new SimpleDateFormat( formatDystart,local );
567
568                double nSumDuration = 0.0d ;
569                Date dFirst = null ;
570
571                for( int nRowUp = startNo; nRowUp < lastNo; nRowUp++ ) {
572                        // ガントでは、データのスキップは行いません。
573
574                        // 最初の行 または、ブレイクするまで
575                        if( isSameGroup(nRowUp, astrOldGroupKeys) || (nRowUp == startNo) ) {
576                                nSumDuration += StringUtil.parseDouble( getValue(nRowUp, posDuration) );
577                                try {
578                                        if( dFirst == null ) {
579                                                dFirst = fmtDate.parse(getValue(nRowUp, posDystart));
580                                        }
581                                }
582                                catch ( ParseException ex) {
583                                        String errMsg = "DYSTARTに指定した日付形式と異なるデータが存在しています。"
584                                                        + "[" + getValue(nRowUp  , posDystart) + "] => ["
585                                                        + fmtDate.toPattern() + "]" ;
586                                        throw new HybsSystemException( errMsg,ex );
587                                }
588                        }
589                        else { break; }
590                }
591
592                String thVal    = ""     ;              // <td ・・・> の <td 以下の ・・・部分の属性文字列。
593                String ymdForm  = "MM/dd" ;             // td タグの BODY 部 の 日付けフォーマット
594
595                if( headerFormat != null ) {
596                        String format = headerFormat.getItdBody().trim() ;
597                        Matcher matcher = cpTdTh.matcher(format);
598                        if( matcher.find() ) {
599                                thVal   = matcher.group(2);
600                                ymdForm = matcher.group(3);
601                        }
602                }
603
604                try {
605                        fmtDate.applyPattern(ymdForm);
606                }
607                catch(IllegalArgumentException eArg) {
608                        String errMsg = "theadの内のitdの内側に指定された日付の形式が不正です。(" + ymdForm + ")";
609                        throw new HybsSystemException( errMsg,eArg );
610                }
611
612                int colspan = (int)Math.round( headerDuration / minDuration );
613                final String th ;
614                if( colspan == 1 ) { th = "<th " + thVal + ">"; }
615                else { th = "<th colspan=\"" + colspan + "\" " + thVal + ">" ; }
616
617                Calendar cal = Calendar.getInstance() ;
618                cal.setTime( dFirst );
619
620                maxDayCnt = (int)Math.round(nSumDuration / headerDuration) ;            // 3.5.5.9 (2004/06/07)
621                int addDate ;
622                int field   ;
623                if( headerDuration >= 1.0d ) {
624                        addDate = (int)Math.round( headerDuration );
625                        field   = Calendar.DATE ;
626                }
627                else {
628                        addDate = (int)Math.round( 24.0d * headerDuration );
629                        field   = Calendar.HOUR_OF_DAY ;
630                }
631
632                // 端数を指定すると、積算誤差がでます。
633                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
634
635                for( int nIndex = 0; nIndex < maxDayCnt; nIndex++ ) {
636                        buf.append( th );
637                        buf.append( fmtDate.format( cal.getTime() ) );
638                        buf.append( "</th>" );
639                        cal.add( field ,addDate );
640                }
641
642                return buf.toString();
643        }
644
645        /**
646         * ガントチャートヘッダー繰り返し部を、getTableHead()メソッドから分離。
647         * このメソッドは、durationColumn が指定されていない場合の処理を行います。
648         * データは、すべての行に関して、同じ日付けのデータとして扱われます。
649         * よって、行間で、日付け違いの並び順になっているとずれることがあります。
650         * ヘッダーは、最初の行の日付けをそのまま表示させます。よって、データと
651         * 日付けが同期されていれば、不連続な日付けのヘッダーを表示させることも可能です。
652         * ヘッダーの期間(headerDuration)とデータの最小期間(minDuration)は、
653         * ともに、'1' であることが前提です。
654         * useSeqDay 属性に、"true" を設定すると、開始日(startDay)と終了日(endDay)
655         * の日付けを連続した日付けとします。開始日(startDay)や終了日(endDay)が指定
656         * されていない場合は、dystartColumn 属性で指定されたカラムの値の最大値、最小値を
657         * 自動セットします。
658         *
659         * @og.rev 3.5.5.9 (2004/06/07) 新規作成
660         * @og.rev 3.5.6.0 (2004/06/18) ithFormat 変数削除
661         * @og.rev 3.6.1.0 (2005/01/05) startDay,endDay,useSeqDay 属性追加
662         *
663         * @param       startNo 開始行番号
664         * @param       lastNo  最終行番号
665         *
666         * @return      テーブルのタグ文字列
667         */
668        private String getGanttHeadNoDuration(final int startNo, final int lastNo) {
669                String[] astrOldGroupKeys = new String[groupCols.length];
670                for( int nIndex =0; nIndex < astrOldGroupKeys.length; nIndex++) {
671                        astrOldGroupKeys[nIndex] = "";
672                }
673
674                String thVal    = ""     ;              // <td ・・・> の <td 以下の ・・・部分の属性文字列。
675                String ymdForm  = "MM/dd" ;             // td タグの BODY 部 の 日付けフォーマット
676
677                if( headerFormat != null ) {
678                        String format = headerFormat.getItdBody().trim() ;
679                        Matcher matcher = cpTdTh.matcher(format);
680                        if( matcher.find() ) {
681                                thVal   = matcher.group(2);
682                                ymdForm = matcher.group(3);
683                        }
684                }
685                String th = "<th " + thVal + ">";
686
687                // 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に、Locale を加味できるように変更
688                Locale local = new Locale( headerLocale );
689                SimpleDateFormat dataFmt = new SimpleDateFormat( formatDystart,local );
690                SimpleDateFormat headFmt = new SimpleDateFormat( ymdForm,Locale.JAPAN );
691                Calendar cal = Calendar.getInstance() ;
692
693                TreeSet<String> daySet = new TreeSet<String>();
694                for( int nRowUp = startNo; nRowUp < lastNo; nRowUp++ ) {
695                        // ガントでは、データのスキップは行いません。
696
697                        String day = getValue(nRowUp, posDystart);
698                        daySet.add( day );
699                }
700                // 3.6.1.0 (2005/01/05)
701                if( useSeqDay ) {
702                        if( startDay == null ) { startDay = daySet.first() ; }
703                        if( endDay   == null ) { endDay   = daySet.last()  ; }
704
705                        try {
706                                Calendar startCal = Calendar.getInstance() ;
707                                Date dStart = dataFmt.parse( startDay );
708                                startCal.setTime( dStart );
709
710                                Calendar endCal = Calendar.getInstance() ;
711                                Date dEnd = dataFmt.parse( endDay );
712                                endCal.setTime( dEnd );
713                                endCal.set( Calendar.HOUR_OF_DAY,12 );  // 日付け比較する為、12時間進めておく。
714
715                                while( startCal.before( endCal ) ) {
716                                        daySet.add( dataFmt.format( startCal.getTime() ) );
717                                        startCal.add( Calendar.DATE ,1 );
718                                }
719                        }
720                        catch ( ParseException ex) {
721                                String errMsg = "startDay,endDayに指定した日付形式と異なるデータが存在しています。"
722                                                + "[" + startDay + "],["
723                                                + "[" + endDay + "] => ["
724                                                + dataFmt.toPattern() + "]" ;
725                                throw new HybsSystemException( errMsg,ex );
726                        }
727                }
728
729                headDays = daySet.toArray( new String[daySet.size()] ); // 4.0.0 (2005/01/31)
730
731                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
732                for( int i=0; i<headDays.length; i++ ) {
733
734                        try {
735                                cal.setTime( dataFmt.parse(headDays[i]) );
736                                buf.append( th );
737                                buf.append( headFmt.format( cal.getTime() ) );
738                                buf.append( "</th>" );
739                        }
740                        catch ( ParseException ex) {
741                                String errMsg = "DYSTARTに指定した日付形式と異なるデータが存在しています。"
742                                                + "[" + headDays[i] + "] => ["
743                                                + dataFmt.toPattern() + "]" ;
744                                throw new HybsSystemException( errMsg,ex );
745                        }
746                }
747
748                return buf.toString();
749        }
750
751        /**
752         * DBTableModel から テーブルのタグ文字列を作成して返します。
753         *
754         * @og.rev 3.5.0.0 (2003/09/17) &lt;tr&gt;属性は、元のフォーマットのまま使用します。
755         * @og.rev 3.5.1.0 (2003/10/03) Noカラムに、numberType 属性を追加
756         * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。
757         * @og.rev 3.5.4.7 (2004/02/06) ヘッダーにソート機能用のリンクを追加します。
758         * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応
759         *
760         * @return      テーブルのタグ文字列
761         */
762        protected String getTableFoot() {
763                footerFormat.makeFormat( getDBTableModel() );
764
765                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
766
767                buf.append("<tfoot>").append( HybsSystem.CR );
768                buf.append( footerFormat.getTrTag() ).append( HybsSystem.CR );
769
770                // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
771                if( isNumberDisplay() ) {
772                        buf.append(" <th");
773                        buf.append(" colspan=\"3\"");
774                        buf.append( footerFormat.getRowspan() );
775                        buf.append("></th>");
776                }
777
778                int cl = 0;
779                for( ; cl < footerFormat.getLocationSize(); cl++ ) {
780                        int loc = footerFormat.getLocation(cl);
781                        if( loc >= 0 ) { buf.append( getSortedColumnLabel(loc) ); }
782                }
783                buf.append( footerFormat.getFormat(cl) ).append( HybsSystem.CR );
784                buf.append("</tfoot>").append( HybsSystem.CR );
785
786                return buf.toString();
787        }
788
789        /**
790         * itaタグの中身を形式化する。
791         *
792         * @og.rev 3.5.5.0 (2004/03/12) systemFormat(例:[KEY.カラム名]形式等)の対応
793         * @og.rev 3.5.5.9 (2004/06/07) durationColumn を指定しない場合の処理を追加
794         * @og.rev 3.5.6.0 (2004/06/18) itdタグの[$xx] , [#xx]対応
795         * @og.rev 3.5.6.0 (2004/06/18) '!' 値のみ 追加 既存の '$' は、レンデラー
796         *
797         * @param   nTblRow      テーブルモデルの行番号
798         * @param   myIteFormat  TableFormatteオブジェクト
799         * @param   inputBuf     出力データバーファ
800         *
801         * @return      StringBuilder戻り値
802         */
803        StringBuilder formatItd(final int nTblRow, final TableFormatter myIteFormat, final StringBuilder inputBuf ) {
804                if( myIteFormat == null ) { return new StringBuilder( "" ); }
805
806                final StringBuilder strBuf ;
807                if( inputBuf != null ) { strBuf = inputBuf; }
808                else {                                   strBuf = new StringBuilder( HybsSystem.BUFFER_LARGE ); }
809
810                int colspan = 1;
811                if( posDuration >= 0 ) {
812                        // 3.7.0.0 (2005/01/18) 小数点の桁落ち対策
813                        colspan = (int)Math.round( StringUtil.parseDouble( getValue(nTblRow, posDuration) ) / minDuration );
814                }
815                else {  // 日付けヘッダー未満は、空タグを出力しておく
816                        String today = getValue(nTblRow, posDystart);
817                        int comp = headDays[headDaysCnt].compareTo( today );
818                        headDaysCnt++ ;
819                        while( comp < 0 && headDaysCnt < headDays.length ) {
820                                strBuf.append( "<td></td>" );
821                                comp = headDays[headDaysCnt].compareTo( today );
822                                headDaysCnt++ ;
823                        }
824                        if( comp != 0 ) {       // 見つからなかった(先に日付けが無くなった)
825                                String errMsg = "日付けヘッダーと日付けデータに矛盾が発生しています。"
826                                                + HybsSystem.CR
827                                                + "groupColumns で日付けがユニークになっていない可能性があります。"
828                                                + "row=[" + (nTblRow +1) + "] , today=[" + today + "]" ;
829                                throw new HybsSystemException( errMsg );
830                        }
831                }
832
833                int cl = 0;
834                for( ; cl < myIteFormat.getLocationSize(); cl++ ) {
835                        String fmt = myIteFormat.getFormat(cl) ;
836                        int loc = myIteFormat.getLocation(cl);  // 3.5.6.0
837                        if( cl == 0 && colspan != 1 ) {
838                                fmt = StringUtil.replace(fmt , "<td", "<td colspan=\"" + colspan + "\"");
839                        }
840                        strBuf.append( fmt );                   // 3.5.6.0
841                        if( loc >= 0 ) {
842                                switch( myIteFormat.getType(cl) ) {
843                                        case '#' : strBuf.append( getColumnLabel(loc) );                        break;
844                                        case '$' : strBuf.append( getRendererValue(nTblRow,loc) );      break;
845                                        case '!' : strBuf.append( getValue(nTblRow,loc) );                      break;
846                                        default  : strBuf.append( getValueLabel(nTblRow,loc) );         break;
847                                }
848                        }
849                        else {
850                                strBuf.append( myIteFormat.getSystemFormat(nTblRow,loc) );
851                        }
852                }
853                strBuf.append(myIteFormat.getFormat(cl));
854
855                return strBuf;
856        }
857
858        /**
859         * フォーマットを設定します。
860         *
861         * @og.rev 3.5.4.0 (2003/11/25) 新規作成
862         * @og.rev 3.5.4.4 (2004/01/16) 配列の最大数を変更
863         * @og.rev 3.5.6.0 (2004/06/18) ithFormat ,  itdFormat 変数削除
864         *
865         * @param       list    TableFormatterのリスト
866         */
867        @Override
868        public void setFormatterList( final List<TableFormatter> list ) {         // 4.3.3.6 (2008/11/15) Generics警告対応
869                bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT];
870
871                bodyFormatsCount = 0;
872                for( int i=0; i<list.size(); i++ ) {
873                        TableFormatter format = list.get( i );          // 4.3.3.6 (2008/11/15) Generics警告対応
874
875                        switch( format.getFormatType() ) {
876                                case TYPE_HEAD : headerFormat = format; break;
877                                case TYPE_BODY : bodyFormats[bodyFormatsCount++] = format; break;
878                                case TYPE_FOOT : footerFormat = format; break;
879                                default : String errMsg = "FormatterType の定義外の値が指定されました。";
880                                // 4.3.4.4 (2009/01/01)
881                                                  throw new HybsSystemException( errMsg );
882                        }
883                        // 3.5.6.0 (2004/06/18) 廃止
884                }
885
886                // 3.5.6.0 (2004/06/18) itdFormats 処理追加
887                // tbody 配列分だけ設定しておきます。
888                itdFormats = new TableFormatter[bodyFormatsCount];
889                for( int i=0; i<bodyFormatsCount; i++ ) {
890                        String format = bodyFormats[i].getItdBody().trim();
891                        itdFormats[i] = new TableFormatter();
892                        itdFormats[i].setFormat( format );
893                }
894        }
895
896        /**
897         * フォーマットメソッドを使用できるかどうかを問い合わせます。
898         *
899         * @return  使用可能(true)/ 使用不可能 (false)
900         */
901        @Override
902        public boolean canUseFormat() {
903                return true;
904        }
905
906        /**
907         * 表示項目の編集(並び替え)が可能かどうかを返します
908         *
909         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
910         *
911         * @return      表示項目の編集(並び替え)が可能かどうか(false:不可能)
912         */
913        @Override
914        public boolean isEditable() {
915                return false;
916        }
917}