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.table;
017
018import org.opengion.hayabusa.db.AbstractTableFilter;
019import org.opengion.hayabusa.db.DBTableModel;
020
021import org.opengion.fukurou.util.ErrorMessage;
022import org.opengion.fukurou.util.StringUtil;
023
024import java.util.Map;
025import java.util.HashMap;
026
027/**
028 * TableFilter_KEY_BREAK は、TableFilter インターフェースを継承した、DBTableModel 処理用の
029 * 実装クラスです。
030 *
031 * ここでは、指定のカラムに対して、キーブレイクが発生したときのデータのみ、残します。
032 * キーブレイクは、グループキーと、ブレークキーがあります。
033 * グループキーは、一塊のレコードを管理し、グループキーごとに、キーブレイクを判定します。
034 * つまり、グループキーの並び順に依存しない形で、キーブレイク可能です。
035 * 例:機種と日付と、状況Fがあったとして、日付、機種、状況F でソートし、機種をグループキー、
036 * 状況Fをブレイクキーとすれば、日付の順に、機種の中で、状況Fがブレークしたときのみ、
037 * データを残す、ということが可能になります。
038 *
039 * GRP_KEY  : グループの判定を行うカラムを、CSV形式で設定します。
040 * BRK_KEY  : キーブレイクの判定を行うカラムを、CSV形式で設定します。
041 * USE_LAST : キーブレイクと関係なく、グループの最後のデータを登録するかどうかを指定します。(初期値:false 登録しない)
042 *
043 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
044 *
045 * @og.formSample
046 * ●形式:
047 *      ① <og:tableFilter classId="KEY_BREAK"
048 *                              keys="GRP_KEY,BRK_KEY,USE_LAST"
049 *                              vals='"CLM1,CLM2....","CLM5,CLM6....",true' />
050 *
051 *      ② <og:tableFilter classId="KEY_BREAK" >
052 *               {
053 *                   GRP_KEY  : CLM1,CLM2....   ;
054 *                   BRK_KEY  : CLM5,CLM6....   ;
055 *                   USE_LAST : true            ;
056 *               }
057 *         </og:tableFilter>
058 *
059 * @og.rev 6.7.9.1 (2017/05/19) 新規追加
060 *
061 * @version  6.7  2017/05/19
062 * @author   Kazuhiko Hasegawa
063 * @since    JDK1.8,
064 */
065public class TableFilter_KEY_BREAK extends AbstractTableFilter {
066        /** このプログラムのVERSION文字列を設定します。   {@value} */
067        private static final String VERSION = "6.7.9.1 (2017/05/19)" ;
068
069        /**
070         * デフォルトコンストラクター
071         *
072         * @og.rev 6.7.9.1 (2017/05/19) 新規追加
073         */
074        public TableFilter_KEY_BREAK() {
075                super();
076                initSet( "GRP_KEY"      , "グループの判定を行うカラムを、CSV形式で設定します。" );
077                initSet( "BRK_KEY"      , "キーブレイクの判定を行うカラムを、CSV形式で設定します。" );
078                initSet( "USE_LAST"     , "キーブレイクと関係なく、グループの最後のデータを登録するかどうかを指定します。(初期値:false 登録しない)"            );
079        }
080
081        /**
082         * DBTableModel処理を実行します。
083         *
084         * @og.rev 6.7.9.1 (2017/05/19) 新規追加
085         *
086         * @return 処理結果のDBTableModel
087         */
088        public DBTableModel execute() {
089                final String[]  grpClms = StringUtil.csv2Array( getValue( "GRP_KEY" ) );
090                final String[]  brkClms = StringUtil.csv2Array( getValue( "BRK_KEY" ) );
091
092                final boolean   useLast = StringUtil.nval( getValue( "USE_LAST"  ), false ) ;
093
094                final DBTableModel table  = getDBTableModel();
095        //      final DBTableModel rtnTbl = table.newModel();
096
097                final int[]    grpClmNo = new int[grpClms.length];              // グループキーカラムの番号
098                final int[]    brkClmNo = new int[brkClms.length];              // ブレイクキーカラムの番号
099
100                for( int i=0; i<grpClms.length; i++ ) {
101                        grpClmNo[i] = table.getColumnNo( grpClms[i],false );    // カラムが存在しなければ、-1
102                }
103
104                for( int i=0; i<brkClms.length; i++ ) {
105                        brkClmNo[i] = table.getColumnNo( brkClms[i],false );    // カラムが存在しなければ、-1
106                }
107
108                final int rowCnt = table.getRowCount();
109
110                final Map<String,String> brkKeysMap = new HashMap<>() ;         // グループキー単位の最後のブレークキー
111
112                String[] data    = null;        // エラー時に表示するため。
113                for( int row=0; row<rowCnt; row++ ) {
114                        try {
115                                data = table.getValues( row );
116
117                                final String grpKeys    = getKeys( grpClmNo , data );   // グループキー
118                                final String oldBlkKeys = brkKeysMap.getOrDefault( grpKeys , "" );              // Mapにキーが無い場合は、空文字列
119
120                                final String brkKeys = getKeys( brkClmNo , data );      // ブレークキー
121                                if( oldBlkKeys.equalsIgnoreCase( brkKeys ) ) {
122                                        table.rowDelete( row );                                 // キーが同じなので、削除フラグを立てる。(論理削除)
123                                }
124                                else {
125                                        brkKeysMap.put( grpKeys , brkKeys );    // キーが異なるので、Mapにセーブします。
126                                }
127                        }
128                        catch( final RuntimeException ex ) {
129                                // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
130                                makeErrorMessage( "TableFilter_KEY_BREAK Error",ErrorMessage.NG )
131                                        .addMessage( row+1,ErrorMessage.NG,"KEY_BREAK"
132                                                , StringUtil.array2csv( data )
133                                        )
134                                        .addMessage( ex );
135                        }
136                }
137
138                // レコードを削除するので、逆順に行います。
139                for( int row=rowCnt-1; row>=0; row-- ) {
140                        final String modType = table.getModifyType( row );
141                        if( DBTableModel.DELETE_TYPE.equals( modType ) ) {                      // 削除対象 (DELETE_TYPE は、fukurou.model.DataModel で定義
142                                if( useLast ) {                                                                                 // 若干複雑
143                                        data = table.getValues( row );
144                                        final String grpKeys = getKeys( grpClmNo , data );      // グループキー
145                                        if( brkKeysMap.containsKey( grpKeys ) ) {                       // brkKeysMap を流用。useLast の場合、逆順で
146                                                brkKeysMap.remove( grpKeys );                                   // 最後のグループキーのデータは、
147                                                continue;                                                                               // 削除しないので、continue する。
148                                        }
149                                }
150                                table.removeValue( row );       // 物理削除
151                        }
152                }
153
154                if( useLast ) { table.resetModify(); }  // useLast 使用時には、DELETEが残るため、一応クリアしておきます。
155
156                return table;
157        }
158
159        /**
160         * キーの配列アドレスと、1行分のデータ配列から、キーとなる文字列を作成します。
161         *
162         * @og.rev 6.7.9.1 (2017/05/19) 新規追加
163         *
164         * @param clms キーの配列アドレス
165         * @param rowData 1行分のデータ配列
166         * @return キーとなる文字列
167         */
168        private String getKeys( final int[] clms , final String[] rowData ) {
169                final StringBuilder buf = new StringBuilder();
170                for( int i=0; i<clms.length; i++ ) {
171                        if( clms[i] >= 0 ) {
172                                buf.append( rowData[clms[i]] ).append( ':' );
173                        }
174                }
175                return buf.toString();
176        }
177}