    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)が、０ かそれ以下です。";
    400                        throw new HybsSystemException( errMsg );
    401                }
    402
    403                headerDuration = StringUtil.parseDouble( strHeadDuration );
    404                if( headerDuration <= 0.0d ) {
    405                        String errMsg = "ヘッダーの表示期間(headerDuration)が、０ かそれ以下です。";
    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         * @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 );  // 日付け比較する為、１２時間進めておく。
    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) &lt;tr&gt;属性は、元のフォーマットのまま使用します。
    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}