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.taglib;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.db.DBColumn;
021import org.opengion.hayabusa.db.DBTableModel;
022import org.opengion.hayabusa.db.DBTableModelUtil;
023import org.opengion.fukurou.system.OgBuilder ;                                  // 6.4.4.1 (2016/03/18)
024import org.opengion.fukurou.util.StringUtil;
025import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
026import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
027
028import static org.opengion.fukurou.util.StringUtil.nval ;
029
030import java.util.Set;                                                                                   // 6.4.3.4 (2016/03/11)
031import java.util.Map;
032import java.util.LinkedHashMap;
033import java.util.Locale ;
034import java.util.List;
035import java.util.ArrayList;
036
037/**
038 * 2つの DBTableModel の 集合処理を行うタグです。
039 *
040 * マスタとスレーブのそれぞれの DBTableModel を取得し、マージ、差分、排他などの
041 * データ処理を行います。結果を、指定の tableId と scope に書き出します。
042 * マスタテーブルは、masterTableId と masterScope より取り出します。スレーブテーブルは、
043 * slaveTableId と slaveScope より取り出します。
044 * 取り出された DBTableModel は、マスタテーブルに対して、スレーブテーブル情報を参照する形で行われ、
045 * 結果はマスタテーブルに上書きされる形で行われます。
046 * 指定できるアクションは、全体集合(UNION_ALL)、和集合(UNION)、積集合(INTERSECT)、
047 * 差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、 列追加(ADD_CLM)、 グループ(GROUP)です。
048 * 列合成と列追加、グループ以外の処理では、カラム順とカラム数は同数でなければなりません。
049 * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。
050 * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。
051 * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。)
052 * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の
053 * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、
054 * 2回目に現れた情報を削除することになります。グループ が指定された場合は、
055 * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで
056 * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで
057 * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。
058 * (マージ後には、同一主キーを持つ行は存在します。)
059 * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、
060 * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。
061 *
062 * 6.9.1.0 (2018/02/26)
063 *   UNION_VAL は、スレーブテーブルの縦列を横列に割り当てるUNION_CLMの特殊版です。
064 *   処理としては、そのまま割り当てるのではなく、スレーブテーブルの縦列の値と
065 *   ヘッダーとして指定する、unionClms の値を比較して、下限値の最大値を割り当てます。
066 *   つまり、ヘッダーの値と同じか、それ以下で、最も近い値を割り当てることになります。
067 *   これは、時間的な推移を表現することになります。
068 *     masterKeys:マスタとスレーブを関連付ける共通のキーとなるカラム
069 *     slaveKeys :スレーブの縦列を横列にするための、キーと値のカラム
070 *     unionClms :横列にするカラム名。この値とスレーブのキーと比較してセットするかどうかを決めます。
071 *
072 * 処理前後でのDBTableModelの件数は、以下の変数で取得することが可能です。
073 *  処理前のマスターテーブル : {@DB.MASTER_COUNT}
074 *  処理前のスレーブテーブル : {@DB.SLAVE_COUNT}
075 *  処理後                   : {@DB.COUNT}
076 *
077 * @og.formSample
078 * ●形式:
079 *       ・<og:tableMerge
080 *             action        = "UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|UNION_VAL|ADD_CLM|GROUP|UNION_SELROW|CDKH|TABLE_REMOVE"
081 *             tableId       = "DEFAULT"        出力テーブルの tableId
082 *             scope         = "session"        出力テーブルの scope
083 *             masterTableId = "DEFAULT"        マスタテーブルの tableId
084 *             masterScope   = "session"        マスタテーブルの scope
085 *             slaveTableId  = "DEFAULT"        スレーブテーブルの tableId
086 *             slaveScope    = "request"        スレーブテーブルの scope
087 *             masterKeys    = "AA,BB,CC"       マスタテーブルの集合処理を行う主キー
088 *             slaveKeys     = "AA,BB,CC"       スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
089 *             diffKeys      = "DD,EE"          マスタテーブルのDIFFERENCE時の差分カラム名
090 *             unionClms     = "DD,EE"          UNION_CLM,ADD_CLM時にスレーブからマスタへ追加するカラム名
091 *             unionLbls     = "dd,ee"          UNION_CLM,ADD_CLM時にスレーブからマスタへ追加するカラムのラベル名 6.9.1.0 (2018/02/26)
092 *             modifyClms    = "FF,GG"          DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名
093 *             noSideEffect  = "false"          テーブルモデルに対する副作用(true:ない/false:ある)
094 *             useDiffData   = "true"           DIFFERENCE時に差分のスレーブデータを追加するかどうか
095 *             useCheckOnly  = "false"          マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
096 *             groupAddClms  = "FF,GG"          masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
097 *             display       = "true"           処理概要を表示するかどうか
098 *         />
099 * ●body:なし
100 *
101 * ●Tag定義:
102 *   <og:tableMerge
103 *       command            【TAG】コマンド (NEW,RENEW)をセットします(初期値:NEW)
104 *       action             【TAG】アクションを指定します(UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|UNION_VAL|ADD_CLM|GROUP|UNION_SELROW|CDKH|TABLE_REMOVE)
105 *       tableId            【TAG】出力先のtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
106 *       scope              【TAG】出力先のscopeを指定します(初期値:session)
107 *       masterTableId      【TAG】マスタテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
108 *       masterScope        【TAG】マスタテーブルのscopeを指定します(初期値:session)
109 *       slaveTableId       【TAG】スレーブテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
110 *       slaveScope         【TAG】スレーブテーブルのscopeを指定します(初期値:session)
111 *       masterKeys         【TAG】マスタテーブルの集合処理を行う主キーを指定します
112 *       slaveKeys          【TAG】スレーブテーブルの集合処理を行う主キーを指定します
113 *       diffKeys           【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します
114 *       unionClms          【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します
115 *       unionLbls          【TAG】スレーブからマスタへ追加するラベルをCSV形式で指定します   6.9.1.0 (2018/02/26)
116 *       nullDelClms        【TAG】指定の値がすべてnull(空文字列)の場合、レコードを削除するカラムをCSV形式で指定します
117 *       useSlaveLoop       【TAG】scope="request" のスレーブテーブルを、ループ処理して使用します(action="TABLE_REMOVE" と、"UNION_CLM" で、利用します)
118 *       modifyClms         【TAG】DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名をCSV形式で指定します
119 *       groupAddClms       【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します
120 *       noSideEffect       【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある)
121 *       useDiffData        【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true)
122 *       useCheckOnly       【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうかを指定します(初期値:false)
123 *       stopZero           【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])
124 *       display            【TAG】マージの結果を表示するかどうかを指定します(初期値:true)
125 *       mainTrans          【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false)
126 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
127 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
128 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
129 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
130 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
131 *       separator          【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")
132 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
133 *   />
134 *
135 * ●使用例
136 *     例1)デフォルト以外に必要な属性のみ指定するサンプル
137 *     <og:tableMerge action="UNION"
138 *           slaveScope = "request" masterKeys = "AA,BB,CC"
139 *     />
140 *
141 *         ・出力先、マスターともに初期値は、tableId="DEFAULT" scope="session" です。
142 *           スレーブは、tableId か scope をける必要がある為、scope="request" を指定しています。
143 *           比較するカラム名は、マスタ、スレーブ同じであれば、マスタのみ指定でかまいません。
144 *
145 *     例2)マスタ、スレーブともメモリに残らないように request で作成します。
146 *     <og:tableMerge action="INTERSECT"
147 *           masterScope  = "request"
148 *           slaveScope   = "request"
149 *           slaveTableId = "SLAVE"
150 *           masterKeys   = "AA,BB,CC"
151 *     />
152 *
153 *         ・マスタ、スレーブともメモリに残らないように request で作成した場合は、
154 *           どちらかの TableId を変える必要があります。
155 *           出力先は初期値の、tableId="DEFAULT" scope="session" です。
156 *
157 * @og.rev 3.8.0.9 (2005/10/17) 新規追加
158 * @og.group DB登録
159 *
160 * @version  0.9.0      2000/10/17
161 * @author       Kazuhiko Hasegawa
162 * @since    JDK5.0,
163 */
164public class TableMergeTag extends CommonTagSupport {
165        /** このプログラムのVERSION文字列を設定します。   {@value} */
166        private static final String VERSION = "6.9.1.0 (2018/02/26)" ;
167        private static final long serialVersionUID = 691020180226L ;
168
169        /** action 引数に渡す事の出来る アクションコマンド  全体集合 {@value} */
170        public static final String ACT_UNION_ALL        = "UNION_ALL" ;
171        /** action 引数に渡す事の出来る アクションコマンド  和集合 {@value} */
172        public static final String ACT_UNION            = "UNION" ;
173        /** action 引数に渡す事の出来る アクションコマンド  積集合 {@value} */
174        public static final String ACT_INTERSECT        = "INTERSECT" ;
175        /** action 引数に渡す事の出来る アクションコマンド  差集合{@value} */
176        public static final String ACT_MINUS            = "MINUS" ;
177        /** action 引数に渡す事の出来る アクションコマンド  差分集合{@value} */
178        public static final String ACT_DIFFERENCE       = "DIFFERENCE" ;
179        /** action 引数に渡す事の出来る アクションコマンド  列合成{@value} */
180        public static final String ACT_UNION_CLM        = "UNION_CLM" ;
181        /** action 引数に渡す事の出来る アクションコマンド  列合成{@value} */
182        public static final String ACT_UNION_VAL        = "UNION_VAL" ;                                 // 6.9.1.0 (2018/02/26)
183        /** action 引数に渡す事の出来る アクションコマンド  列合成{@value} */
184        public static final String ACT_ADD_CLM          = "ADD_CLM" ;
185        /** action 引数に渡す事の出来る アクションコマンド  グループ {@value} */
186        public static final String ACT_GROUP            = "GROUP" ;
187        /** action 引数に渡す事の出来る アクションコマンド  グループ {@value} */
188        public static final String ACT_UNION_SELROW     = "UNION_SELROW" ;                              // 4.3.2.0 (2008/09/11)
189        /** action 引数に渡す事の出来る アクションコマンド  改廃コード {@value} */
190        public static final String ACT_CDKH                     = "CDKH" ;                                              // 6.3.1.1 (2015/07/10)
191        /** action 引数に渡す事の出来る アクションコマンド  DBTableModelの削除 {@value} */
192        public static final String ACT_TABLE_REMOVE     = "TABLE_REMOVE" ;                              // 6.7.3.0 (2017/01/27)
193
194        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
195        private static final Set<String> ACTION_SET = new ArraySet<>( ACT_UNION_ALL , ACT_UNION , ACT_INTERSECT , ACT_MINUS , ACT_DIFFERENCE , ACT_UNION_CLM , ACT_UNION_VAL ,
196                                                                                                                                  ACT_ADD_CLM , ACT_GROUP , ACT_UNION_SELROW , ACT_CDKH , ACT_TABLE_REMOVE );
197
198        /** command 引数に渡す事の出来る コマンド  新規 {@value} */
199        public static final String CMD_NEW       = "NEW" ;
200        /** command 引数に渡す事の出来る コマンド  再検索 {@value} */
201        public static final String CMD_RENEW = "RENEW" ;
202        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
203        private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_NEW , CMD_RENEW );
204
205        private String  command         = CMD_NEW;
206
207        private String  action          ;
208        private String  tableId         = HybsSystem.TBL_MDL_KEY;               // 出力先の tableId
209        private String  scope           = "session";                                    // 出力先の scope
210
211        private String  masterTableId   = HybsSystem.TBL_MDL_KEY;       // マスタテーブルの tableId
212        private String  masterScope             = "session";                            // マスタテーブルの scope
213        private String  slaveTableId    = HybsSystem.TBL_MDL_KEY;       // スレーブテーブルの tableId
214        private String  slaveScope              = "request";                            // スレーブテーブルの scope
215        private String  masterKeys              ;                                                       // マスタテーブルの集合処理を行う主キー
216        private String  slaveKeys               ;                                                       // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
217        private String  diffKeys                ;                                                       // マスタテーブルのDIFFERENCE時の差分カラム名
218        private String  unionClms               ;                                                       // 6.7.3.0 (2017/01/27) 値がnull時にレコードを削除
219        private String  unionLbls               ;                                                       // 6.9.1.0 (2018/02/26) 追加
220        private String  nullDelClms             ;                                                       // UNION_CLM時にスレーブからマスタへ追加するカラム名
221        private String  modifyClms              ;                                                       // DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名
222        private String  groupAddClms    ;                                                       // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
223        private boolean noSideEffect    ;                                                       // テーブルモデルに対する副作用(true:ない/false:ある)
224        private boolean useDiffData             = true;                                         // DIFFERENCE時に差分のスレーブデータを追加するかどうか
225        private boolean useCheckOnly    ;                                                       // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
226        private boolean display                 = true;
227        private boolean isMainTrans             = true;                                         // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
228        private String  separator               = ",";                                          // 5.3.1.0 (2011/01/01) groupAddClmnsで結合する際の区切り文字
229        private boolean stopZero                ;                                                       // 5.7.6.2 (2014/05/16) stopZero属性追加
230        private boolean useSlaveLoop    ;                                                       // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加
231
232        /**
233         * デフォルトコンストラクター
234         *
235         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
236         */
237        public TableMergeTag() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
238
239        /**
240         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
241         *
242         * @og.rev 4.3.2.0 (2008/09/11) UNION_SELROWアクション対応
243         * @og.rev 4.3.3.0 (2008/10/01) 処理前後のテーブル件数を取得できるようにする
244         * @og.rev 4.3.3.1 (2008/10/08) スレーブのnullチェック追加
245         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
246         * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
247         * @og.rev 5.7.6.2 (2014/05/16) stopZero属性、DB.COUNT キーで検索件数をリクエストにセットする。
248         * @og.rev 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。
249         * @og.rev 6.3.1.1 (2015/07/10) 改廃コードを設定する CDKH アクションを追加
250         * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。
251         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
252         * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
253         * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop,nullDelClms属性追加と、action="TABLE_REMOVE" の追加
254         * @og.rev 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加
255         *
256         * @return      後続処理の指示
257         */
258        @Override
259        public int doEndTag() {
260                debugPrint();           // 4.0.0 (2005/02/28)
261                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
262                if( !useTag() || ! check( command, COMMAND_SET ) ) { return EVAL_PAGE ; }
263
264                // 整合性チェック:アクションリストとの存在チェック
265                if( !check( action, ACTION_SET ) ) {
266                        final String errMsg = "指定のアクションは実行できません。アクションエラー"       + CR
267                                                        + "action=[" + action + "] "                                                            + CR
268                                                        + "指定可能なアクション:"
269                                                        + String.join( ", " , ACTION_SET ) ;
270                        throw new HybsSystemException( errMsg );
271                }
272
273                // 6.7.3.0 (2017/01/27) action="TABLE_REMOVE" で、DBTableModel を無条件で、削除します。
274                if( ACT_TABLE_REMOVE.equalsIgnoreCase( action ) ) {
275                        super.setScope( masterScope );
276                        removeObject( masterTableId );
277                        return EVAL_PAGE ;
278                }
279
280                // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
281                if( slaveKeys == null ) { slaveKeys = masterKeys; }
282
283                super.setScope( masterScope );
284                DBTableModel masterTable = (DBTableModel)getObject( masterTableId );
285
286                // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加と、action="TABLE_REMOVE" の追加
287                // 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined
288//              if( useSlaveLoop ) {
289//                      // 初っ端はDBTableModel が存在しないはずなので、slave のテーブルを、マスターに登録しなおす。(処理は、これだけで終了)
290//                      if( masterTable == null ) {
291                if( useSlaveLoop && masterTable == null ) {
292                                // 初っ端はDBTableModel が存在しないはずなので、slave のテーブルを、マスターに登録しなおす。(処理は、これだけで終了)
293                                // slave の scope と、tableId から取り出した DBTableModel を、master に登録する。
294                                super.setScope( slaveScope );
295                                masterTable = (DBTableModel)getObject( slaveTableId );
296                                if( masterTable != null && masterTable.getRowCount() > 0 ) {
297                                        super.setScope( masterScope );
298                                        setObject( masterTableId , masterTable );
299                                }
300                                return EVAL_PAGE ;
301//                      }
302                }
303
304                // 整合性チェック:マスタテーブルは必須
305                if( masterTable == null ) {
306                        final String errMsg = "マスタテーブルは必須です。"
307                                                + CR
308                                                + "action=[" + action + "] "
309                                                + "masterTableId=[" + masterTableId + "] "
310                                                + "masterScope=[" + masterScope + "] " ;
311                        throw new HybsSystemException( errMsg );
312                }
313                if( noSideEffect ) { masterTable = cloneTable( masterTable ); }
314                // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。
315                final Map<String,Integer> masterMap = makeKeyMap( masterTable,masterKeys,useCheckOnly,true );   // useGroup=true(行う) なので、masterTable は副作用が発生する。
316
317                DBTableModel slaveTable = null;
318                Map<String,Integer> slaveMap = null;
319
320                // 整合性チェック:action="GROUP" と "ADD_CLM" 以外では、スレーブテーブルは必須
321                // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加
322                if( ! ACT_GROUP.equalsIgnoreCase( action ) && !ACT_ADD_CLM.equalsIgnoreCase( action ) ) {
323                        super.setScope( slaveScope );
324                        slaveTable = (DBTableModel)getObject( slaveTableId );
325
326                        if( slaveTable == null ) {
327                                final String errMsg = "action=[" + action + "] 時には、スレーブテーブルが必要です。"
328                                                        + CR
329                                                        + "slaveTableId=[" + slaveTableId + "] "
330                                                        + "slaveScope=[" + slaveScope + "] " ;
331                                throw new HybsSystemException( errMsg );
332                        }
333                        if( noSideEffect ) { slaveTable = cloneTable( slaveTable ); }
334                        // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。
335                        slaveMap = makeKeyMap( slaveTable,slaveKeys,false,false );      // スレーブはuseCheckOnlyしない、グループ化しない。
336
337                        // その場合、マスタとスレーブが同一メモリ上のテーブル(物理的に同じ)でない事。
338                        if( slaveTable == masterTable ) {
339                                final String errMsg = "マスタとスレーブが同一メモリになっています。"
340                                                        + "通常、検索結果は tableId と scope を別々に指定するか、noSideEffect=true(副作用なし)を、指定してください。"
341                                                        + CR
342                                                        + "action=[" + action + "] "
343                                                        + "masterTableId=[" + masterTableId + "] "
344                                                        + "slaveTableId=[" + slaveTableId + "] "
345                                                        + "masterScope=[" + masterScope + "] "
346                                                        + "slaveScope=[" + slaveScope + "] " ;
347                                throw new HybsSystemException( errMsg );
348                        }
349                }
350
351                super.setScope( scope );
352                useMainTrans( isMainTrans );                    // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
353                startQueryTransaction( tableId );               // 3.6.0.8 (2004/11/19)
354
355                // 設定値の整合性チェック
356                // 整合性チェック:action="UNION_ALL" と "UNION_VAL" と "GROUP"と "ADD_CLM"と "UNION_CLM" 以外では、masterKeys が必須
357                // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加
358                if( ! ACT_UNION_ALL.equalsIgnoreCase( action )
359                        && !ACT_UNION_VAL.equalsIgnoreCase( action )    // 6.9.1.0 (2018/02/26)
360                        && !ACT_UNION_SELROW.equalsIgnoreCase( action ) // 4.3.2.0 (2008/09/11)
361                        && !ACT_GROUP.equalsIgnoreCase( action )
362                        && !ACT_ADD_CLM.equalsIgnoreCase( action )
363                        && !ACT_UNION_CLM.equalsIgnoreCase( action )
364                        && masterKeys == null ) {
365                                final String errMsg = "action=\"" + action + "\" 時には、masterKeys が必須です。"
366                                                        + CR
367                                                        + "masterKeys=[" + masterKeys + "] ";
368                                throw new HybsSystemException( errMsg );
369                }
370
371                // 設定値の整合性チェック
372                // 整合性チェック:action="DIFFERENCE" では、diffKeys が必須
373                if( ACT_DIFFERENCE.equalsIgnoreCase( action ) && diffKeys == null ) {
374                        final String errMsg = "action=\"" + action + "\" 時には、diffKeys が必須です。"
375                                                + CR
376                                                + "diffKeys=[" + diffKeys + "] ";
377                        throw new HybsSystemException( errMsg );
378                }
379
380                // 設定値の整合性チェック
381                // 整合性チェック:列合成(UNION_CLM) と 値列合成(UNION_VAL) と グループ(GROUP) と 列追加(ADD_CLM) と 積集合(INTERSECT) と 差集合(MINUS) 以外では、
382                //                 テーブルカラム数が同じである必要がある。
383                // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加
384                if( ! ACT_UNION_CLM.equalsIgnoreCase( action )
385                         && !ACT_UNION_VAL.equalsIgnoreCase( action )           // 6.9.1.0 (2018/02/26)
386                         && !ACT_GROUP.equalsIgnoreCase( action )
387                         && !ACT_ADD_CLM.equalsIgnoreCase( action )
388                         && !ACT_INTERSECT.equalsIgnoreCase( action )           // 6.3.1.1 (2015/07/10) INTERSECT と MINUS を条件に追加
389                         && !ACT_MINUS.equalsIgnoreCase( action ) ) {           // 6.3.1.1 (2015/07/10) INTERSECT と MINUS を条件に追加
390                        final int mClmSize = masterTable.getColumnCount();
391                        final int sClmSize = slaveTable.getColumnCount();
392
393                        if( mClmSize != sClmSize ) {
394                                final String errMsg = "action=\"" + action + "\" 時には、テーブルカラム数が異なってはいけません。"
395                                                        + CR
396                                                        + "Master=" + mClmSize + " ,["
397                                                        + StringUtil.array2csv( masterTable.getNames() ) + "]"
398                                                        + CR
399                                                        + "Slave =" + sClmSize + " ,["
400                                                        + StringUtil.array2csv( slaveTable.getNames() ) + "]";
401                                throw new HybsSystemException( errMsg );
402                        }
403                }
404
405                // 6.4.4.1 (2016/03/18) display == true 時の出力を後ろにします。
406                final String MST_CNT = String.valueOf( masterTable.getRowCount() );
407                final String SLV_CNT = slaveTable == null ? null : String.valueOf( slaveTable.getRowCount() );
408
409                setRequestAttribute( "DB.MASTER_COUNT"   , MST_CNT );
410                if( SLV_CNT != null ) {
411                        setRequestAttribute( "DB.SLAVE_COUNT", SLV_CNT );
412                }
413
414                DBTableModel table = null;
415                if( ACT_UNION_ALL.equalsIgnoreCase( action ) ) {                        // 全体集合
416                        table = makeUnionAll( masterTable,slaveTable );
417                }
418                else if( ACT_UNION_SELROW.equalsIgnoreCase( action ) ) {        // 4.3.2.0 (2008/09/11) 全体集合(slave表をmaster表のチェック行から追加)
419                        table = makeUnionSelrow( masterTable,slaveTable );
420                }
421                else if( ACT_UNION.equalsIgnoreCase( action ) ) {                       // 和集合
422                        table = makeUnion( masterTable,masterMap,slaveTable,slaveMap );
423                }
424                else if( ACT_INTERSECT.equalsIgnoreCase( action ) ) {           // 積集合
425                        table = makeIntersect( masterTable,masterMap,slaveMap );
426                }
427                else if( ACT_MINUS.equalsIgnoreCase( action ) ) {                       // 差集合
428                        table = makeMinus( masterTable,masterMap,slaveMap );
429                }
430                else if( ACT_DIFFERENCE.equalsIgnoreCase( action ) ) {          // 差分集合
431                        table = makeDifference( masterTable,masterMap,slaveTable,slaveMap );
432                }
433                else if( ACT_UNION_CLM.equalsIgnoreCase( action ) ) {           // 列合成
434                        if( unionClms == null ) {
435                                final String errMsg = "action=\"UNION_CLM\" 時には、unionClms が必須です。" ;
436                                throw new HybsSystemException( errMsg );
437                        }
438
439                        table = makeUnionClm( masterTable,slaveKeys,slaveTable,slaveMap );
440                }
441                // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加
442                else if( ACT_UNION_VAL.equalsIgnoreCase( action ) ) {           // 値列合成
443                        if( unionClms == null ) {
444                                final String errMsg = "action=\"UNION_VAL\" 時には、unionClms が必須です。" ;
445                                throw new HybsSystemException( errMsg );
446                        }
447
448                        table = makeUnionVal( masterTable,masterKeys,slaveTable,slaveKeys );
449                }
450                else if( ACT_ADD_CLM.equalsIgnoreCase( action ) ) {             // 列追加
451                        if( unionClms == null ) {
452                                final String errMsg = "action=\"ADD_CLM\" 時には、unionClms が必須です。" ;
453                                throw new HybsSystemException( errMsg );
454                        }
455
456                        table = makeAddClm( masterTable );
457                }
458                else if( ACT_GROUP.equalsIgnoreCase( action ) ) {                       // グループ化は、先のレコードが残ります。
459                        table = makeGroup( masterTable );
460
461                        // 6.7.3.0 (2017/01/27) nullDelClmsで、値がnull時にレコードを削除
462                        if( nullDelClms != null && !nullDelClms.isEmpty() ) {
463                                table = deleteRows( table,nullDelClms );
464                        }
465                }
466                else if( ACT_CDKH.equalsIgnoreCase( action ) ) {                        // 6.3.1.1 (2015/07/10) CDKH
467                        table = makeCdkh(  masterTable,masterMap,slaveTable,slaveMap );
468                }
469
470                // 6.0.2.4 (2014/10/17) NP:null 値を利用している可能性があります。
471                if( table == null ) {
472                        final String errMsg = "テーブルデータを作成できませんでした。action=[" + action + "]" ;
473                        throw new HybsSystemException( errMsg );
474                }
475
476                // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
477                super.setScope( scope );
478                if( ! commitTableObject( tableId, table ) ) {
479                        return SKIP_PAGE ;
480                }
481
482                // 5.7.6.2 (2014/05/16) マージした結果の DBTableModel のデータ件数
483                final int rowCnt = table.getRowCount();
484
485                // 6.4.4.1 (2016/03/18) display == true 時の出力を後ろにします。
486                // 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
487                if( display ) {
488                        final OgBuilder buf = new OgBuilder()
489                                .append( action , "( M[" , MST_CNT , "]" )
490                                .appendIf( SLV_CNT != null
491                                                                , ",S["  , SLV_CNT , "]" )
492                                .append( " ) = [" , String.valueOf( rowCnt ) , "]" );
493
494                        jspPrint( buf.toString() );
495                }
496
497                // 5.7.6.2 (2014/05/16) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
498                setRequestAttribute( "DB.COUNT" , String.valueOf( rowCnt ) );
499
500                // 5.7.6.2 (2014/05/16) 件数0件かつ stopZero = true
501                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
502                return rowCnt == 0 && stopZero
503                                        ? SKIP_PAGE
504                                        : EVAL_PAGE ;           // ページの残りを評価する。
505        }
506
507        /**
508         * タグリブオブジェクトをリリースします。
509         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
510         *
511         * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加
512         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
513         * @og.rev 5.3.1.0 (2011/01/01) separator追加
514         * @og.rev 5.7.6.2 (2014/05/16) stopZero属性追加
515         * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop,nullDelClms属性追加
516         */
517        @Override
518        protected void release2() {
519                super.release2();
520                command                 = CMD_NEW;
521                action                  = null;
522                tableId                 = HybsSystem.TBL_MDL_KEY;       // 出力先の tableId
523                scope                   = "session";                            // 出力先の scope
524                masterTableId   = HybsSystem.TBL_MDL_KEY;       // マスタテーブルの tableId
525                masterScope             = "session";                            // マスタテーブルの scope
526                slaveTableId    = HybsSystem.TBL_MDL_KEY;       // スレーブテーブルの tableId
527                slaveScope              = "request";                            // スレーブテーブルの scope
528                masterKeys              = null;                                         // マスタテーブルの集合処理を行う主キー
529                slaveKeys               = null;                                         // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
530                diffKeys                = null;                                         // マスタテーブルのDIFFERENCE時の差分カラム名
531                unionClms               = null;                                         // スレーブからマスタへ追加するカラム名
532                unionLbls               = null;                                         // 6.9.1.0 (2018/02/26) スレーブからマスタへ追加するラベル名
533                nullDelClms             = null;                                         // 6.7.3.0 (2017/01/27) 値がnull時にレコードを削除
534                modifyClms              = null;                                         // DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名
535                groupAddClms    = null;                                         // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
536                noSideEffect    = false;                                        // テーブルモデルに対する副作用(true:ない/false:ある)
537                useDiffData             = true;                                         // DIFFERENCE時に差分のスレーブデータを追加するかどうか
538                useCheckOnly    = false;                                        // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
539                display                 = true;
540                stopZero                = false;                                        // 5.7.6.2 (2014/05/16) stopZero属性追加
541                useSlaveLoop    = false;                                        // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加
542                isMainTrans             = true;                                         // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
543                separator               = ",";
544        }
545
546        /**
547         * 指定のテーブルの指定のカラム(CSV)より、行番号をマップ化します。
548         * なお、引数のテーブルについては、useGroup属性に応じて、主キーによるグループ処理を
549         * 行うかどうか決定します。(グループ化処理を行うと、副作用が発生します。)
550         *
551         * @og.rev 4.3.2.0 (2008/09/11) マスタキー未指定時は何もしない
552         * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加
553         * @og.rev 5.3.1.0 (2011/01/01) groupAddClms の separator指定対応
554         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
555         * @og.rev 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。
556         *
557         * @param       table   マップ作成元のデータテーブル(副作用あり)
558         * @param       keys    マップ作成の主キー(CSV形式)
559         * @param       useCheckOnly    チェック行のみ対象 [true:する/false:しない]
560         * @param       useGroup                主キーによるグループ処理 [true:する/false:しない]
561         *
562         * @return マップ化された主キーと行番号
563         */
564        private Map<String,Integer> makeKeyMap( final DBTableModel table, final String keys, final boolean useCheckOnly, final boolean useGroup ) {
565
566                // カラム名をカラム番号に変換します。
567                final int[] clmNo = makeColumnNo( table,keys );
568                final int   clmSize = clmNo.length;
569                if( clmSize == 0 ) {    // マスタキー未指定時には全カラムを使用します。
570                        return null;            // 4.3.2.0 (2008/09/11)
571                }
572
573                // groupAddClms をカラム番号に変換します。
574                final int[] grpClmNo = makeColumnNo( table,groupAddClms );
575                final int   grpClmSize = grpClmNo.length;
576
577                final Map<String,Integer> tempMap = new LinkedHashMap<>();
578
579                // 注意:GROUP化の過程で テーブル件数が動的に変化します。
580                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );                   // 6.1.0.0 (2014/12/26) refactoring
581                for( int row=0; row<table.getRowCount(); row++ ) {
582                        if( useCheckOnly && table.getModifyType( row ).isEmpty() ) {
583                                continue;
584                        }
585
586                        buf.setLength(0);                                                                                                       // 6.1.0.0 (2014/12/26) refactoring
587                        for( int i=0; i<clmSize; i++ ) {
588                                buf.append( table.getValue( row,clmNo[i] ) ).append( '|' );             // 6.0.2.5 (2014/10/31) char を append する。
589                        }
590                        // 主キー連結文字列
591                        final String key = buf.toString().toUpperCase(Locale.JAPAN);            // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
592                        if( tempMap.containsKey( key ) ) {
593                                // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。
594                                if( useGroup ) {
595                                        // 5.1.4.0 (2010/03/01) groupAddClms 追加
596                                        final int orgRow = tempMap.get( key ).intValue() ;                              // 追加済みの行番号を取得
597                                        for( int i=0; i<grpClmSize; i++ ) {
598                                                final int clm    = grpClmNo[i];
599                                                final String val1 = table.getValue( orgRow,clm ) ;                      // 既存の行・カラムのデータ
600                                                final String val2 = table.getValue( row,clm ) ;                         // 削除する行・カラムのデータ
601                                                // 5.3.1.0 (2011/01/01)
602                                                table.setValueAt( val1 + separator + val2,orgRow,clm );         // CSV 連結、データを戻す。
603                                        }
604                                        table.removeValue( row );
605                                        row-- ;
606                                }
607                        }
608                        else {                                                  // まだ、未登録の場合
609                                tempMap.put( key,Integer.valueOf( row ) );
610                        }
611                }
612
613                return tempMap;
614        }
615
616        /**
617         * 指定のテーブルの指定のカラム(CSV)より、行番号をマップ化します。
618         * なお、引数のテーブルについては、useGroup属性に応じて、主キーによるグループ処理を
619         * 行うかどうか決定します。(グループ化処理を行うと、副作用が発生します。)
620         *
621         * @og.rev 6.9.1.0 (2018/02/26) 新規追加
622         *
623         * @param       table   マップ作成元のデータテーブル(副作用なし) 通常は、スレーブテーブル
624         * @param       keys    マップ作成の主キー(CSV形式)  通常は、マスターキー(スレーブとマッチングさせるためのキー)
625         * @param       valClm  値の配列リストに設定するカラム列(CSV形式)  キーカラムと値カラム
626         *
627         * @return マップ化された主キーと値の配列リスト
628         */
629        private Map<String,List<String[]>> makeValMap( final DBTableModel table, final String keys, final String valClm ) {
630                // カラム名をカラム番号に変換します。
631                final int[] clmNo = makeColumnNo( table,keys );                 // キーとなるカラムのCSV形式
632                final int   clmSize = clmNo.length;
633                if( clmSize == 0 ) {    // マスタキー未指定時には全カラムを使用します。
634                        return null;
635                }
636
637                final int[] valNo = makeColumnNo( table,valClm );               // 値のカラムのCSV形式          (PMD:6.9.9.4 (2018/10/01))
638                final Map<String,List<String[]>> tempMap = new LinkedHashMap<>();
639
640                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
641                for( int row=0; row<table.getRowCount(); row++ ) {
642                        buf.setLength(0);
643                        for( int i=0; i<clmSize; i++ ) {
644                                buf.append( table.getValue( row,clmNo[i] ) ).append( '|' );
645                        }
646                        // 値の取得
647                        final String[] vals = new String[valNo.length];
648                        for( int i=0; i<valNo.length; i++ ) {
649                                vals[i] = table.getValue( row,valNo[i] );
650                        }
651
652                        // 主キー連結文字列
653                        final String key = buf.toString().toUpperCase(Locale.JAPAN);            // 大文字・小文字を区別しない。
654                        tempMap.computeIfAbsent( key , k -> new ArrayList<String[]>() ).add( vals );
655                }
656
657                return tempMap;
658        }
659
660        /**
661         * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。
662         *
663         * @param       masterTbl       マスターテーブルモデル
664         * @param       slaveTbl        スレーブテーブルモデル
665         *
666         * @return      処理結果のテーブルモデル
667         */
668        private DBTableModel makeUnionAll( final DBTableModel masterTbl,final DBTableModel slaveTbl ) {
669                final DBTableModel table = masterTbl;
670
671                for( int row=0; row<slaveTbl.getRowCount(); row++ ) {
672                        final String[] vals = slaveTbl.getValues( row );
673                        table.addColumnValues( vals );
674                }
675
676                return table;
677        }
678
679        /**
680         * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。
681         * スレーブ表は、マスタ表のチェックされた行を起点として登録されます。
682         * チェックされていない場合は、スレーブ表は先頭から追加されます。
683         *
684         * @og.rev 4.3.2.0 (2008/09/11) 新規作成
685         *
686         * @param       masterTbl       マスターテーブルモデル
687         * @param       slaveTbl        スレーブテーブルモデル
688         *
689         * @return      処理結果のテーブルモデル
690         */
691        private DBTableModel makeUnionSelrow( final DBTableModel masterTbl,final DBTableModel slaveTbl ) {
692                final DBTableModel table = masterTbl;
693
694                final int insRowNo = getParameterRows().length > 0 ? getParameterRows()[0] + 1 : 0 ;
695                for( int row=0; row<slaveTbl.getRowCount(); row++ ) {
696                        final String[] vals = slaveTbl.getValues( row );
697                        table.addValues( vals, insRowNo + row, false );
698                }
699
700                return table;
701        }
702
703        /**
704         * 指定のマスタ,スレーブテーブルを使用して 和集合 処理を実行します。
705         *
706         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
707         *
708         * @param       masterTbl       マスターテーブルモデル
709         * @param       masterMap       マスターテーブルの主キーマップ
710         * @param       slaveTbl        スレーブテーブルモデル
711         * @param       slaveMap        スレーブテーブルの主キーマップ
712         *
713         * @return      処理結果のテーブルモデル
714         */
715        private DBTableModel makeUnion( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
716                                                                        final DBTableModel slaveTbl,final Map<String,Integer> slaveMap ) {
717                final DBTableModel table = masterTbl;
718
719                @SuppressWarnings("rawtypes")
720                final Map.Entry[] entry = slaveMap.entrySet().toArray( new Map.Entry[slaveMap.size()] );
721
722                final int size = entry.length;
723                for( int i=0; i<size; i++ ) {
724                        String key  = (String)entry[i].getKey();
725                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
726                        if( ! masterMap.containsKey( key ) ) {          // マスタにスレーブデータが存在しない
727                                final int row =  ((Integer)entry[i].getValue()).intValue();
728                                final String[] vals = slaveTbl.getValues( row );
729                                table.addColumnValues( vals );
730                        }
731                }
732                return table;
733        }
734
735        /**
736         * 指定のマスタ,スレーブテーブルを使用して 積集合 処理を実行します。
737         *
738         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
739         * @og.rev 6.2.5.1 (2015/06/12) Modify情報が書かれていると、DELETE状況が再セットされないため、削除されなかった。
740         *
741         * @param       masterTbl       マスターテーブルモデル
742         * @param       masterMap       マスターテーブルの主キーマップ
743         * @param       slaveMap        スレーブテーブルの主キーマップ
744         *
745         * @return      処理結果のテーブルモデル
746         */
747        private DBTableModel makeIntersect( final DBTableModel masterTbl,final Map<String,Integer> masterMap, final Map<String,Integer> slaveMap ) {
748                final DBTableModel table = masterTbl;
749
750                @SuppressWarnings("rawtypes")
751                final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
752
753                // 6.2.5.1 (2015/06/12) 削除する行番号を記憶しておく。
754                final List<Integer> rowList = new ArrayList<>();
755
756                // table.resetModify();         // 6.2.5.1 (2015/06/12) Modify情報をリセットしておく必要があった。
757
758                final int size = entry.length;
759                for( int i=0; i<size; i++ ) {
760                        String key  = (String)entry[i].getKey();
761                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
762                        if( ! slaveMap.containsKey( key ) ) {           // スレーブにマスタが存在しない
763                                rowList.add( (Integer)entry[i].getValue() );    // 6.2.5.1 (2015/06/12) rowをセット
764                        }
765                }
766
767                // 行番号が変わらないように逆順削除します。
768                // 6.2.5.1 (2015/06/12) rowを削除
769                for( int i=rowList.size()-1 ; i>=0; i-- ) {
770                        table.removeValue( rowList.get(i) );
771                }
772
773                return table;
774        }
775
776        /**
777         * 指定のマスタ,スレーブテーブルを使用して 差集合 処理を実行します。
778         *
779         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
780         * @og.rev 6.2.5.1 (2015/06/12) Modify情報が書かれていると、DELETE状況が再セットされないため、削除されなかった。
781         *
782         * @param       masterTbl       マスターテーブルモデル
783         * @param       masterMap       マスターテーブルの主キーマップ
784         * @param       slaveMap        スレーブテーブルの主キーマップ
785         *
786         * @return      処理結果のテーブルモデル
787         */
788        private DBTableModel makeMinus( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
789                                                                        final Map<String,Integer> slaveMap ) {
790                final DBTableModel table = masterTbl;
791
792                @SuppressWarnings("rawtypes")
793                final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
794
795                // 6.2.5.1 (2015/06/12) 削除する行番号を記憶しておく。
796                final List<Integer> rowList = new ArrayList<>();
797
798                // table.resetModify();         // 6.2.5.1 (2015/06/12) Modify情報をリセットしておく必要があった。
799
800                final int size = entry.length;
801                for( int i=0; i<size; i++ ) {
802                        String key  = (String)entry[i].getKey();
803                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
804                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタが存在する
805                                rowList.add( (Integer)entry[i].getValue() );    // 6.2.5.1 (2015/06/12) rowをセット
806                        }
807                }
808
809                // 行番号が変わらないように逆順削除します。
810                // 6.2.5.1 (2015/06/12) rowを削除
811                for( int i=rowList.size()-1 ; i>=0; i-- ) {
812                        table.removeValue( rowList.get(i) );
813                }
814
815                return table;
816        }
817
818        /**
819         * 指定のマスタ,スレーブテーブルを使用して 差分集合 処理を実行します。
820         *
821         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
822         *
823         * @param       masterTbl       マスターテーブルモデル
824         * @param       masterMap       マスターテーブルの主キーマップ
825         * @param       slaveTbl        スレーブテーブルモデル
826         * @param       slaveMap        スレーブテーブルの主キーマップ
827         *
828         * @return      処理結果のテーブルモデル
829         */
830        private DBTableModel makeDifference( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
831                                                                                 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) {
832                final DBTableModel table = masterTbl;
833
834                // テーブルの差分属性のカラム番号を求めます。(マスタ、スレーブ共通)
835                final int[] diffClmNo = makeColumnNo( table,diffKeys );
836                final int diffClmSize = diffClmNo.length;
837
838                // スレーブからマスタへ値の再セットを行うカラム番号を求めます。(マスタ、スレーブ共通)
839                final int[] modClmNo = makeColumnNo( table,modifyClms );
840                final int modClmSize = modClmNo.length;
841
842                final int clmSize = table.getColumnCount();
843                final DBTableModel newTable = DBTableModelUtil.newDBTable();
844                newTable.init( clmSize );
845
846                // テーブルの全カラムを新たに作成するテーブルにコピーします。
847                for( int i=0; i<clmSize; i++ ) {
848                        newTable.setDBColumn( i,table.getDBColumn( i ) );
849                }
850
851                // スレーブの先頭カラムが、WRITABLE かどうか
852                final boolean writeFlag = "WRITABLE".equalsIgnoreCase( slaveTbl.getColumnName(0) );
853
854                @SuppressWarnings("rawtypes")
855                final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
856
857                final int size = entry.length;
858                for( int i=0; i<size; i++ ) {
859                        String key  = (String)entry[i].getKey();
860                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
861                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタが存在する
862                                final int mrow =  ((Integer)entry[i].getValue()).intValue();
863                                final int srow =  slaveMap.get( key ).intValue();
864                                boolean unmatched = false;
865                                for( int j=0; j<diffClmSize; j++ ) {
866                                        final String mval = table.getValue( mrow,diffClmNo[j] );
867                                        final String sval = slaveTbl.getValue( srow,diffClmNo[j] );
868                                        if( mval != null && !mval.equalsIgnoreCase( sval ) ) { unmatched = true; break; }               // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
869                                }
870
871                                if( unmatched ) {               // 属性情報が異なる場合のみ処理
872                                        // データのコピー
873                                        String[] mvals = new String[clmSize];
874                                        System.arraycopy( table.getValues( mrow ),0,mvals,0 ,clmSize );
875
876                                        // スレーブの値をマスタの値にセットする。
877                                        for( int j=0; j<modClmSize; j++ ) {
878                                                final String val = slaveTbl.getValue( srow,modClmNo[j] );
879                                                mvals[modClmNo[j]] = val;
880                                        }
881                                        newTable.addColumnValues( mvals );
882
883                                        if( useDiffData ) {
884                                                // データのコピー
885                                                String[] svals = new String[clmSize];
886                                                System.arraycopy( slaveTbl.getValues( srow ),0,svals,0 ,clmSize );
887                                                if( writeFlag ) { svals[0] = "0"; }     // 書き込み不許可
888                                                for( int j=0; j<diffClmSize; j++ ) {
889                                                        final int dclmNo = diffClmNo[j];
890                                                        final String mval = mvals[dclmNo];
891                                                        final String sval = svals[dclmNo];
892                                                        if( mval != null && !mval.equalsIgnoreCase( sval ) ) {          // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
893                                                                svals[dclmNo] = "<span class=\"unmatched\">" + sval + "</span>" ;
894                                                        }
895                                                }
896                                                newTable.addColumnValues( svals );
897                                        }
898                                }
899                        }
900                }
901                return newTable;
902        }
903
904        /**
905         * 指定のマスタ,スレーブテーブルを使用して 値列合成 処理を実行します。
906         *
907         *   UNION_VAL は、スレーブテーブルの縦列を横列に割り当てるUNION_CLMの特殊版です。
908         *   処理としては、そのまま割り当てるのではなく、スレーブテーブルの縦列の値と
909         *   ヘッダーとして指定する、unionClms の値を比較して、下限値の最大値を割り当てます。
910         *   つまり、ヘッダーの値と同じか、それ以下で、最も近い値を割り当てることになります。
911         *   これは、時間的な推移を表現することになります。
912         *     masterKeys:マスタとスレーブを関連付ける共通のキーとなるカラム
913         *     slaveKeys :スレーブの縦列を横列にするための、キーと値のカラム
914         *     unionClms :横列にするカラム名。この値とスレーブのキーと比較してセットするかどうかを決めます。
915         *
916         * @og.rev 6.9.1.0 (2018/02/26) 新規追加
917         *
918         * @param       masterTbl       マスターテーブルモデル
919         * @param       mstrKeys        マスターキー
920         * @param       slaveTbl        スレーブテーブルモデル
921         * @param       slaveKeys       スレーブキー
922         *
923         * @return      処理結果のテーブルモデル
924         */
925        private DBTableModel makeUnionVal( final DBTableModel masterTbl , final String mstrKeys , final DBTableModel slaveTbl , final String slaveKeys ) {
926
927                final Map<String,List<String[]>> slvMap = makeValMap( slaveTbl,mstrKeys,slaveKeys );
928
929                final DBTableModel table = makeAddClm( masterTbl );
930
931                // カラム名をカラム番号に変換します。
932                final int[] keyClmNo = makeColumnNo( table,mstrKeys );
933
934                final int[] mClmNo = makeColumnNo( table   ,unionClms );
935//              final int   addSize = mClmNo.length;            // unionClms の個数なので、マスタ、スレーブは同一
936
937                final String[] valClms = StringUtil.csv2Array( unionClms );             //
938
939                // 突合せを行い、マッチするカラムについて、値をセットし直します。
940                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
941                for( int row=0; row<table.getRowCount(); row++ ) {
942                        final String[] mvals = table.getValues( row );
943                        buf.setLength(0);
944                        for( int i=0; i<keyClmNo.length; i++ ) {
945                                buf.append( mvals[keyClmNo[i]] ).append( '|' );
946                        }
947                        // 主キー連結文字列
948                        final String key = buf.toString().toUpperCase(Locale.JAPAN);            // 大文字・小文字を区別しない。
949                        final List<String[]> slvVals = slvMap.get( key );
950                        if( slvVals != null ) {
951                                for( int j=0; j<valClms.length; j++ ) {                 // テーブルのヘッダー列
952                                        for( final String[] svals : slvVals ) {         // 値の配列 [0]=比較キー [1]=設定値
953                                                if( svals[0].compareTo( valClms[j] ) <= 0 ) {
954                                                        table.setValueAt( svals[1] , row , mClmNo[j] );
955                                                }
956                                                else {
957                                                        break;          // 値が超えれば、終了
958                                                }
959                                        }
960                                }
961                        }
962                }
963                table.resetModify();
964
965                return table;
966        }
967
968        /**
969         * 指定のマスタ,スレーブテーブルを使用して 列合成(UNION_CLM) 処理を実行します。
970         *
971         * すでに、マスタ にカラムが存在している場合は、値の書き換えを、
972         * カラムが存在しなければ、カラムを追加します。
973         * マスタとスレーブのデータ突合せ時のキーは、slaveKeys になります。
974         * これは、masterMap を使用しないことを意味します。(masterKeys 指定によるGROUP化は
975         * 行います。masterKeys を指定しない場合は、サマライズなしで処理します。)
976         * 具体的には、マスタテーブルの複数の行に対して、スレーブデータの値が設定
977         * される可能性があることを意味します。
978         *
979         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
980         *
981         * @param       masterTbl       マスターテーブルモデル
982         * @param       slaveKeys       スレーブキー
983         * @param       slaveTbl        スレーブテーブルモデル
984         * @param       slaveMap        スレーブテーブルの主キーマップ
985         *
986         * @return      処理結果のテーブルモデル
987         */
988        private DBTableModel makeUnionClm( final DBTableModel masterTbl ,final String slaveKeys ,
989                                                                                final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) {
990
991                final DBTableModel table = makeAddClm( masterTbl );
992
993                // カラム名をカラム番号に変換します。
994                final int[] keyClmNo = makeColumnNo( table,slaveKeys );
995
996                final int[] mClmNo = makeColumnNo( table   ,unionClms );
997                final int[] sClmNo = makeColumnNo( slaveTbl,unionClms );
998                final int   addSize = mClmNo.length;            // unionClms の個数なので、マスタ、スレーブは同一
999
1000                // 突合せを行い、マッチするカラムについて、値をセットし直します。
1001                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
1002                for( int row=0; row<table.getRowCount(); row++ ) {
1003                        final String[] mvals = table.getValues( row );
1004                        buf.setLength(0);                                                                               // 6.1.0.0 (2014/12/26) refactoring
1005                        for( int i=0; i<keyClmNo.length; i++ ) {
1006                                buf.append( mvals[keyClmNo[i]] ).append( '|' );         // 6.0.2.5 (2014/10/31) char を append する。
1007                        }
1008                        // 主キー連結文字列
1009                        final String key = buf.toString().toUpperCase(Locale.JAPAN);            // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
1010                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタに対応するデータが存在する
1011                                final int slvRow = slaveMap.get( key ).intValue();
1012                                final String[] svals = slaveTbl.getValues( slvRow );            // スレーブの行番号
1013                                for( int j=0; j<addSize; j++ ) {
1014                                        mvals[mClmNo[j]] = svals[sClmNo[j]];
1015                                }
1016                                table.setValues( mvals,row );
1017                        }
1018                }
1019                table.resetModify();
1020
1021                return table;
1022        }
1023
1024        /**
1025         * 指定のマスタテーブルに 列追加 処理を実行します。
1026         *
1027         * すでに、マスタ にカラムが存在している場合は、値の書き換えを、
1028         * カラムが存在しなければ、カラムを追加します。
1029         *
1030         * @og.rev 6.9.1.0 (2018/02/26) unionLbls追加
1031         *
1032         * @param       masterTbl       マスターテーブルモデル
1033         *
1034         * @return      処理結果のテーブルモデル
1035         */
1036        private DBTableModel makeAddClm( final DBTableModel masterTbl ) {
1037                final String[]  addClms  = StringUtil.csv2Array( unionClms );
1038                final int       addClmSize = addClms.length;
1039                boolean[] useAdd         = new boolean[addClmSize];
1040                int addSize = 0;
1041                for( int i=0; i<addClmSize; i++ ) {
1042                        if( masterTbl.getColumnNo( addClms[i],false ) < 0 ) {   // 存在しなければ -1
1043                                useAdd[i] = true;
1044                                addSize++ ;
1045                        }
1046                        else {
1047                                useAdd[i] = false;              // マスタに存在すれば、追加不要
1048                        }
1049                }
1050
1051                final int mstrClmSize = masterTbl.getColumnCount();
1052                final int clmSize = mstrClmSize + addSize;
1053
1054                final DBTableModel table = DBTableModelUtil.newDBTable();
1055                table.init( clmSize );
1056                int clmNo = 0;
1057                // マスタテーブルの全カラムを新たに作成するテーブルにコピーします。
1058                for( int i=0; i<mstrClmSize; i++ ) {
1059                        table.setDBColumn( clmNo++,masterTbl.getDBColumn( i ) );
1060                }
1061
1062                // 6.9.1.0 (2018/02/26) unionLbls追加
1063                final String[]  addLbls  = StringUtil.csv2Array( unionLbls , ',' , addClmSize );        // 無くてもaddClmSize 個の配列を作る。
1064
1065                // 追加するカラムを新たに作成するテーブルに追加します。
1066                String[] addClmVal = new String[addSize];       // 追加分のみ
1067                int addCnt = 0;
1068                for( int i=0; i<addClmSize; i++ ) {
1069                        if( useAdd[i] ) {
1070//                              final DBColumn column = getDBColumn( addClms[i] );
1071                                final DBColumn column = getResource().makeDBColumn( addClms[i] , addLbls[i] );  // 6.9.1.0 (2018/02/26)
1072                                addClmVal[addCnt++] = nval( column.getDefault(),"" );
1073                                table.setDBColumn( clmNo++,column );
1074                        }
1075                }
1076                // 一旦、すべてのマスタデータを新テーブルにコピーします。
1077                for( int row=0; row<masterTbl.getRowCount(); row++ ) {
1078                        final String[] vals = new String[clmSize];
1079                        System.arraycopy( masterTbl.getValues( row ),0,vals,0 ,mstrClmSize );   // マスタデータのコピー
1080                        System.arraycopy( addClmVal,0,vals,mstrClmSize ,addSize );                              // 追加列情報の初期値
1081                        table.addColumnValues( vals );
1082                }
1083
1084                return table;
1085        }
1086
1087        /**
1088         * 指定のマスタテーブルを使用して グループ 処理を実行します。
1089         *
1090         * @param       masterTbl       マスターテーブルモデル
1091         *
1092         * @return      処理結果のテーブルモデル
1093         */
1094        private DBTableModel makeGroup( final DBTableModel masterTbl ) {
1095                return masterTbl ;
1096        }
1097
1098        /**
1099         * 指定のマスタ,スレーブテーブルを使用して、改廃コード(A,C)を設定します。
1100         * スレーブにデータがあれば、"C" を、なければ、"A" をセットします。
1101         * "D" は、設定できません。
1102         *
1103         * @og.rev 6.3.1.1 (2015/07/10) 改廃コードを設定する CDKH アクションを追加
1104         *
1105         * @param       masterTbl       マスターテーブルモデル
1106         * @param       masterMap       マスターテーブルの主キーマップ
1107         * @param       slaveTbl        スレーブテーブルモデル
1108         * @param       slaveMap        スレーブテーブルの主キーマップ
1109         *
1110         * @return      処理結果のテーブルモデル
1111         */
1112        private DBTableModel makeCdkh( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
1113                                                                                 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) {
1114                final DBTableModel table = masterTbl;
1115
1116                // スレーブからマスタへ値の再セットを行うカラム番号を求めます。(マスタ、スレーブ共通)
1117                final int[] modClmNo = makeColumnNo( table,modifyClms );
1118                final int modClmSize = modClmNo.length;
1119
1120                @SuppressWarnings("rawtypes")
1121                final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
1122
1123                table.resetModify();            // リセットしておきます。
1124
1125                final int size = entry.length;
1126                for( int i=0; i<size; i++ ) {
1127                        String key  = (String)entry[i].getKey();
1128                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
1129                        if( slaveMap.containsKey( key ) ) {                             // スレーブにマスタが存在する
1130                                final int mrow =  ((Integer)entry[i].getValue()).intValue();
1131                                table.setModifyType( mrow , "C" );                      // 一致すれば、C:変更タイプ
1132
1133                                // スレーブの値をマスタの値にセットする。
1134                                final int srow = slaveMap.get( key ).intValue();
1135                                for( int j=0; j<modClmSize; j++ ) {
1136                                        final String val = slaveTbl.getValue( srow,modClmNo[j] );       // スレーブから取り出して
1137                                        table.setValueAt( val,mrow,modClmNo[j] );                                       // マスタにセットする。
1138                                }
1139                        }
1140                }
1141
1142                for( int row=0; row<table.getRowCount(); row++ ) {
1143                        if( table.getModifyType( row ).isEmpty() ) {
1144                                table.setModifyType( row , "A" );                       // 残りを、A:追加タイプ
1145                        }
1146                }
1147
1148                return table;
1149        }
1150
1151        /**
1152         * 指定の値がすべてnull(空文字列)の場合、レコードを削除します。
1153         *
1154         * @og.rev 6.7.3.0 (2017/01/27) 指定の値がすべてnull(空文字列)の場合、レコードを削除します。
1155         *
1156         * @param       table   マップ作成元のデータテーブル(副作用あり)
1157         * @param       delClms null(空文字列)判定を行うカラム列(CSV形式)
1158         *
1159         * @return レコードが削除されたテーブル
1160         */
1161        private DBTableModel deleteRows( final DBTableModel table, final String delClms ) {
1162
1163                // カラム名をカラム番号に変換します。
1164                final int[] clmNo = makeColumnNo( table,delClms );
1165                final int   clmSize = clmNo.length;
1166                if( clmSize == 0 ) {    // マスタキー未指定時には全カラムを使用します。
1167                        return table;
1168                }
1169
1170                // レコードの削除なので逆順に処理します。
1171                for( int row=table.getRowCount()-1; row>=0; row-- ) {
1172                        boolean isNull = true;
1173                        for( int i=0; i<clmSize; i++ ) {
1174                                if( !StringUtil.isNull( table.getValue( row,clmNo[i] ) ) ) {
1175                                        isNull = false;
1176                                        break;
1177                                }
1178                        }
1179
1180                        if( isNull ) { table.removeValue( row ); }
1181                }
1182
1183                return table;
1184        }
1185
1186        /**
1187         * 指定のテーブルと同じテーブルを別オブジェクトとして作成します。
1188         *
1189         * @param       tbl     コピー元テーブルモデル
1190         *
1191         * @return      コピーされた新テーブルモデル
1192         */
1193        private DBTableModel cloneTable( final DBTableModel tbl ) {
1194                final int clmSize = tbl.getColumnCount();
1195
1196                final DBTableModel table = DBTableModelUtil.newDBTable();
1197                table.init( clmSize );
1198
1199                // テーブルの全カラムを新たに作成するテーブルにコピーします。
1200                for( int i=0; i<clmSize; i++ ) {
1201                        table.setDBColumn( i,tbl.getDBColumn( i ) );
1202                }
1203                // すべてのデータを新テーブルにコピーします。
1204                for( int row=0; row<tbl.getRowCount(); row++ ) {
1205                        final String[] vals = new String[clmSize];
1206                        System.arraycopy( tbl.getValues( row ),0,vals,0 ,clmSize );     // データのコピー
1207                        table.addColumnValues( vals );
1208                }
1209                return table;
1210        }
1211
1212        /**
1213         * 指定のテーブルより、カラム列名に対応するカラム番号配列を作成します。
1214         *
1215         * カラム名のCSVデータが 指定されない場合(clmcsv == null)は、長さ0の配列を返します。
1216         *
1217         * @param       table   テーブルモデル
1218         * @param       clmcsv  カラム名のCSVデータ
1219         *
1220         * @return      カラム名に対応する、列番号配列(なければ、長さ0配列)
1221         */
1222        private int[] makeColumnNo( final DBTableModel table,final String clmcsv ) {
1223
1224                // マスタテーブルの差分属性のカラム番号を求めます。
1225                final String[] clms = StringUtil.csv2Array( clmcsv );
1226                int[] clmNo = new int[clms.length];
1227
1228                // カラム名をカラム番号に変換します。
1229                for( int i=0; i<clms.length; i++ ) {
1230                        clmNo[i] = table.getColumnNo( clms[i] );
1231                }
1232
1233                return clmNo;
1234        }
1235
1236        /**
1237         * 【TAG】コマンド (NEW,RENEW)をセットします(初期値:NEW)。
1238         *
1239         * @og.tag
1240         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
1241         * フィールド定数値のいづれかを、指定できます。
1242         * 初期値は NEW です。
1243         *
1244         * @param       cmd     コマンド (public static final 宣言されている文字列)
1245         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.TableMergeTag.CMD_NEW">コマンド定数</a>
1246         */
1247        public void setCommand( final String cmd ) {
1248                final String cmd2 = getRequestParameter( cmd );
1249                if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
1250        }
1251
1252        /**
1253         * 【TAG】アクションを指定します[UNION_ALL/UNION/INTERSECT/MINUS/DIFFERENCE/UNION_CLM/UNION_VAL/ADD_CLM/GROUP/UNION_SELROW/CDKH/TABLE_REMOVE]。
1254         *
1255         * @og.tag
1256         * 指定できるアクションは、全体集合(UNION_ALL)、全体集合(挿入位置指定)(UNION_SELROW)、和集合(UNION)
1257         * 、積集合(INTERSECT)、差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、値列合成(UNION_VAL)、列追加(ADD_CLM)、 グループ(GROUP)です。
1258         * 列合成とグループ以外の処理では、カラム順とカラム数は同数でなければなりません。
1259         * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。
1260         * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。
1261         * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。)
1262         * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の
1263         * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、
1264         * 2回目に現れた情報を削除することになります。グループ が指定された場合は、
1265         * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで
1266         * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで
1267         * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。
1268         * (マージ後には、同一主キーを持つ行は存在します。)
1269         * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、
1270         * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。
1271         *
1272         * <table class="plain">
1273         *   <caption>アクションの説明</caption>
1274         *   <tr><th>action      </th><th>名称    </th><th>処理概要                                                                                                   </th><th>1</th><th>2</th><th>3</th><th>4</th><tr>
1275         *   <tr><td>UNION_ALL   </td><td>全体集合</td><td>マスタとスレーブを合成                                                                      </td><td>○</td><td>○</td><td> </td><td> </td><tr>
1276         *   <tr><td>UNION       </td><td>和集合  </td><td>マスタとスレーブのユニーク部のみ合成                                              </td><td>○</td><td>○</td><td> </td><td> </td><tr>
1277         *   <tr><td>INTERSECT   </td><td>積集合  </td><td>マスタとスレーブのユニーク部が一致するマスタのみ選択              </td><td>○</td><td>○</td><td> </td><td> </td><tr>
1278         *   <tr><td>MINUS       </td><td>差集合  </td><td>マスタからスレーブに存在するユニーク部を削除した残り              </td><td>○</td><td>○</td><td> </td><td> </td><tr>
1279         *   <tr><td>DIFFERENCE  </td><td>差分集合</td><td>ユニーク部が一致し、差分カラムが異なるマスタのみ選択               </td><td>○</td><td>○</td><td> </td><td>○</td><tr>
1280         *   <tr><td>UNION_CLM   </td><td>列合成  </td><td>マスタとキー一致するスレーブのカラム情報を追加                 </td><td>○</td><td>○</td><td>○</td><td> </td><tr>
1281         *   <tr><td>UNION_VAL   </td><td>値列合成</td><td>マスタとキー一致するスレーブの縦列をカラムに追加                 </td><td>○</td><td>○</td><td>○</td><td> </td><tr>
1282         *   <tr><td>ADD_CLM     </td><td>列追加  </td><td>UNION_CLMとの違いは、カラムのみ追加することです。           </td><td> </td><td> </td><td>○</td><td> </td><tr>
1283         *   <tr><td>GROUP       </td><td>グループ</td><td>マスタのユニーク部化                                                                               </td><td> </td><td> </td><td> </td><td> </td><tr>
1284         *   <tr><td>UNION_SELROW</td><td>全体集合</td><td>マスタとスレーブを合成(マスタ表のチェック行を起点に追加     </td><td>○</td><td> </td><td> </td><td> </td><tr>
1285         *   <tr><td>CDKH        </td><td>改廃CD  </td><td>改廃コードを設定                                                                                       </td><td>○</td><td>○</td><td> </td><td>○</td><tr>
1286         *   <tr><td>TABLE_REMOVE</td><td>TBL削除 </td><td>マスタテーブルオブジェクトを無条件で、削除します                       </td><td> </td><td> </td><td> </td><td> </td><tr>
1287         * </table>
1288         *
1289         * ※:マスタテーブルオブジェクトは、常に必須
1290         * 1:スレーブテーブルオブジェクト必須
1291         * 2:masterKeys 属性必須
1292         * 3:unionClms 属性必須(スレーブテーブルのカラム名または追加するカラム名)
1293         * 4:diffKeys 属性必須(DIFFERENCE時の差分カラム名)、modifyClms 属性使用可能
1294         *
1295         * @param       action アクション [UNION_ALL/UNION/INTERSECT/MINUS/DIFFERENCE/UNION_CLM/ADD_CLM/GROUP/UNION_SELROW/CDKH]
1296         */
1297        public void setAction( final String action ) {
1298                this.action = nval( getRequestParameter( action ),this.action );
1299        }
1300
1301        /**
1302         * 【TAG】出力先のtableIdを指定します
1303         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1304         *
1305         * @og.tag
1306         * 集合処理結果の DBTableModel をメモリにセットする場合のキー(tableId)を指定します。
1307         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1308         *
1309         * @param       id テーブルID (sessionに登録する時のID)
1310         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
1311         */
1312        public void setTableId( final String id ) {
1313                tableId = nval( getRequestParameter( id ),tableId );
1314        }
1315
1316        /**
1317         * 【TAG】出力先のscopeを指定します(初期値:session)。
1318         *
1319         * @og.tag
1320         * 集合処理結果の DBTableModel をメモリにセットする場合のスコープを指定します。
1321         * ここでは、マスタやスレーブのスコープ設定が必要な為、superクラスのメソッドを
1322         * オーバーライドしてこのオブジェクト内でキープしています。
1323         * 初期値は、session です。
1324         *
1325         * @param       scope   出力先のscope
1326         */
1327        @Override
1328        public void setScope( final String scope ) {
1329                this.scope = nval( getRequestParameter( scope ),this.scope );
1330        }
1331
1332        /**
1333         * 【TAG】マスタテーブルのtableIdを指定します
1334         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1335         *
1336         * @og.tag
1337         * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。
1338         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1339         *
1340         * @param       masterTableId   マスタテーブルID
1341         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
1342         */
1343        public void setMasterTableId( final String masterTableId ) {
1344                this.masterTableId = nval( getRequestParameter( masterTableId ),this.masterTableId );
1345        }
1346
1347        /**
1348         * 【TAG】マスタテーブルのscopeを指定します(初期値:session)。
1349         *
1350         * @og.tag
1351         * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のスコープを指定します。
1352         * 初期値は、session です。
1353         *
1354         * @param       masterScope     マスタスコープ
1355         */
1356        public void setMasterScope( final String masterScope ) {
1357                this.masterScope = nval( getRequestParameter( masterScope ),this.masterScope );
1358        }
1359
1360        /**
1361         * 【TAG】マスタテーブルの集合処理を行う主キーを指定します。
1362         *
1363         * @og.tag
1364         * 集合処理を行う場合の、カラム名を、CSV形式(CSV形式)で指定します。
1365         * このキーの組み合わせを元に、集合処理の突合せを行います。
1366         * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と
1367         * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。
1368         * アクションが全体集合(UNION_ALL)以外の場合は、必須属性になります。
1369         *
1370         * @param       masterKeys      マスタテーブル主キー (CSV形式)
1371         */
1372        public void setMasterKeys( final String masterKeys ) {
1373                this.masterKeys = nval( getRequestParameter( masterKeys ),this.masterKeys );
1374        }
1375
1376        /**
1377         * 【TAG】スレーブテーブルの集合処理を行う主キーを指定します。
1378         *
1379         * @og.tag
1380         * 集合処理を行う場合の、カラム名を、CSV形式(CSV形式)で指定します。
1381         * このキーの組み合わせを元に、集合処理の突合せを行います。
1382         * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と
1383         * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。
1384         * null の場合は、masterKeys と同じとします。
1385         *
1386         * @param       slaveKeys       スレーブ主キー (CSV形式)
1387         */
1388        public void setSlaveKeys( final String slaveKeys ) {
1389                this.slaveKeys = nval( getRequestParameter( slaveKeys ),this.slaveKeys );
1390        }
1391
1392        /**
1393         * 【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します。
1394         *
1395         * @og.tag
1396         * アクションが差分処理(DIFFERENCE)の場合に、差分チェックを行うカラム名を、
1397         * CSV形式(CSV形式)で指定します。
1398         * 差分処理とは、masterKeys で指定されたキーでは、マスタ、スレーブともに存在し
1399         * かつ、このキー(diffKeys)で指定されたキーの値が異なるマスタレコードを
1400         * 抜き出します。
1401         * つまり、主キーは存在し、属性が異なる情報のピックアップになりますので、
1402         * データ更新(UPDATE)対象を見つける場合に使用できます。
1403         * アクションが差分処理(DIFFERENCE)の場合は、必須属性になります。
1404         *
1405         * @param       diffKeys        差分カラム名 (CSV形式)
1406         * @see         #setMasterKeys( String )
1407         */
1408        public void setDiffKeys( final String diffKeys ) {
1409                this.diffKeys = nval( getRequestParameter( diffKeys ),this.diffKeys );
1410        }
1411
1412        /**
1413         * 【TAG】スレーブテーブルのtableIdを指定します
1414         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1415         *
1416         * @og.tag
1417         * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。
1418         * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。
1419         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1420         *
1421         * @param       slaveTableId    スレーブテーブルID
1422         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
1423         */
1424        public void setSlaveTableId( final String slaveTableId ) {
1425                this.slaveTableId = nval( getRequestParameter( slaveTableId ),this.slaveTableId );
1426        }
1427
1428        /**
1429         * 【TAG】スレーブテーブルのscopeを指定します(初期値:session)。
1430         *
1431         * @og.tag
1432         * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のスコープを指定します。
1433         * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。
1434         * 初期値は、session です。
1435         *
1436         * @param       slaveScope      スレーブスコープ
1437         */
1438        public void setSlaveScope( final String slaveScope ) {
1439                this.slaveScope = nval( getRequestParameter( slaveScope ),this.slaveScope );
1440        }
1441
1442        /**
1443         * 【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します。
1444         *
1445         * @og.tag
1446         * アクションが列合成(UNION_CLM)、値列合成(UNION_VAL)または列追加(ADD_CLM)の場合に使用されます。
1447         * 列合成(UNION_CLM)は、マスタとスレーブの主キーに対して、ここで指定のスレーブの
1448         * カラム列名を、マスタの列に追加します。主キーがマッチしない行に関しては、
1449         * カラムの初期値が適用されたデータを作成します。
1450         * 列追加(ADD_CLM)は、マスタテーブルに指定のカラムを追加するだけです、スレーブテーブルは
1451         * 参照しません。よって、主キーも指定不要です。
1452         *
1453         * @param       unionClms       列合成カラム名 (CSV形式)
1454         */
1455        public void setUnionClms( final String unionClms ) {
1456                this.unionClms = nval( getRequestParameter( unionClms ),this.unionClms );
1457        }
1458
1459        /**
1460         * 【TAG】スレーブからマスタへ追加するラベルをCSV形式で指定します。
1461         *
1462         * @og.tag
1463         * アクションが列合成(UNION_CLM)、値列合成(UNION_VAL)または列追加(ADD_CLM)の場合に、
1464         * unionClmsと共に使用されます。
1465         *
1466         * 特に、値列合成(UNION_VAL)時のカラムは、縦列の値からカラムを作成する関係で、
1467         * 通常カラムリソースに存在しません。
1468         * そこで、キーをそのままヘッダーに表示する場合に、不都合が発生する場合があるため、
1469         * ラベルを置き換える機能を用意します。
1470         *
1471         * @og.rev 6.9.1.0 (2018/02/26) unionLbls属性追加
1472         *
1473         * @param       unionLbls       列合成カラム名 (CSV形式)
1474         */
1475        public void setUnionLbls( final String unionLbls ) {
1476                this.unionLbls = nval( getRequestParameter( unionLbls ),this.unionLbls );
1477        }
1478
1479        /**
1480         * 【TAG】指定の値がすべてnull(空文字列)の場合、レコードを削除するカラムをCSV形式で指定します。
1481         *
1482         * @og.tag
1483         * アクションが(GROUP)で、指定のカラム列の値が、すべてnull(空文字列)の場合は、
1484         * そのレコード自身を削除します。
1485         *
1486         * @og.rev 6.7.3.0 (2017/01/27) nullDelClms属性追加
1487         *
1488         * @param       nullDelClms     null(空文字列)の場合、レコードを削除するカラムをCSV形式で指定
1489         */
1490        public void setNullDelClms( final String nullDelClms ) {
1491                this.nullDelClms = nval( getRequestParameter( nullDelClms ),this.nullDelClms );
1492        }
1493
1494        /**
1495         * 【TAG】スレーブからマスタへ値を更新するカラム名をCSV形式で指定します。
1496         *
1497         * @og.tag
1498         * アクションが差分処理(DIFFERENCE)、改廃CD(CDKH)の場合に、結果にマスタテーブルが抜き出されますが、
1499         * 更新する場合に、スレーブ特有のユニークキー(例:UNIQ)を用いて更新する場合、
1500         * 指定のカラム値は、スレーブの値にセットしておきたい場合があります。
1501         * ここでは、指定のカラムについて、値だけスレーブからマスタへセットします。
1502         * なお、値の更新については、マスタとスレーブが同一キーという制約があります。
1503         *
1504         * @param       modifyClms      値更新カラム名 (CSV形式)
1505         */
1506        public void setModifyClms( final String modifyClms ) {
1507                this.modifyClms = nval( getRequestParameter( modifyClms ),this.modifyClms );
1508        }
1509
1510        /**
1511         * 【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します。
1512         *
1513         * @og.tag
1514         * masterKeysで集合処理するときに、通常、最初に見つかった行データのみ残りますが、
1515         * ここに指定したカラムについては、発生都度、自分自身の情報に、CSV形式で連結して
1516         * いきます。
1517         * この操作により、本来削除された情報が、1行のCSV形式で取得できる効果が得られます。
1518         * これは、value タグの action="APPEND" を、DBTableModel に対して実施するような感じです。
1519         *
1520         * @og.rev 5.1.4.0 (2010/03/01) 新規追加
1521         *
1522         * @param       groupAddClms    相違データCSV連結 (CSV形式)
1523         */
1524        public void setGroupAddClms( final String groupAddClms ) {
1525                this.groupAddClms = nval( getRequestParameter( groupAddClms ),this.groupAddClms );
1526        }
1527
1528        /**
1529         * 【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある)。
1530         *
1531         * @og.tag
1532         * すべての処理で、DBTableModel に対して、ユニーク化やグループ化などの集合処理を
1533         * 行う過程で、マスタテーブルに対して直接処理を行うと、副作用が発生します。
1534         * 同様に、スレーブテーブルにおいても、一旦キー列でグループ化されるため、副作用が
1535         * 発生します。これは、無駄なメモリ領域の確保と、テーブル(マスタ、スレーブとも)の
1536         * コピー処理時間の節約になります。初期値の設定も副作用がある状態になっています。
1537         * ところが、都合によっては、色々な action を連続して行いたい場合など、毎回、
1538         * データベースを検索するよりもメモリ上でコピーしたほうが都合がよいケースでは、
1539         * 副作用が出ないように、noSideEffect="true" に設定します。
1540         * ただし、マスタ、スレーブともテーブルをコピーを行い、結果のテーブルも派生する為、
1541         * 通常、2つの領域(マスタと結果は同じテーブルに書かれる)で良い所を、5つの領域が
1542         * 作成されます。
1543         * 初期値は、副作用がある(noSideEffect="false")です。
1544         *
1545         * @param       noSideEffect    処理結果副作用 [true:ない/false:ある]
1546         */
1547        public void setNoSideEffect( final String noSideEffect ) {
1548                this.noSideEffect = nval( getRequestParameter( noSideEffect ),this.noSideEffect );
1549        }
1550
1551        /**
1552         * 【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true)。
1553         *
1554         * @og.tag
1555         * アクションが差分処理(DIFFERENCE)の場合に、結果にマスタテーブルが抜き出されますが、
1556         * 差分対象のスレーブデータと比較したい場合があります。
1557         * このフラグを true にセットすると、書込み禁止属性が付いた状態で、スレーブデータが
1558         * 結果テーブルに追加されます。
1559         * なお、この処理では、通常と異なり、マスタテーブルにはグループ化の副作用は発生しますが、
1560         * 結果テーブルは新規に作成され、先頭行に必ず WRITABLE カラムが付加されます。
1561         * 初期値は、true:追加する です。
1562         *
1563         * @param       flag    差分追加 [true:追加する/false:追加しない]
1564         */
1565        public void setUseDiffData( final String flag ) {
1566                useDiffData = nval( getRequestParameter( flag ),useDiffData );
1567        }
1568
1569        /**
1570         * 【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうか[true:する/false:しない]を指定します(初期値:false)。
1571         *
1572         * @og.tag
1573         * 処理対象のマスタテーブルについて、選択行が指定された場合に、選択行のみを処理対象に
1574         * するか、全件を対象にするかを指定します。
1575         * 積集合や差分集合など通常は、全件を対象にすることになりますが、列合成や列追加など、
1576         * マスタテーブルに対してのみ作用を及ぼす処理では、選択行のみを対象に処理を行う事が
1577         * 考えられます。その場合、初期グループ化と同じで、対象とする行が選択行のみになります。
1578         * 初期値は、false:全件対象 です。
1579         *
1580         * @param       flag    選択行のみ対象 [true:選択行のみ/false:全件]
1581         */
1582        public void setUseCheckOnly( final String flag ) {
1583                useCheckOnly = nval( getRequestParameter( flag ),useCheckOnly );
1584        }
1585
1586        /**
1587         * 【TAG】マージの結果を表示するかどうか[true:する/false:しない]を指定します(初期値:true)。
1588         *
1589         * @og.tag
1590         * true で、マージ結果を表示します。 false では、何も表示しません(初期値:true)
1591         * マスタテーブルの件数は、通常、キーでグループ化されるため、入力件数と異なります。
1592         * 同様に、スレーブ件数も異なります。結果の件数は、処理結果が現実的かどうかの
1593         * 判断に使用されます。
1594         * 初期値は、true:表示する です。
1595         *
1596         * @param       flag    結果表示 [true:する/false:しない]
1597         */
1598        public void setDisplay( final String flag ) {
1599                display = nval( getRequestParameter( flag ),display );
1600        }
1601
1602        /**
1603         * 【TAG】検索結果が0件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。
1604         *
1605         * @og.tag
1606         * 初期値は、false(続行する)です。
1607         *
1608         * @og.rev 5.7.6.2 (2014/05/16) 新規追加
1609         *
1610         * @param  flag 0件時停止 [true:処理を中止する/false:続行する]
1611         */
1612        public void setStopZero( final String flag ) {
1613                stopZero = nval( getRequestParameter( flag ),stopZero );
1614        }
1615
1616        /**
1617         * 【TAG】scope="request" のスレーブテーブルをループして使用するかどうか[true/false]を指定します(初期値:false[使用しない])。
1618         *
1619         * action="UNION_CLM" では、カラムを動的に追加していきます。これを、繰り返すことで、特定の処理結果を、
1620         * カラム列にコピーしていく処理が可能になります。(クロステーブル的な処理です。)
1621         * 内容的には、scope="request" の同一の DBTableModel を、初回に、scope="session" にコピーすることで、
1622         * 以後、マスターテーブルとして処理を続けるというものです。
1623         * 初回かどうかを判別するのは、scope="session" の DBTableModel が存在しない場合は、scope="request" のDBTableModel を
1624         * scope="session" に移すだけの処理を行います。
1625         * 以降は、scope="session" に、DBTableModel が存在するはずなので、これを、マスターテーブルとして使用します。
1626         * scope="session" の DBTableModel を消すのは、action="TABLE_REMOVE" です。
1627         *
1628         * @og.tag
1629         * 初期値は、false(使用しない)です。
1630         *
1631         * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop属性追加
1632         *
1633         * @param  flag スレーブテーブルをループして使用するかどうか [true:使用する/false:使用しない]
1634         */
1635        public void setUseSlaveLoop( final String flag ) {
1636                useSlaveLoop = nval( getRequestParameter( flag ),useSlaveLoop );
1637        }
1638
1639        /**
1640         * 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)。
1641         *
1642         * @og.tag
1643         * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが
1644         * ファイルダウンロードの対象の表になります。
1645         *
1646         * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。
1647         * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい
1648         * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から
1649         * 除外することができます。
1650         *
1651         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
1652         *
1653         * @param  flag メイントランザクションかどうか [true:メイン/false:その他]
1654         */
1655        public void setMainTrans( final String flag ) {
1656                isMainTrans = nval( getRequestParameter( flag ),isMainTrans );
1657        }
1658
1659        /**
1660         * 【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。
1661         *
1662         * @og.tag
1663         * groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。
1664         * 初期値は、"," に設定されています。
1665         *
1666         * @og.rev 5.3.1.0 (2011/01/01)
1667         *
1668         * @param   sepa        項目区切り文字 (初期値:",")
1669         */
1670        public void setSeparator( final String sepa ) {
1671                separator = nval( getRequestParameter( sepa ),separator );
1672        }
1673
1674        /**
1675         * このオブジェクトの文字列表現を返します。
1676         * 基本的にデバッグ目的に使用します。
1677         *
1678         * @return このクラスの文字列表現
1679         * @og.rtnNotNull
1680         */
1681        @Override
1682        public String toString() {
1683                return ToString.title( this.getClass().getName() )
1684                                .println( "VERSION"                     ,VERSION                )
1685                                .println( "action"                      ,action                 )
1686                                .println( "tableId"                     ,tableId                )
1687                                .println( "scope"                       ,scope                  )
1688                                .println( "masterTableId"       ,masterTableId  )
1689                                .println( "masterScope"         ,masterScope    )
1690                                .println( "slaveTableId"        ,slaveTableId   )
1691                                .println( "slaveScope"          ,slaveScope             )
1692                                .println( "masterKeys"          ,masterKeys             )
1693                                .println( "diffKeys"            ,diffKeys               )
1694                                .println( "unionClms"           ,unionClms              )
1695                                .println( "nullDelClms"         ,nullDelClms    )
1696                                .println( "modifyClms"          ,modifyClms             )
1697                                .println( "noSideEffect"        ,noSideEffect   )
1698                                .println( "useDiffData"         ,useDiffData    )
1699                                .println( "useCheckOnly"        ,useCheckOnly   )
1700                                .println( "display"                     ,display                )
1701                                .println( "ACTION_LIST"         ,ACTION_SET             )
1702                                .println( "Other..."    ,getAttributes().getAttribute() )                       // 6.4.3.4 (2016/03/11)
1703                                .fixForm().toString() ;
1704        }
1705}