    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.hayabusa.db;
    017
    018import java.math.BigDecimal;
    019import java.sql.ResultSet;
    020import java.sql.SQLException;
    021import java.text.DecimalFormat;
    022import java.util.ArrayList;
    023import java.util.HashMap;
    024import java.util.LinkedHashMap;
    025import java.util.List;
    026import java.util.Map;
    027
    028import org.opengion.fukurou.db.ResultSetValue;                          // 6.0.4.0 (2014/11/28)
    029import org.opengion.fukurou.util.StringUtil;
    030import org.opengion.hayabusa.common.HybsSystem;
    031import org.opengion.hayabusa.common.HybsSystemException;
    032import org.opengion.hayabusa.resource.ResourceManager;
    033import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    034
    035/**
    036 * DBTableModelを継承した TableModelの編集設定による変換を行うための実装クラスです。
    037 *
    038 * このクラスでは、オブジェクト初期化後は、通常のDBTableModelと同じ振る舞いをします。
    039 * オブジェクト初期化時(createメソッド呼び出し時)に、検索結果オブジェクトから直接、編集設定に
    040 * 応じて変換されたDBTableModelを生成します。
    041 *
    042 * このような実装を行う理由は、メモリ使用量を節約するためです。
    043 * この編集設定では、集計機能を備えていますが、一旦DBTableModel作成後に集計処理を行うと、
    044 * メモリを大量に使用する恐れがあるため、検索結果オブジェクトから直接集計処理を行い、DBTableModelを
    045 * 生成しています。
    046 *
    047 * DBTableModel インターフェースは，データベースの検索結果(Resultset)をラップする
    048 * インターフェースとして使用して下さい。
    049 *
    050 * @og.rev 5.3.6.0 (2011/06/01) 新規作成
    051 * @og.group テーブル管理
    052 *
    053 * @version  5.0
    054 * @author   Hiroki Nakamura
    055 * @since    JDK6.0,
    056 */
    057public class DBTableModelEditor extends DBTableModelImpl {
    058        private static final String                     JS              = HybsSystem.JOINT_STRING;
    059        private static final DecimalFormat      FORMAT  = new DecimalFormat( "0.#########" );
    060
    061        private int rowCountColumn = -1;
    062        private DBEditConfig config;
    063
    064        /**
    065         * デフォルトコンストラクター
    066         *
    067         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    068         */
    069        public DBTableModelEditor() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    070
    071        /**
    072         * DBTableModel を設定し、このオブジェクトを初期化します。
    073         *
    074         * @og.rev 5.7.1.2 (2013/12/20) msg ⇒ errMsg 変更
    075         * @og.rev 6.0.2.1 (2014/09/26) queryタグが複数あり、mainTrans=false で制御されていない場合、エラーが発生する
    076         * @og.rev 6.0.2.5 (2014/10/31) FireBardでの日付型取得対応
    077         * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    078         * @og.rev 6.0.4.0 (2014/11/28) queryタグが複数ある場合の事前チェックの条件訂正
    079         *
    080         * @param       result                  検索結果オブジェクト
    081         * @param       skipRowCount    読み飛ばし件数
    082         * @param       maxRowCount             最大検索件数
    083         * @param       resource                ResourceManagerオブジェクト
    084         * @param       config                  編集設定オブジェクト
    085         * @throws      SQLException データベースアクセスエラー
    086         */
    087        public void create( final ResultSet result, final int skipRowCount, final int maxRowCount, final ResourceManager resource, final DBEditConfig config ) throws SQLException {
    088                if( result == null || config == null || resource == null ) {
    089                        final String errMsg = "DBTableModelまたは、DBEditConfigが設定されていません。";
    090                        throw new HybsSystemException( errMsg );        // 5.7.1.2 (2013/12/20) msg ⇒ errMsg 変更
    091                }
    092
    093                this.config = config;
    094
    095                /**********************************************************************
    096                 * 各パラメーターの初期化処理
    097                 **********************************************************************/
    098                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    099                final ResultSetValue rsv = new ResultSetValue( result );                // 6.0.4.0 (2014/11/28)
    100                int colCnt = rsv.getColumnCount();                                              // 6.0.4.0 (2014/11/28)
    101
    102                if( config.useGroup() || config.useSubTotal() || config.useTotal() || config.useGrandTotal() ) {
    103                        rowCountColumn = colCnt;
    104                        colCnt++;
    105                }
    106                init( colCnt );
    107
    108                DBColumn[] dbColumn = new DBColumn[numberOfColumns];
    109                boolean[] sumFilter                     = new boolean[numberOfColumns];
    110                boolean[] groupFilter           = new boolean[numberOfColumns];
    111                boolean[] subTotalFilter        = new boolean[numberOfColumns];
    112                boolean[] totalFilter           = new boolean[numberOfColumns];
    113                boolean   sumFilterCheck        = false;                                                // 6.0.2.1 (2014/09/26)
    114                if( config.useGrandTotal() ) { sumFilterCheck = true; }         // 6.0.4.0 (2014/11/28)
    115                for( int column=0; column<numberOfColumns; column++ ) {
    116                        String name = null;
    117                        // 6.1.0.0 (2014/12/26) refactoring : Avoid if(x != y) ..; else ..;
    118                        if( column == rowCountColumn ) {
    119                                name = "rowCount";
    120                                dbColumn[column] = resource.makeDBColumn( name );
    121                        }
    122                        else {
    123                                name = rsv.getColumnName(column);                                       // 6.0.4.0 (2014/11/28)
    124                                dbColumn[column] = resource.getDBColumn( name );
    125                                if( dbColumn[column] == null ) {
    126                                        dbColumn[column] = DBTableModelUtil.makeDBColumn( name,column,rsv,resource );   // 6.0.4.0 (2014/11/28)
    127                                }
    128                        }
    129
    130                        setDBColumn( column,dbColumn[column] );
    131                        sumFilter[column]               = config.isSumClm( name );
    132                        groupFilter[column]             = config.isGroupClm( name );
    133                        subTotalFilter[column]  = config.isSubTotalClm( name );
    134                        totalFilter[column]             = config.isTotalClm( name );
    135                        // 6.0.4.0 (2014/11/28) queryタグが複数ある場合の事前チェックの条件訂正
    136                        if( sumFilter[column] || groupFilter[column] || subTotalFilter[column] || totalFilter[column] ) {
    137                                sumFilterCheck = true;
    138                        }
    139                }
    140
    141                /**********************************************************************
    142                 * 集計、ソート、合計処理
    143                 **********************************************************************/
    144                // 集計キーに基づく集計処理を行いデータを追加します。
    145                if( config.useGroup() ) {
    146                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    147                        addGroupRows( rsv, skipRowCount, maxRowCount, sumFilter, groupFilter );
    148                }
    149                // 通常と同じように結果カーソルからデータを読込みデータを追加します。
    150                else {
    151                        // 5.5.2.4 (2012/05/16) int[] types は使われていないので、削除します。
    152                        // 6.0.2.5 (2014/10/31) int[] types 復活。isOther ﾌﾗｸﾞも使います。
    153                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    154                        addPlainRows( rsv, skipRowCount, maxRowCount );
    155                }
    156
    157                // ソート処理
    158                if( getRowCount() > 0 && config.useOrderBy() ) {
    159                        sort();
    160                }
    161
    162                // 小計・合計行を追加します。
    163                if( getRowCount() > 0 && !isOverflow()
    164                        && ( config.useSubTotal() || config.useTotal() || config.useGrandTotal() ) ) {
    165
    166                        // 6.0.2.1 (2014/09/26) queryタグが複数あり、mainTrans=false で制御されていない場合、エラーが発生する
    167                        if( !sumFilterCheck ) {
    168                                final String errMsg = "小計、合計カラムが存在しません。"
    169                                                                + " これは、queryタグが複数あり、mainTrans=false で制御されていない可能性があります。" ;
    170                                throw new HybsSystemException( errMsg );
    171                        }
    172
    173                        addTotalRows( maxRowCount, resource, sumFilter, groupFilter, subTotalFilter, totalFilter );
    174                }
    175        }
    176
    177        /**
    178         * 集計キーの設定に基づき、DBTableModelの行を追加します。
    179         * 内部的には、キーブレイクではなく、内部マップにより集計処理を行っているため、
    180         * 集計キーが検索順により散在した場合でも1まとまりで集計されます。
    181         *
    182         * @og.rev 5.3.9.0 (2011/09/01) 値がNULLの場合にエラーになるバグを修正
    183         * @og.rev 5.6.1.0 (2013/02/01) doubleをBigDecimalに変更
    184         * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    185         * @og.rev 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。
    186         * @og.rev 7.0.5.0 (2019/09/09) アンダーバー付きの文字列から、アンダーバーを削除する。
    187         *
    188         * @param rsv ResultSetValueオブジェクト
    189         * @param skipRowCount 読み飛ばし件数
    190         * @param maxRowCount 最大検索件数
    191         * @param sumFilter 集計項目フィルター
    192         * @param groupFilter グループキーフィルター
    193         * @throws SQLException データベースアクセスエラー
    194         */
    195        private void addGroupRows( final ResultSetValue rsv, final int skipRowCount, final int maxRowCount
    196                                                                , final boolean[] sumFilter, final boolean[] groupFilter ) throws SQLException {
    197                int numberOfRows = 0;
    198                while( numberOfRows < skipRowCount && rsv.next() ) {
    199                        // 注意 resultSet.next() を先に判定すると必ず１件読み飛ばしてしまう。
    200                        numberOfRows ++ ;
    201                }
    202                numberOfRows = 0;
    203
    204                final Map<String,String[]>      groupLinkedMap  = new LinkedHashMap<>();
    205                final Map<String,Integer>       groupCountMap   = new HashMap<>();
    206                final Map<String,BigDecimal[]>  sumMap          = new HashMap<>();                      // 5.6.1.0 (2013/02/01)
    207                final StringBuilder groupKey = new StringBuilder( BUFFER_MIDDLE );              // 6.1.0.0 (2014/12/26) refactoring
    208                while( numberOfRows < maxRowCount && rsv.next() ) {
    209                        groupKey.setLength(0);                                                                                          // 6.1.0.0 (2014/12/26) refactoring
    210                        BigDecimal[] sumVals = new BigDecimal[config.getSumClmCount()];         // 5.6.1.0 (2013/02/01)
    211                        String[]   groupVals = new String[config.getGroupClmCount()];
    212                        int sc = 0;
    213                        int gc = 0;
    214                        for( int column=0; column<numberOfColumns; column++ ) {
    215                                if( column != rowCountColumn ) {
    216                                        final String val = rsv.getValue( column );
    217                                        if( sumFilter[column] ) {
    218                                                // 5.3.9.0 (2011/09/01) 値がNULLの場合の対応漏れ
    219                                                // sumVals[sc++] = ( val != null && val.length() > 0 ? Double.valueOf( val ) : 0 );
    220        //                                      sumVals[sc++] = ( val != null && val.length() > 0 ? new BigDecimal( val ) : BigDecimal.ZERO ); // 5.6.1.0 (2013/02/01)
    221                                                // 7.0.5.0 (2019/09/09) アンダーバー付きの文字列から、アンダーバーを削除する。
    222                                                sumVals[sc++] = new BigDecimal( unscoDel( val,"0" ) );
    223
    224                                        }
    225                                        if( groupFilter[column] ) {
    226                                                groupVals[gc++] = val;
    227                                                groupKey.append( val ).append( JS );
    228                                        }
    229                                }
    230                        }
    231
    232                        final String key = groupKey.toString();
    233                        int groupCount = 0;
    234                        if( groupLinkedMap.containsKey( key ) ) {
    235                                final BigDecimal[] eSumVals = sumMap.get( key ); // 5.6.1.0 (2013/02/01)
    236                                for( int i=0; i<config.getSumClmCount(); i++ ) {
    237                                        sumVals[i] = sumVals[i] == null ? BigDecimal.ZERO : sumVals[i].add( eSumVals[i] ); // 5.6.1.0 (2013/02/01)
    238                                }
    239                                sumMap.put( key, sumVals );
    240                                groupCount = groupCountMap.get( key ).intValue() + 1;
    241                        }
    242                        else {
    243                                groupLinkedMap.put( key, groupVals );
    244                                groupCount = 1;
    245                                numberOfRows++;
    246                        }
    247                        sumMap.put( key, sumVals );
    248                        groupCountMap.put( key, Integer.valueOf( groupCount ) );
    249                }
    250
    251                // 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。
    252                groupLinkedMap.forEach( (k,v) -> addRow( groupFilter, v, groupCountMap.get( k ), sumFilter, sumMap.get( k ) ) );
    253
    254                // 最大件数が、超えた場合でかつ次のデータがある場合は、オーバーフロー
    255                if( numberOfRows >= maxRowCount && rsv.next() ) {
    256                        setOverflow( true );
    257                }
    258        }
    259
    260        /**
    261         * 検索結果オブジェクトを順に読み取り、そのままDBTableModelの行を追加します。
    262         *
    263         * @og.rev 5.5.2.4 (2012/05/16) int[] types は使われていないので、削除します。
    264         * @og.rev 6.0.2.5 (2014/10/31) int[] types 復活。isOther ﾌﾗｸﾞも使います。
    265         * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    266         *
    267         * @param rsv ResultSetValueオブジェクト
    268         * @param skipRowCount 読み飛ばし件数
    269         * @param maxRowCount 最大検索件数
    270         * @throws      SQLException データベースアクセスエラー
    271         */
    272        private void addPlainRows( final ResultSetValue rsv, final int skipRowCount, final int maxRowCount ) throws SQLException {
    273                int numberOfRows = 0;
    274                while( numberOfRows < skipRowCount && rsv.next() ) {
    275                        // 注意 resultSet.next() を先に判定すると必ず１件読み飛ばしてしまう。
    276                        numberOfRows ++ ;
    277                }
    278                numberOfRows = 0;
    279
    280                // 6.0.2.5 (2014/10/31) 行列のループなので、 CLOB 使用可否でループを分ける。
    281                        // 6.0.2.5 (2014/10/31) typesを考慮した処理
    282                        while( numberOfRows < maxRowCount && rsv.next() ) {
    283                                numberOfRows++ ;
    284                                String[] columnValues = new String[numberOfColumns];
    285                                for( int column=0; column<numberOfColumns; column++ ) {
    286                                        // 6.1.0.0 (2014/12/26) refactoring : Avoid if(x != y) ..; else ..;
    287                                        if( column == rowCountColumn ) {
    288                                                columnValues[column] = "";
    289                                        }
    290                                        else {
    291                                                columnValues[column] = rsv.getValue( column );
    292                                        }
    293                                }
    294                                addColumnValues( columnValues );
    295                        }
    296
    297                // 最大件数が、超えた場合でかつ次のデータがある場合は、オーバーフロー
    298                if( numberOfRows >= maxRowCount && rsv.next() ) {
    299                        setOverflow( true );
    300                }
    301        }
    302
    303        /**
    304         * DBTableModelのソート処理を行います。
    305         *
    306         */
    307        private void sort() {
    308                // orderByClmsによる並び替え
    309                final DBTableModelSorter temp = new DBTableModelSorter();
    310                temp.setModel( this );
    311                final String[] oClms = StringUtil.csv2Array( config.getOrderByClms() );
    312                for( int i=oClms.length-1; i>=0; i-- ) {
    313                        String oc = oClms[i];
    314                        boolean ascending = true;
    315                        if( StringUtil.startsChar( oc , '!' ) ) {                               // 6.2.0.0 (2015/02/27) １文字 String.startsWith
    316                                oc = oc.substring( 1 );
    317                                ascending = false;
    318                        }
    319                        final int clmNo = getColumnNo( oc );
    320                        temp.sortByColumn( clmNo, ascending );
    321                }
    322                this.data = temp.data;
    323                this.rowHeader = temp.rowHeader;
    324        }
    325
    326        /**
    327         * DBTableModelからデータを読み取り、編集設定情報を元に合計行の追加処理を行います。
    328         * 合計行の追加は、キーブレイクにより行われますので、同じキーが複数回出現した場合は、
    329         * それぞれの行に対して、合計行が付加されます。
    330         *
    331         * @og.rev 5.3.7.0 (2011/07/01) 小計、合計行追加処理でオーバーフローフラグがセットされないバグを修正
    332         * @og.rev 5.6.1.0 (2013/02/01) 誤差回避のため、doubleではなくdecimalで計算する
    333         * @og.rev 5.6.8.1 (2013/09/13) 1行目が合計されていなかったので修正
    334         * @og.rev 6.0.2.1 (2014/09/26) queryタグが複数あり、mainTrans=false で制御されていない場合、エラーが発生する対応
    335         *
    336         * @param       maxRowCount 最大検索件数
    337         * @param       resource リソースマネージャー
    338         * @param       sumFilter 集計項目フィルター
    339         * @param       groupFilter グループキーフィルター
    340         * @param       subTotalFilter 小計キーフィルター
    341         * @param       totalFilter 合計キーフィルター
    342         *
    343         * @return      オーバーフローしたかどうか(最大件数が超えた場合でかつ次のデータがある場合は、true)
    344         */
    345        private boolean addTotalRows( final int maxRowCount, final ResourceManager resource
    346                                                                        , final boolean[] sumFilter
    347                                                                        , final boolean[] groupFilter
    348                                                                        , final boolean[] subTotalFilter
    349                                                                        , final boolean[] totalFilter ) {
    350
    351                final String subTotalLabel      = ( config.useSubTotal()   ? resource.makeDBColumn( "EDIT_SUBTOTAL_VALUE"   ).getLongLabel() : null );
    352                final String totalLabel         = ( config.useTotal()      ? resource.makeDBColumn( "EDIT_TOTAL_VALUE"      ).getLongLabel() : null );
    353                final String grandTotalLabel= ( config.useGrandTotal() ? resource.makeDBColumn( "EDIT_GRANDTOTAL_VALUE" ).getLongLabel() : null );
    354
    355                int numberOfRows = getRowCount();
    356                final int sumClmCount  = config.getSumClmCount();
    357                BigDecimal subTotalSum[]   = new BigDecimal[sumClmCount]; // 5.6.1.0 (2013/02/01)
    358                BigDecimal totalSum[]      = new BigDecimal[sumClmCount];
    359                BigDecimal grandTotalSum[] = new BigDecimal[sumClmCount];
    360
    361                String lastSubTotalKey = null;
    362                String lastTotalKey    = null;
    363
    364                int subTotalCount   = 0;
    365                int totalCount      = 0;
    366                int grandTotalCount = 0;
    367                int rowCount =0;
    368
    369                int tblIdx = 0;
    370                final StringBuilder groupKey    = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
    371                final StringBuilder subTotalKey = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
    372                final StringBuilder totalKey    = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
    373                while( numberOfRows < maxRowCount && tblIdx < getRowCount() ) {
    374                        BigDecimal[] sumVals      = new BigDecimal[sumClmCount];        // 5.6.1.0 (2013/02/01)
    375                        groupKey.setLength(0);                                                                                                                  // 6.1.0.0 (2014/12/26) refactoring
    376                        subTotalKey.setLength(0);                                                                                                               // 6.1.0.0 (2014/12/26) refactoring
    377                        totalKey.setLength(0);                                                                                                                  // 6.1.0.0 (2014/12/26) refactoring
    378
    379                        int sc = 0;
    380                        for( int column=0; column<numberOfColumns; column++ ) {
    381                                final String val = getValue( tblIdx, column );
    382                                if( groupFilter[column] )               { groupKey.append( val ).append( JS ); }
    383                                // 7.0.5.0 (2019/09/09) アンダーバー付きの文字列から、アンダーバーを削除する。
    384                //              if( sumFilter[column] )                 { sumVals[sc++] = ( val != null && val.length() > 0 ? new BigDecimal( val ) : BigDecimal.ZERO ); } // 5.6.1.0 (2013/02/01)
    385                                if( sumFilter[column] )                 { sumVals[sc++] = new BigDecimal( unscoDel( val,"0" ) ) ; }                                                     // 7.0.5.0 (2019/09/09)
    386                                if( subTotalFilter[column] )    { subTotalKey.append( val ).append( JS ); }
    387                                if( totalFilter[column] )               { totalKey.append( val ).append( JS ); }
    388                                // 7.0.5.0 (2019/09/09) アンダーバー付きの文字列から、アンダーバーを削除する。
    389                //              if( column == rowCountColumn )  { rowCount = ( val != null && val.length() > 0 ? Integer.parseInt( val ) : 0 ); }       // 6.0.2.4 (2014/10/17) メソッド間違い
    390                                if( column == rowCountColumn )  { rowCount = Integer.parseInt( unscoDel( val,"0" ) ); }                                                         // 7.0.5.0 (2019/09/09)
    391                        }
    392
    393                        // 小計キーブレイク処理
    394                        if( numberOfRows < maxRowCount && config.useSubTotal() && lastSubTotalKey != null && lastSubTotalKey.length() > 0
    395                                && !lastSubTotalKey.equals( subTotalKey.toString() ) ) {
    396                                addRow( subTotalFilter, subTotalLabel, subTotalCount, sumFilter, subTotalSum, tblIdx );
    397                                subTotalSum = new BigDecimal[sumClmCount]; // 5.6.1.0 (2013/02/01)
    398                                subTotalCount = 0;
    399                                numberOfRows++;
    400                                tblIdx++;
    401                        }
    402
    403                        // 合計キーブレイク処理
    404                        if( numberOfRows < maxRowCount && config.useTotal() && lastTotalKey != null && lastTotalKey.length() > 0
    405                                && !lastTotalKey.equals( totalKey.toString() ) ) {
    406                                addRow( totalFilter, totalLabel, totalCount, sumFilter, totalSum, tblIdx );
    407                                totalSum = new BigDecimal[sumClmCount]; // 5.6.1.0 (2013/02/01)
    408                                totalCount = 0;
    409                                numberOfRows++;
    410                                tblIdx++;
    411                        }
    412
    413                        // 小計、合計、総合計単位に集計項目の合計値を加算します。
    414                        // 6.0.2.0 (2014/09/19) BigDecimal.ZERO.add で、null エラーが発生するのは、query が複数あり、mainTrans=false で制御されていない場合
    415                        for( int cnt=0; cnt<sc; cnt++ ) {
    416                                subTotalSum[cnt]        = subTotalSum[cnt]      == null ? BigDecimal.ZERO.add(sumVals[cnt]) : subTotalSum[cnt].add(  sumVals[cnt]); // 5.6.8.1 (2013/09/13)
    417                                totalSum[cnt]           = totalSum[cnt]         == null ? BigDecimal.ZERO.add(sumVals[cnt]) : totalSum[cnt].add(     sumVals[cnt]);
    418                                grandTotalSum[cnt]      = grandTotalSum[cnt]== null ? BigDecimal.ZERO.add(sumVals[cnt]) : grandTotalSum[cnt].add(sumVals[cnt]);
    419                        }
    420
    421                        lastSubTotalKey = subTotalKey.toString();
    422                        lastTotalKey    = totalKey.toString();
    423
    424                        // グループ集計時はグルーピングした行数を加算する。
    425                        int gcnt = 1;
    426                        if( config.useGroup() && rowCountColumn >= 0 && rowCount > 0 ) {
    427                                gcnt = rowCount;
    428                        }
    429                        subTotalCount += gcnt;
    430                        totalCount    += gcnt;
    431                        grandTotalCount += gcnt;
    432
    433                        tblIdx++;
    434                }
    435
    436                // 最大件数が、超えた場合でかつ次のデータがある場合は、オーバーフロー
    437                boolean isOverFlow = tblIdx < getRowCount() ;
    438
    439                // 小計キー最終行処理
    440                if( config.useSubTotal() && lastSubTotalKey != null ) {
    441                        if( numberOfRows < maxRowCount ) {
    442                                addRow( subTotalFilter, subTotalLabel, subTotalCount, sumFilter, subTotalSum, tblIdx );
    443                                numberOfRows++;
    444                                tblIdx++;
    445                        }
    446                        else {
    447                                isOverFlow = true;
    448                        }
    449                }
    450
    451                // 合計キー最終行処理
    452                if( config.useTotal() && lastTotalKey != null ) {
    453                        if( numberOfRows < maxRowCount ) {
    454                                addRow( totalFilter, totalLabel, totalCount, sumFilter, totalSum, tblIdx );
    455                                numberOfRows++;
    456                                tblIdx++;
    457                        }
    458                        else {
    459                                isOverFlow = true;
    460                        }
    461                }
    462
    463                // 総合計処理
    464                if( config.useGrandTotal() && numberOfRows > 0 ) {
    465                        if( numberOfRows < maxRowCount ) {
    466                                final boolean[] grandTotalFilter = new boolean[numberOfColumns];
    467                                // 総合計のラベル表示廃止
    468                                // grandTotalFilter[0] = true;
    469
    470                                if( config.useFirstTotal() ) {          // 6.1.1.0 (2015/01/17)
    471                                        addRow( grandTotalFilter, grandTotalLabel, grandTotalCount, sumFilter, grandTotalSum, 0 );
    472                                }
    473                                else {
    474                                        addRow( grandTotalFilter, grandTotalLabel, grandTotalCount, sumFilter, grandTotalSum, tblIdx );
    475                                        tblIdx++;
    476                                }
    477                                numberOfRows++;
    478                        }
    479                        else {
    480                                isOverFlow = true;
    481                        }
    482                }
    483
    484                if( isOverFlow ) {
    485                        setOverflow( true );
    486                }
    487
    488                return isOverFlow;
    489        }
    490
    491        /**
    492         * キーの値配列、集計値の配列を引数として、追加行を生成し、DBTableModelに追加します。
    493         * キー、及び集計値がDBTableModel上のどのカラムに位置するかは、キーフィルタ、集計フィルタで指定します。
    494         *
    495         * @og.rev 5.6.1.0 (2013/02/01) doubleをdecimalに
    496         *
    497         * @param keyFilter キーフィルタ
    498         * @param keyVals キーの値配列
    499         * @param keyCount 集計した行のカウント
    500         * @param sumFilter 集計フィルタ
    501         * @param sumVals 集計値配列
    502         * @param aRow 挿入する行番号
    503         */
    504        private void addRow( final boolean[] keyFilter, final String[] keyVals, final int keyCount
    505                        , final boolean[] sumFilter, final BigDecimal[] sumVals, final int aRow ) {
    506                String[] columnValues = new String[numberOfColumns];
    507                int sc = 0;
    508                int kc = 0;
    509                for( int column=0; column<numberOfColumns; column++ ) {
    510                        String val = "";
    511                        if( keyFilter[column] ) {
    512                                val = keyVals[kc++];
    513                        }
    514                        if( sumFilter[column] ) {
    515                                synchronized( FORMAT ) {                                                        // 7.2.9.5 (2020/11/28)
    516                                        val = FORMAT.format( sumVals[sc++] );
    517                                }
    518                        }
    519                        if( column == rowCountColumn ) {
    520                                val = String.valueOf( keyCount );
    521                        }
    522                        columnValues[column] = val;
    523                }
    524
    525                if( aRow < 0 ) {
    526                        addColumnValues( columnValues );
    527                }
    528                else {
    529                        addValues( columnValues, aRow, false );
    530                }
    531        }
    532
    533        /**
    534         * キーの値配列、集計値の配列を引数として、追加行を生成し、DBTableModelに追加します。
    535         * キー、及び集計値がDBTableModel上のどのカラムに位置するかは、キーフィルタ、集計フィルタで指定します。
    536         *
    537         * @og.rev 5.6.1.0 (2013/02/01) doubleをbigDecimal
    538         *
    539         * @param keyFilter キーフィルタ
    540         * @param keyVals キーの値配列
    541         * @param keyCount 集計した行のカウント
    542         * @param sumFilter 集計フィルタ
    543         * @param sumVals 集計値配列
    544         */
    545        private void addRow( final boolean[] keyFilter, final String[] keyVals, final int keyCount
    546                        , final boolean[] sumFilter, final BigDecimal[] sumVals ) {
    547                addRow( keyFilter, keyVals, keyCount, sumFilter, sumVals, -1 );
    548        }
    549
    550        /**
    551         * キーの値、集計値の配列を引数として、追加行を生成し、DBTableModelに追加します。
    552         * キー、及び集計値がDBTableModel上のどのカラムに位置するかは、キーフィルタ、集計フィルタで指定します。
    553         *
    554         * @og.rev 5.6.1.0 (2013/02/01) doubleをbigDecimalに
    555         *
    556         * @param keyFilter キーフィルタ
    557         * @param keyVal キーの値
    558         * @param keyCount 集計した行のカウント
    559         * @param sumFilter 集計フィルタ
    560         * @param sumVals 集計値配列
    561         * @param aRow 挿入する行番号
    562         */
    563        private void addRow( final boolean[] keyFilter, final String keyVal, final int keyCount
    564                        , final boolean[] sumFilter, final BigDecimal[] sumVals, final int aRow ) {
    565                final List<String> tmp = new ArrayList<>();
    566                for( int column=0; column<numberOfColumns; column++ ) {
    567                        if( keyFilter[column] ) {
    568                                tmp.add( keyVal );
    569                        }
    570                }
    571                addRow( keyFilter, tmp.toArray( new String[tmp.size()] ), keyCount, sumFilter, sumVals, aRow );
    572        }
    573
    574        /**
    575         * アンダーバー付きの文字列から、アンダーバーを削除します。
    576         * null,ゼロ文字列の場合は、第二引数の値を返します。
    577         *
    578         * @og.rev 7.0.5.0 (2019/09/09) アンダーバー付きの文字列から、アンダーバーを削除する。
    579         *
    580         * @param    inStr 基準となる文字列
    581         * @param    def デフォルト文字列
    582         *
    583         * @return  null、ゼロ文字列、"_"の場合は、デフォルト文字列を、そうでなければ、入力文字からアンダーバーを削除した文字列を返す。
    584         */
    585        public static String unscoDel( final String inStr,final String def ) {
    586                final String rtn = StringUtil.nval2( inStr,def );
    587
    588                return rtn.charAt(0) == '_' ? rtn.substring(1) : rtn ;
    589        }
    590}