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;
023
024import org.opengion.fukurou.util.StringUtil;
025import static org.opengion.fukurou.util.StringUtil.nval ;
026
027import java.util.Map;
028import java.util.LinkedHashMap;
029import java.util.Locale ;
030
031/**
032 * 2つの DBTableModel の 集合処理を行うタグです。
033 *
034 * マスタとスレーブのそれぞれの DBTableModel を取得し、マージ、差分、排他などの
035 * データ処理を行います。結果を、指定の tableId と scope に書き出します。
036 * マスタテーブルは、masterTableId と masterScope より取り出します。スレーブテーブルは、
037 * slaveTableId と slaveScope より取り出します。
038 * 取り出された DBTableModel は、マスタテーブルに対して、スレーブテーブル情報を参照する形で行われ、
039 * 結果はマスタテーブルに上書きされる形で行われます。
040 * 指定できるアクションは、全体集合(UNION_ALL)、和集合(UNION)、積集合(INTERSECT)、
041 * 差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、 列追加(ADD_CLM)、 グループ(GROUP)です。
042 * 列合成と列追加、グループ以外の処理では、カラム順とカラム数は同数でなければなりません。
043 * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。
044 * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。
045 * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。)
046 * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の
047 * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、
048 * 2回目に現れた情報を削除することになります。グループ が指定された場合は、
049 * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで
050 * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで
051 * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。
052 * (マージ後には、同一主キーを持つ行は存在します。)
053 * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、
054 * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。
055 *
056 * 処理前後でのDBTableModelの件数は、以下の変数で取得することが可能です。
057 *  処理前のマスターテーブル : {@DB.MASTER_COUNT}
058 *  処理前のスレーブテーブル : {@DB.SLAVE_COUNT}
059 *  処理後                   : {@DB.COUNT}
060 *
061 * @og.formSample
062 * ●形式:
063 *       ・<og:tableMerge
064 *             action        = "UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|ADD_CLM|GROUP|UNION_SELROW"
065 *             tableId       = "DEFAULT"        出力テーブルの tableId
066 *             scope         = "session"        出力テーブルの scope
067 *             masterTableId = "DEFAULT"        マスタテーブルの tableId
068 *             masterScope   = "session"        マスタテーブルの scope
069 *             slaveTableId  = "DEFAULT"        スレーブテーブルの tableId
070 *             slaveScope    = "request"        スレーブテーブルの scope
071 *             masterKeys    = "AA,BB,CC"       マスタテーブルの集合処理を行う主キー
072 *             slaveKeys     = "AA,BB,CC"       スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
073 *             diffKeys      = "DD,EE"          マスタテーブルのDIFFERENCE時の差分カラム名
074 *             unionClms     = "DD,EE"          UNION_CLM,ADD_CLM時にスレーブからマスタへ追加するカラム名
075 *             modifyClms    = "FF,GG"          DIFFERENCE時にスレーブからマスタへ値を更新するカラム名
076 *             noSideEffect  = "false"          テーブルモデルに対する副作用(true:ない/false:ある)
077 *             useDiffData   = "true"           DIFFERENCE時に差分のスレーブデータを追加するかどうか
078 *             useCheckOnly  = "false"          マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
079 *             groupAddClms  = "FF,GG"          masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
080 *             display       = "true"           処理概要を表示するかどうか
081 *         />
082 * ●body:なし
083 *
084 * ●Tag定義:
085 *   <og:tableMerge
086 *       command            【TAG】コマンド(NEW,RENEW)をセットします(初期値:NEW)
087 *       action             【TAG】アクションを指定します(UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|ADD_CLM|GROUP|UNION_SELROW)
088 *       tableId            【TAG】出力先のtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
089 *       scope              【TAG】出力先のscopeを指定します(初期値:session)
090 *       masterTableId      【TAG】マスタテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
091 *       masterScope        【TAG】マスタテーブルのscopeを指定します(初期値:session)
092 *       slaveTableId       【TAG】スレーブテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl])
093 *       slaveScope         【TAG】スレーブテーブルのscopeを指定します(初期値:session)
094 *       masterKeys         【TAG】マスタテーブルの集合処理を行う主キーを指定します
095 *       slaveKeys          【TAG】スレーブテーブルの集合処理を行う主キーを指定します
096 *       diffKeys           【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します
097 *       unionClms          【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します
098 *       modifyClms         【TAG】スレーブからマスタへ値を更新するカラム名をCSV形式で指定します
099 *       groupAddClms       【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します
100 *       noSideEffect       【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある)
101 *       useDiffData        【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true)
102 *       useCheckOnly       【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうかを指定します(初期値:false)
103 *       stopZero           【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])
104 *       display            【TAG】マージの結果を表示するかどうかを指定します(初期値:true)
105 *       mainTrans          【TAG】(通常使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false)
106 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
107 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
108 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:true)
109 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:true)
110 *       separator          【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")
111 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
112 *   />
113 *
114 * ●使用例
115 *     例1)デフォルト以外に必要な属性のみ指定するサンプル
116 *     <og:tableMerge action="UNION"
117 *           slaveScope = "request" masterKeys = "AA,BB,CC"
118 *     />
119 *
120 *         ・出力先、マスターともに初期値は、tableId="DEFAULT" scope="session" です。
121 *           スレーブは、tableId か scope をける必要がある為、scope="request" を指定しています。
122 *           比較するカラム名は、マスタ、スレーブ同じであれば、マスタのみ指定でかまいません。
123 *
124 *     例2)マスタ、スレーブともメモリに残らないように request で作成します。
125 *     <og:tableMerge action="INTERSECT"
126 *           masterScope  = "request"
127 *           slaveScope   = "request"
128 *           slaveTableId = "SLAVE"
129 *           masterKeys   = "AA,BB,CC"
130 *     />
131 *
132 *         ・マスタ、スレーブともメモリに残らないように request で作成した場合は、
133 *           どちらかの TableId を変える必要があります。
134 *           出力先は初期値の、tableId="DEFAULT" scope="session" です。
135 *
136 * @og.rev 3.8.0.9 (2005/10/17) 新規追加
137 * @og.group DB登録
138 *
139 * @version  0.9.0      2000/10/17
140 * @author       Kazuhiko Hasegawa
141 * @since    JDK5.0,
142 */
143public class TableMergeTag extends CommonTagSupport {
144        //* このプログラムのVERSION文字列を設定します。   {@value} */
145        private static final String VERSION = "5.7.6.2 (2014/05/16)" ;
146
147        private static final long serialVersionUID = 576220140516L ;
148
149        /** action 引数に渡す事の出来る アクションコマンド  全体集合 {@value} */
150        public static final String ACT_UNION_ALL        = "UNION_ALL" ;
151        /** action 引数に渡す事の出来る アクションコマンド  和集合 {@value} */
152        public static final String ACT_UNION            = "UNION" ;
153        /** action 引数に渡す事の出来る アクションコマンド  積集合 {@value} */
154        public static final String ACT_INTERSECT        = "INTERSECT" ;
155        /** action 引数に渡す事の出来る アクションコマンド  差集合{@value} */
156        public static final String ACT_MINUS            = "MINUS" ;
157        /** action 引数に渡す事の出来る アクションコマンド  差分集合{@value} */
158        public static final String ACT_DIFFERENCE       = "DIFFERENCE" ;
159        /** action 引数に渡す事の出来る アクションコマンド  列合成{@value} */
160        public static final String ACT_UNION_CLM        = "UNION_CLM" ;
161        /** action 引数に渡す事の出来る アクションコマンド  列合成{@value} */
162        public static final String ACT_ADD_CLM          = "ADD_CLM" ;
163        /** action 引数に渡す事の出来る アクションコマンド  グループ {@value} */
164        public static final String ACT_GROUP            = "GROUP" ;
165        /** action 引数に渡す事の出来る アクションコマンド  グループ {@value} */
166        public static final String ACT_UNION_SELROW     = "UNION_SELROW" ;// 4.3.2.0 (2008/09/11)
167
168        /** action 引数に渡す事の出来る アクションコマンド リスト  */
169        private static final String[] ACTION_LIST = new String[] {
170                ACT_UNION_ALL , ACT_UNION , ACT_INTERSECT , ACT_MINUS , ACT_DIFFERENCE , ACT_UNION_CLM , ACT_ADD_CLM , ACT_GROUP, ACT_UNION_SELROW };
171
172        /** command 引数に渡す事の出来る コマンド  新規 {@value} */
173        public static final String CMD_NEW       = "NEW" ;
174        /** command 引数に渡す事の出来る コマンド  再検索 {@value} */
175        public static final String CMD_RENEW = "RENEW" ;
176        /** command 引数に渡す事の出来る コマンド リスト  */
177        private static final String[] COMMAND_LIST = new String[] { CMD_NEW , CMD_RENEW };
178
179        private String  command         = CMD_NEW;
180
181        private String  action          = null;
182        private String  tableId         = HybsSystem.TBL_MDL_KEY;               // 出力先の tableId
183        private String  scope           = "session";                                    // 出力先の scope
184
185        private String  masterTableId   = HybsSystem.TBL_MDL_KEY;       // マスタテーブルの tableId
186        private String  masterScope             = "session";                            // マスタテーブルの scope
187        private String  slaveTableId    = HybsSystem.TBL_MDL_KEY;       // スレーブテーブルの tableId
188        private String  slaveScope              = "request";                            // スレーブテーブルの scope
189        private String  masterKeys              = null;                                         // マスタテーブルの集合処理を行う主キー
190        private String  slaveKeys               = null;                                         // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
191        private String  diffKeys                = null;                                         // マスタテーブルのDIFFERENCE時の差分カラム名
192        private String  unionClms               = null;                                         // UNION_CLM時にスレーブからマスタへ追加するカラム名
193        private String  modifyClms              = null;                                         // DIFFERENCE時にスレーブからマスタへ値を更新するカラム名
194        private String  groupAddClms    = null;                                         // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
195        private boolean noSideEffect    = false;                                        // テーブルモデルに対する副作用(true:ない/false:ある)
196        private boolean useDiffData             = true;                                         // DIFFERENCE時に差分のスレーブデータを追加するかどうか
197        private boolean useCheckOnly    = false;                                        // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
198        private boolean display                 = true;
199        private boolean isMainTrans             = true;                                         // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
200        private String  separator               = ",";                                          // 5.3.1.0 (2011/01/01) groupAddClmnsで結合する際の区切り文字
201        private boolean stopZero                = false;                                        // 5.7.6.2 (2014/05/16) stopZero属性追加
202
203        /**
204         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
205         *
206         * @og.rev 4.3.2.0 (2008/09/11) UNION_SELROWアクション対応
207         * @og.rev 4.3.3.0 (2008/10/01) 処理前後のテーブル件数を取得できるようにする
208         * @og.rev 4.3.3.1 (2008/10/08) スレーブのnullチェック追加
209         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
210         * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
211         * @og.rev 5.7.6.2 (2014/05/16) stopZero属性、DB.COUNT キーで検索件数をリクエストにセットする。
212         *
213         * @return      後続処理の指示
214         */
215        @Override
216        public int doEndTag() {
217                debugPrint();           // 4.0.0 (2005/02/28)
218                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
219                if( !useTag() || ! check( command, COMMAND_LIST ) ) { return EVAL_PAGE ; }
220
221                // 整合性チェック:アクションリストとの存在チェック
222                if( !check( action, ACTION_LIST ) ) {
223                        String errMsg = "指定のアクションは実行できません。アクションエラー"
224                                                        + HybsSystem.CR
225                                                        + "action=[" + action + "] "
226                                                        + HybsSystem.CR
227                                                        + "指定可能なアクション:"
228                                                        + StringUtil.array2csv( ACTION_LIST ) ;
229                        throw new HybsSystemException( errMsg );
230                }
231
232                // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
233                if( slaveKeys == null ) { slaveKeys = masterKeys; }
234
235                super.setScope( masterScope );
236                DBTableModel masterTable = (DBTableModel)getObject( masterTableId );
237
238                // 整合性チェック:マスタテーブルは必須
239                if( masterTable == null ) {
240                        String errMsg = "マスタテーブルは必須です。"
241                                                + HybsSystem.CR
242                                                + "action=[" + action + "] "
243                                                + "masterTableId=[" + masterTableId + "] "
244                                                + "masterScope=[" + masterScope + "] " ;
245                        throw new HybsSystemException( errMsg );
246                }
247                if( noSideEffect ) { masterTable = cloneTable( masterTable ); }
248                Map<String,Integer> masterMap = makeKeyMap( masterTable,masterKeys,useCheckOnly );
249
250                DBTableModel slaveTable = null;
251                Map<String,Integer> slaveMap = null;
252
253                // 整合性チェック:action="GROUP" と "ADD_CLM" 以外では、スレーブテーブルは必須
254                if( ! ACT_GROUP.equalsIgnoreCase( action ) && !ACT_ADD_CLM.equalsIgnoreCase( action ) ) {
255                        super.setScope( slaveScope );
256                        slaveTable = (DBTableModel)getObject( slaveTableId );
257
258                        if( slaveTable == null ) {
259                                String errMsg = "action=\"" + action + "\" 時には、スレーブテーブルが必要です。"
260                                                        + HybsSystem.CR
261                                                        + "slaveTableId=[" + slaveTableId + "] "
262                                                        + "slaveScope=[" + slaveScope + "] " ;
263                                throw new HybsSystemException( errMsg );
264                        }
265                        if( noSideEffect ) { slaveTable = cloneTable( slaveTable ); }
266                        slaveMap = makeKeyMap( slaveTable,slaveKeys,false );    // スレーブはuseCheckOnly無関係
267
268                        // その場合、マスタとスレーブが同一メモリ上のテーブル(物理的に同じ)でない事。
269                        if( slaveTable == masterTable ) {
270                                String errMsg = "マスタとスレーブが同一メモリになっています。"
271                                                        + "通常、検索結果は TableId と Scope を別々に指定します。"
272                                                        + HybsSystem.CR
273                                                        + "action=[" + action + "] "
274                                                        + "masterTableId=[" + masterTableId + "] "
275                                                        + "slaveTableId=[" + slaveTableId + "] "
276                                                        + "masterScope=[" + masterScope + "] "
277                                                        + "slaveScope=[" + slaveScope + "] " ;
278                                throw new HybsSystemException( errMsg );
279                        }
280                }
281
282                super.setScope( scope );
283                useMainTrans( isMainTrans );                    // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
284                startQueryTransaction( tableId );               // 3.6.0.8 (2004/11/19)
285
286                // 設定値の整合性チェック
287                // 整合性チェック:action="UNION_ALL" と "GROUP"と "ADD_CLM"と "UNION_CLM" 以外では、masterKeys が必須
288                if( ! ACT_UNION_ALL.equalsIgnoreCase( action )
289                        && !ACT_UNION_SELROW.equalsIgnoreCase( action ) // 4.3.2.0 (2008/09/11)
290                        && !ACT_GROUP.equalsIgnoreCase( action )
291                        && !ACT_ADD_CLM.equalsIgnoreCase( action )
292                        && !ACT_UNION_CLM.equalsIgnoreCase( action )
293                        && masterKeys == null ) {
294                                String errMsg = "action=\"" + action + "\" 時には、masterKeys が必須です。"
295                                                        + HybsSystem.CR
296                                                        + "masterKeys=[" + masterKeys + "] ";
297                                throw new HybsSystemException( errMsg );
298                }
299
300                // 設定値の整合性チェック
301                // 整合性チェック:action="DIFFERENCE" では、diffKeys が必須
302                if( ACT_DIFFERENCE.equalsIgnoreCase( action ) && diffKeys == null ) {
303                        String errMsg = "action=\"" + action + "\" 時には、diffKeys が必須です。"
304                                                + HybsSystem.CR
305                                                + "diffKeys=[" + diffKeys + "] ";
306                        throw new HybsSystemException( errMsg );
307                }
308
309                // 設定値の整合性チェック
310                // 整合性チェック:列合成(UNION_CLM) と グループ(GROUP) と 列追加(ADD_CLM) 以外では、テーブルカラム数が同じである必要がある。
311                if( ! ACT_UNION_CLM.equalsIgnoreCase( action )
312                         && !ACT_GROUP.equalsIgnoreCase( action )
313                         && !ACT_ADD_CLM.equalsIgnoreCase( action ) ) {
314                        int mClmSize = masterTable.getColumnCount();
315                        int sClmSize = slaveTable.getColumnCount();
316
317                        if( mClmSize != sClmSize ) {
318                                String errMsg = "action=\"" + action + "\" 時には、テーブルカラム数が異なってはいけません。"
319                                                        + HybsSystem.CR
320                                                        + "Master=" + mClmSize + " ,["
321                                                        + StringUtil.array2csv( masterTable.getNames() ) + "]"
322                                                        + HybsSystem.CR
323                                                        + "Slave =" + sClmSize + " ,["
324                                                        + StringUtil.array2csv( slaveTable.getNames() ) + "]";
325                                throw new HybsSystemException( errMsg );
326                        }
327                }
328
329                StringBuilder buf = null;
330                if( display ) {
331                        buf = new StringBuilder();
332                        buf.append( action ).append( "(" );
333                        buf.append( " M[" ).append( masterTable.getRowCount() ).append( "]" );
334                        if( slaveTable != null ) {
335                                buf.append( ",S[" ).append( slaveTable.getRowCount() ).append( "]" );
336                        }
337                }
338
339                // 4.3.3.0 (2008/10/01)
340                setRequestAttribute( "DB.MASTER_COUNT"   , String.valueOf( masterTable.getRowCount() ) );
341                // 4.3.3.1 (2008/10/08) nullチェック
342                if( slaveTable != null ) {
343                        setRequestAttribute( "DB.SLAVE_COUNT", String.valueOf( slaveTable.getRowCount() ) );
344                }
345
346                DBTableModel table = null;
347                if( ACT_UNION_ALL.equalsIgnoreCase( action ) ) {                        // 全体集合
348                        table = makeUnionAll( masterTable,slaveTable );
349                }
350                else if( ACT_UNION_SELROW.equalsIgnoreCase( action ) ) {        // 4.3.2.0 (2008/09/11) 全体集合(slave表をmaster表のチェック行から追加)
351                        table = makeUnionSelrow( masterTable,slaveTable );
352                }
353                else if( ACT_UNION.equalsIgnoreCase( action ) ) {                       // 和集合
354                        table = makeUnion( masterTable,masterMap,slaveTable,slaveMap );
355                }
356                else if( ACT_INTERSECT.equalsIgnoreCase( action ) ) {           // 積集合
357                        table = makeIntersect( masterTable,masterMap,slaveMap );
358                }
359                else if( ACT_MINUS.equalsIgnoreCase( action ) ) {                       // 差集合
360                        table = makeMinus( masterTable,masterMap,slaveMap );
361                }
362                else if( ACT_DIFFERENCE.equalsIgnoreCase( action ) ) {          // 差分集合
363                        table = makeDifference( masterTable,masterMap,slaveTable,slaveMap );
364                }
365                else if( ACT_UNION_CLM.equalsIgnoreCase( action ) ) {           // 列合成
366                        if( unionClms == null ) {
367                                String errMsg = "action=\"UNION_CLM\" 時には、unionClms が必須です。" ;
368                                throw new HybsSystemException( errMsg );
369                        }
370
371                        table = makeUnionClm( masterTable,slaveKeys,slaveTable,slaveMap );
372                }
373                else if( ACT_ADD_CLM.equalsIgnoreCase( action ) ) {             // 列追加
374                        if( unionClms == null ) {
375                                String errMsg = "action=\"ADD_CLM\" 時には、unionClms が必須です。" ;
376                                throw new HybsSystemException( errMsg );
377                        }
378
379                        table = makeAddClm( masterTable );
380                }
381                else if( ACT_GROUP.equalsIgnoreCase( action ) ) {                       // グループ
382                        table = makeGroup( masterTable );
383                }
384
385                if( table != null ) {
386                        // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
387                        super.setScope( scope );
388                        if( ! commitTableObject( tableId, table ) ) {
389                                return SKIP_PAGE ;
390                        }
391                }
392
393                // 5.7.6.2 (2014/05/16) マージした結果の DBTableModel のデータ件数
394                int rowCnt = table.getRowCount();
395
396                if( display ) {
397//                      buf.append( " ) = [" ).append( table.getRowCount() ).append( "]" );
398                        buf.append( " ) = [" ).append( rowCnt ).append( "]" );
399                        jspPrint( buf.toString() );
400                }
401
402                // 5.7.6.2 (2014/05/16) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
403                setRequestAttribute( "DB.COUNT" , String.valueOf( rowCnt ) );
404
405                // 5.7.6.2 (2014/05/16) 件数0件かつ stopZero = true
406                if( rowCnt == 0 && stopZero ) { return SKIP_PAGE; }
407
408                // 4.3.4.4 (2009/01/01)
409//              setRequestAttribute( "DB.COUNT"   , ( table == null ? 0 :String.valueOf( table.getRowCount() ) ) );
410
411                return EVAL_PAGE ;              // ページの残りを評価する。
412        }
413
414        /**
415         * タグリブオブジェクトをリリースします。
416         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
417         *
418         * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加
419         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
420         * @og.rev 5.3.1.0 (2011/01/01) separator追加
421         * @og.rev 5.7.6.2 (2014/05/16) stopZero属性追加
422         */
423        @Override
424        protected void release2() {
425                super.release2();
426                command                 = CMD_NEW;
427                action                  = null;
428                tableId                 = HybsSystem.TBL_MDL_KEY;       // 出力先の tableId
429                scope                   = "session";                            // 出力先の scope
430                masterTableId   = HybsSystem.TBL_MDL_KEY;       // マスタテーブルの tableId
431                masterScope             = "session";                            // マスタテーブルの scope
432                slaveTableId    = HybsSystem.TBL_MDL_KEY;       // スレーブテーブルの tableId
433                slaveScope              = "request";                            // スレーブテーブルの scope
434                masterKeys              = null;                                         // マスタテーブルの集合処理を行う主キー
435                slaveKeys               = null;                                         // スレーブテーブルの集合処理を行う主キー(null時=masterKeys)
436                diffKeys                = null;                                         // マスタテーブルのDIFFERENCE時の差分カラム名
437                unionClms               = null;                                         // スレーブからマスタへ追加するカラム名
438                modifyClms              = null;                                         // スレーブからマスタへ値を更新するカラム名
439                groupAddClms    = null;                                         // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名
440                noSideEffect    = false;                                        // テーブルモデルに対する副作用(true:ない/false:ある)
441                useDiffData             = true;                                         // DIFFERENCE時に差分のスレーブデータを追加するかどうか
442                useCheckOnly    = false;                                        // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか
443                display                 = true;
444                stopZero                = false;                                        // 5.7.6.2 (2014/05/16) stopZero属性追加
445                isMainTrans             = true;                                         // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
446                separator               = ",";
447        }
448
449        /**
450         * 指定のテーブルの指定のカラム(CSV)より、行番号をマップ化します。
451         * なお、引数のテーブルについては、主キーによるグループ処理が行われます。(副作用あり)
452         *
453         * @og.rev 4.3.2.0 (2008/09/11) マスタキー未指定時は何もしない
454         * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加
455         * @og.rev 5.3.1.0 (2011/01/01) groupAddClms の separator指定対応
456         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
457         *
458         * @param       table   マップ作成元のデータテーブル(副作用あり)
459         * @param       keys    マップ作成の主キー(CSV形式)
460         * @param       useCheckOnly    チェック行のみを対象にするかどうか
461         *
462         * @return マップ化された主キーと行番号
463         */
464        private Map<String,Integer> makeKeyMap( final DBTableModel table, final String keys, final boolean useCheckOnly ) {
465
466                // カラム名をカラム番号に変換します。
467                int[] clmNo = makeColumnNo( table,keys );
468                int   clmSize = clmNo.length;
469                if( clmSize == 0 ) {    // マスタキー未指定時には全カラムを使用します。
470                        return null; // 4.3.2.0 (2008/09/11)
471                }
472
473                // groupAddClms をカラム番号に変換します。
474                int[] grpClmNo = makeColumnNo( table,groupAddClms );
475                int   grpClmSize = grpClmNo.length;
476
477                Map<String,Integer> tempMap = new LinkedHashMap<String,Integer>();
478
479                // 注意:GROUP化の過程で テーブル件数が動的に変化します。
480                StringBuilder buf ;
481                for( int row=0; row<table.getRowCount(); row++ ) {
482                        if( useCheckOnly && table.getModifyType( row ).length() == 0 ) {
483                                continue;
484                        }
485
486                        buf = new StringBuilder();
487                        for( int i=0; i<clmSize; i++ ) {
488                                buf.append( table.getValue( row,clmNo[i] ) ).append( "|" );
489                        }
490                        // 主キー連結文字列
491                        String key = buf.toString().toUpperCase(Locale.JAPAN);          // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
492                        if( tempMap.containsKey( key ) ) {
493                                // 5.1.4.0 (2010/03/01) groupAddClms 追加
494                                int orgRow = tempMap.get( key ).intValue() ;                            // 追加済みの行番号を取得
495                                for( int i=0; i<grpClmSize; i++ ) {
496                                        int clm    = grpClmNo[i];
497                                        String val1 = table.getValue( orgRow,clm ) ;                    // 既存の行・カラムのデータ
498                                        String val2 = table.getValue( row,clm ) ;                               // 削除する行・カラムのデータ
499                                        // 5.3.1.0 (2011/01/01)
500                                        table.setValueAt( val1 + separator + val2,orgRow,clm );         // CSV 連結、データを戻す。
501                                }
502                                table.removeValue( row );
503                                row-- ;
504                        }
505                        else {                                                  // まだ、未登録の場合
506                                tempMap.put( key,Integer.valueOf( row ) );
507                        }
508                }
509
510                return tempMap;
511        }
512
513        /**
514         * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。
515         *
516         * @param       masterTbl       マスターテーブルモデル
517         * @param       slaveTbl        スレーブテーブルモデル
518         *
519         * @return      処理結果のテーブルモデル
520         */
521        private DBTableModel makeUnionAll( final DBTableModel masterTbl,final DBTableModel slaveTbl ) {
522                DBTableModel table = masterTbl;
523
524                for( int row=0; row<slaveTbl.getRowCount(); row++ ) {
525                        String[] vals = slaveTbl.getValues( row );
526                        table.addColumnValues( vals );
527                }
528
529                return table;
530        }
531
532        /**
533         * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。
534         * スレーブ表は、マスタ表のチェックされた行を起点として登録されます。
535         * チェックされていない場合は、スレーブ表は先頭から追加されます。
536         *
537         * @og.rev 4.3.2.0 (2008/09/11) 新規作成
538         *
539         * @param       masterTbl       マスターテーブルモデル
540         * @param       slaveTbl        スレーブテーブルモデル
541         *
542         * @return      処理結果のテーブルモデル
543         */
544        private DBTableModel makeUnionSelrow( final DBTableModel masterTbl,final DBTableModel slaveTbl ) {
545                DBTableModel table = masterTbl;
546
547                int insRowNo = ( getParameterRows().length > 0 ) ? getParameterRows()[0] + 1 : 0 ;
548                for( int row=0; row<slaveTbl.getRowCount(); row++ ) {
549                        String[] vals = slaveTbl.getValues( row );
550                        table.addValues( vals, insRowNo + row, false );
551                }
552
553                return table;
554        }
555
556        /**
557         * 指定のマスタ,スレーブテーブルを使用して 和集合 処理を実行します。
558         *
559         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
560         *
561         * @param       masterTbl       マスターテーブルモデル
562         * @param       masterMap       マスターテーブルの主キーマップ
563         * @param       slaveTbl        スレーブテーブルモデル
564         * @param       slaveMap        スレーブテーブルの主キーマップ
565         *
566         * @return      処理結果のテーブルモデル
567         */
568        private DBTableModel makeUnion( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
569                                                                        final DBTableModel slaveTbl,final Map<String,Integer> slaveMap ) {
570                DBTableModel table = masterTbl;
571
572                @SuppressWarnings("rawtypes")
573                Map.Entry[] entry = slaveMap.entrySet().toArray( new Map.Entry[slaveMap.size()] );
574
575                int size = entry.length;
576                for( int i=0; i<size; i++ ) {
577                        String key  = (String)entry[i].getKey();
578                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
579                        if( ! masterMap.containsKey( key ) ) {          // マスタにスレーブデータが存在しない
580                                int row =  ((Integer)entry[i].getValue()).intValue();
581                                String[] vals = slaveTbl.getValues( row );
582                                table.addColumnValues( vals );
583                        }
584                }
585                return table;
586        }
587
588        /**
589         * 指定のマスタ,スレーブテーブルを使用して 積集合 処理を実行します。
590         *
591         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
592         *
593         * @param       masterTbl       マスターテーブルモデル
594         * @param       masterMap       マスターテーブルの主キーマップ
595         * @param       slaveMap        スレーブテーブルの主キーマップ
596         *
597         * @return      処理結果のテーブルモデル
598         */
599        private DBTableModel makeIntersect( final DBTableModel masterTbl,final Map<String,Integer> masterMap, final Map<String,Integer> slaveMap ) {
600                DBTableModel table = masterTbl;
601
602                @SuppressWarnings("rawtypes")
603                Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
604
605                int size = entry.length;
606                for( int i=0; i<size; i++ ) {
607                        String key  = (String)entry[i].getKey();
608                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
609                        if( ! slaveMap.containsKey( key ) ) {           // スレーブにマスタが存在しない
610                                int row =  ((Integer)entry[i].getValue()).intValue();
611                                table.rowDelete( row );                         // 論理削除
612                        }
613                }
614
615                // 行番号が変わらないように逆順削除します。
616                for( int row=table.getRowCount()-1; row>=0; row-- ) {
617                        if( DBTableModel.DELETE_TYPE.equalsIgnoreCase( table.getModifyType( row ) ) ) {         // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
618                                table.removeValue( row );
619                        }
620                }
621
622                return table;
623        }
624
625        /**
626         * 指定のマスタ,スレーブテーブルを使用して 差集合 処理を実行します。
627         *
628         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
629         *
630         * @param       masterTbl       マスターテーブルモデル
631         * @param       masterMap       マスターテーブルの主キーマップ
632         * @param       slaveMap        スレーブテーブルの主キーマップ
633         *
634         * @return      処理結果のテーブルモデル
635         */
636        private DBTableModel makeMinus( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
637                                                                        final Map<String,Integer> slaveMap ) {
638                DBTableModel table = masterTbl;
639
640                @SuppressWarnings("rawtypes")
641                Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
642
643                int size = entry.length;
644                for( int i=0; i<size; i++ ) {
645                        String key  = (String)entry[i].getKey();
646                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
647                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタが存在する
648                                int row =  ((Integer)entry[i].getValue()).intValue();
649                                table.rowDelete( row );                         // 論理削除
650                        }
651                }
652
653                // 行番号が変わらないように逆順削除します。
654                for( int row=table.getRowCount()-1; row>=0; row-- ) {
655                        if( DBTableModel.DELETE_TYPE.equalsIgnoreCase( table.getModifyType( row ) ) ) {         // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
656                                table.removeValue( row );
657                        }
658                }
659
660                return table;
661        }
662
663        /**
664         * 指定のマスタ,スレーブテーブルを使用して 差分集合 処理を実行します。
665         *
666         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
667         *
668         * @param       masterTbl       マスターテーブルモデル
669         * @param       masterMap       マスターテーブルの主キーマップ
670         * @param       slaveTbl        スレーブテーブルモデル
671         * @param       slaveMap        スレーブテーブルの主キーマップ
672         *
673         * @return      処理結果のテーブルモデル
674         */
675        private DBTableModel makeDifference( final DBTableModel masterTbl,final Map<String,Integer> masterMap,
676                                                                                 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) {
677                DBTableModel table = masterTbl;
678
679                // テーブルの差分属性のカラム番号を求めます。(マスタ、スレーブ共通)
680                int[] diffClmNo = makeColumnNo( table,diffKeys );
681                int diffClmSize = diffClmNo.length;
682
683                // スレーブからマスタへ値の再セットを行うカラム番号を求めます。(マスタ、スレーブ共通)
684                int[] modClmNo = makeColumnNo( table,modifyClms );
685                int modClmSize = modClmNo.length;
686
687                int clmSize = table.getColumnCount();
688                DBTableModel newTable = DBTableModelUtil.newDBTable();
689                newTable.init( clmSize );
690
691                // テーブルの全カラムを新たに作成するテーブルにコピーします。
692                for( int i=0; i<clmSize; i++ ) {
693                        newTable.setDBColumn( i,table.getDBColumn( i ) );
694                }
695
696                // スレーブの先頭カラムが、WRITABLE かどうか
697                boolean writeFlag = "WRITABLE".equalsIgnoreCase( slaveTbl.getColumnName(0) );
698
699                @SuppressWarnings("rawtypes")
700                Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] );
701
702                int size = entry.length;
703                for( int i=0; i<size; i++ ) {
704                        String key  = (String)entry[i].getKey();
705                        if( key != null ) { key = key.toUpperCase(Locale.JAPAN); }              // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
706                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタが存在する
707                                int mrow =  ((Integer)entry[i].getValue()).intValue();
708                                int srow =  slaveMap.get( key ).intValue();
709                                boolean unmatched = false;
710                                for( int j=0; j<diffClmSize; j++ ) {
711                                        String mval = table.getValue( mrow,diffClmNo[j] );
712                                        String sval = slaveTbl.getValue( srow,diffClmNo[j] );
713                                        if( mval != null && !mval.equalsIgnoreCase( sval ) ) { unmatched = true; break; }               // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
714                                }
715
716                                if( unmatched ) {               // 属性情報が異なる場合のみ処理
717                                        // データのコピー
718                                        String[] mvals = new String[clmSize];
719                                        System.arraycopy( table.getValues( mrow ),0,mvals,0 ,clmSize );
720
721                                        // スレーブの値をマスタの値にセットする。
722                                        for( int j=0; j<modClmSize; j++ ) {
723                                                String val = slaveTbl.getValue( srow,modClmNo[j] );
724                                                mvals[modClmNo[j]] = val;
725                                        }
726                                        newTable.addColumnValues( mvals );
727
728                                        if( useDiffData ) {
729                                                // データのコピー
730                                                String[] svals = new String[clmSize];
731                                                System.arraycopy( slaveTbl.getValues( srow ),0,svals,0 ,clmSize );
732                                                if( writeFlag ) { svals[0] = "0"; }     // 書き込み不許可
733                                                for( int j=0; j<diffClmSize; j++ ) {
734                                                        int dclmNo = diffClmNo[j];
735                                                        String mval = mvals[dclmNo];
736                                                        String sval = svals[dclmNo];
737                                                        if( mval != null && !mval.equalsIgnoreCase( sval ) ) {          // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
738                                                                svals[dclmNo] = "<span class=\"unmatched\">" + sval + "</span>" ;
739                                                        }
740                                                }
741                                                newTable.addColumnValues( svals );
742        //                                      newTable.setRowWritable( newTable.getRowCount()-1,false );      // 書き込み不許可
743                                        }
744                                }
745                        }
746                }
747                return newTable;
748        }
749
750        /**
751         * 指定のマスタ,スレーブテーブルを使用して 列合成 処理を実行します。
752         *
753         * すでに、マスタ にカラムが存在している場合は、値の書き換えを、
754         * カラムが存在しなければ、カラムを追加します。
755         * マスタとスレーブのデータ突合せ時のキーは、slaveKeys になります。
756         * これは、masterMap を使用しないことを意味します。(masterKeys 指定によるGROUP化は
757         * 行います。masterKeys を指定しない場合は、サマライズなしで処理します。)
758         * 具体的には、マスタテーブルの複数の行に対して、スレーブデータの値が設定
759         * される可能性があることを意味します。
760         *
761         * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
762         *
763         * @param       masterTbl       マスターテーブルモデル
764         * @param       slaveKeys       スレーブキー
765         * @param       slaveTbl        スレーブテーブルモデル
766         * @param       slaveMap        スレーブテーブルの主キーマップ
767         *
768         * @return      処理結果のテーブルモデル
769         */
770        private DBTableModel makeUnionClm( final DBTableModel masterTbl ,final String slaveKeys ,
771                                                                                final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) {
772
773                DBTableModel table = makeAddClm( masterTbl );
774
775                // カラム名をカラム番号に変換します。
776                int[] keyClmNo = makeColumnNo( table,slaveKeys );
777
778                int[] mClmNo = makeColumnNo( table   ,unionClms );
779                int[] sClmNo = makeColumnNo( slaveTbl,unionClms );
780                int   addSize = mClmNo.length;          // unionClms の個数なので、マスタ、スレーブは同一
781
782                // 突合せを行い、マッチするカラムについて、値をセットし直します。
783                for( int row=0; row<table.getRowCount(); row++ ) {
784                        String[] mvals = table.getValues( row );
785                        StringBuilder buf = new StringBuilder();
786                        for( int i=0; i<keyClmNo.length; i++ ) {
787                                buf.append( mvals[keyClmNo[i]] ).append( "|" );
788                        }
789                        // 主キー連結文字列
790                        String key = buf.toString().toUpperCase(Locale.JAPAN);          // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。
791                        if( slaveMap.containsKey( key ) ) {             // スレーブにマスタに対応するデータが存在する
792                                int slvRow = slaveMap.get( key ).intValue();
793                                String[] svals = slaveTbl.getValues( slvRow );          // スレーブの行番号
794                                for( int j=0; j<addSize; j++ ) {
795                                        mvals[mClmNo[j]] = svals[sClmNo[j]];
796                                }
797                                table.setValues( mvals,row );
798                        }
799                }
800                table.resetModify();
801
802                return table;
803        }
804
805        /**
806         * 指定のマスタテーブルに 列追加 処理を実行します。
807         *
808         * すでに、マスタ にカラムが存在している場合は、値の書き換えを、
809         * カラムが存在しなければ、カラムを追加します。
810         *
811         * @param       masterTbl       マスターテーブルモデル
812         *
813         * @return      処理結果のテーブルモデル
814         */
815        private DBTableModel makeAddClm( final DBTableModel masterTbl ) {
816                String[]  addClms        = StringUtil.csv2Array( unionClms );
817                int       addClmSize = addClms.length;
818                boolean[] useAdd         = new boolean[addClmSize];
819                int addSize = 0;
820                for( int i=0; i<addClmSize; i++ ) {
821                        if( masterTbl.getColumnNo( addClms[i],false ) < 0 ) {   // 存在しなければ -1
822                                useAdd[i] = true;
823                                addSize++ ;
824                        }
825                        else {
826                                useAdd[i] = false;              // マスタに存在すれば、追加不要
827                        }
828                }
829
830                int mstrClmSize = masterTbl.getColumnCount();
831                int clmSize = mstrClmSize + addSize;
832
833                DBTableModel table = DBTableModelUtil.newDBTable();
834                table.init( clmSize );
835                int clmNo = 0;
836                // マスタテーブルの全カラムを新たに作成するテーブルにコピーします。
837                for( int i=0; i<mstrClmSize; i++ ) {
838                        table.setDBColumn( clmNo++,masterTbl.getDBColumn( i ) );
839                }
840                // 追加するカラムを新たに作成するテーブルに追加します。
841                String[] addClmVal = new String[addSize];       // 追加分のみ
842                int addCnt = 0;
843                for( int i=0; i<addClmSize; i++ ) {
844                        if( useAdd[i] ) {
845                                DBColumn column = getDBColumn( addClms[i] );
846                                addClmVal[addCnt++] = nval( column.getDefault(),"" );
847                                table.setDBColumn( clmNo++,column );
848                        }
849                }
850                // 一旦、すべてのマスタデータを新テーブルにコピーします。
851                for( int row=0; row<masterTbl.getRowCount(); row++ ) {
852                        String[] vals = new String[clmSize];
853                        System.arraycopy( masterTbl.getValues( row ),0,vals,0 ,mstrClmSize );   // マスタデータのコピー
854                        System.arraycopy( addClmVal,0,vals,mstrClmSize ,addSize );                              // 追加列情報の初期値
855                        table.addColumnValues( vals );
856                }
857
858                return table;
859        }
860
861        /**
862         * 指定のマスタテーブルを使用して グループ 処理を実行します。
863         *
864         * @param       masterTbl       マスターテーブルモデル
865         *
866         * @return      処理結果のテーブルモデル
867         */
868        private DBTableModel makeGroup( final DBTableModel masterTbl ) {
869                DBTableModel table = masterTbl;
870
871                return table;
872        }
873
874        /**
875         * 指定のテーブルと同じテーブルを別オブジェクトとして作成します。
876         *
877         * @param       tbl     コピー元テーブルモデル
878         *
879         * @return      コピーされた新テーブルモデル
880         */
881        private DBTableModel cloneTable( final DBTableModel tbl ) {
882                int clmSize = tbl.getColumnCount();
883
884                DBTableModel table = DBTableModelUtil.newDBTable();
885                table.init( clmSize );
886
887                // テーブルの全カラムを新たに作成するテーブルにコピーします。
888                for( int i=0; i<clmSize; i++ ) {
889                        table.setDBColumn( i,tbl.getDBColumn( i ) );
890                }
891                // すべてのデータを新テーブルにコピーします。
892                for( int row=0; row<tbl.getRowCount(); row++ ) {
893                        String[] vals = new String[clmSize];
894                        System.arraycopy( tbl.getValues( row ),0,vals,0 ,clmSize );     // データのコピー
895                        table.addColumnValues( vals );
896                }
897                return table;
898        }
899
900        /**
901         * 指定のテーブルより、カラム列名に対応するカラム番号配列を作成します。
902         *
903         * カラム名のCSVデータが 指定されない場合(clmcsv == null)は、長さ0の配列を返します。
904         *
905         * @param       table   テーブルモデル
906         * @param       clmcsv  カラム名のCSVデータ
907         *
908         * @return      カラム名に対応する、列番号配列(なければ、長さ0配列)
909         */
910        private int[] makeColumnNo( final DBTableModel table,final String clmcsv ) {
911
912                // マスタテーブルの差分属性のカラム番号を求めます。
913                String[] clms = StringUtil.csv2Array( clmcsv );
914                int[] clmNo = new int[clms.length];
915
916                // カラム名をカラム番号に変換します。
917                for( int i=0; i<clms.length; i++ ) {
918                        clmNo[i] = table.getColumnNo( clms[i] );
919                }
920
921                return clmNo;
922        }
923
924        /**
925         * 【TAG】コマンド(NEW,RENEW)をセットします(初期値:NEW)。
926         *
927         * @og.tag
928         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
929         * フィールド定数値のいづれかを、指定できます。
930         * 初期値は NEW です。
931         *
932         * @param       cmd     コマンド(public static final 宣言されている文字列)
933         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.TableMergeTag.CMD_NEW">コマンド定数</a>
934         */
935        public void setCommand( final String cmd ) {
936                String cmd2 = getRequestParameter( cmd );
937                if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
938        }
939
940        /**
941         * 【TAG】アクションを指定します(UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|ADD_CLM|GROUP|UNION_SELROW)。
942         *
943         * @og.tag
944         * 指定できるアクションは、全体集合(UNION_ALL)、全体集合(挿入位置指定)(UNION_SELROW)、和集合(UNION)
945         * 、積集合(INTERSECT)、差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、列追加(ADD_CLM)、 グループ(GROUP)です。
946         * 列合成とグループ以外の処理では、カラム順とカラム数は同数でなければなりません。
947         * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。
948         * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。
949         * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。)
950         * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の
951         * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、
952         * 2回目に現れた情報を削除することになります。グループ が指定された場合は、
953         * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで
954         * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで
955         * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。
956         * (マージ後には、同一主キーを持つ行は存在します。)
957         * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、
958         * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。
959         *
960         * <table border="1" frame="box" rules="all" >
961         *   <caption>アクションの説明</caption>
962         *   <tr><th>action      </th><th>名称    </th><th>処理概要                                                                                           </th><th>1</th><th>2</th><th>3</th><th>4</th><tr>
963         *   <tr><td>UNION_ALL   </td><td>全体集合</td><td>マスタとスレーブを合成
964         *   <tr><td>UNION_SELROW</td><td>全体集合</td><td>マスタとスレーブを合成(マスタ表のチェック行を起点に追加</td><td>○</td><td> </td><td> </td><td> </td><tr>
965         *   <tr><td>UNION       </td><td>和集合  </td><td>マスタとスレーブのユニーク部のみ合成                                      </td><td>○</td><td>○</td><td> </td><td> </td><tr>
966         *   <tr><td>INTERSECT   </td><td>積集合  </td><td>マスタとスレーブのユニーク部が一致するマスタのみ選択      </td><td>○</td><td>○</td><td> </td><td> </td><tr>
967         *   <tr><td>MINUS       </td><td>差集合  </td><td>マスタからスレーブに存在するユニーク部を削除した残り      </td><td>○</td><td>○</td><td> </td><td> </td><tr>
968         *   <tr><td>DIFFERENCE  </td><td>差分集合</td><td>ユニーク部が一致し、差分カラムが異なるマスタのみ選択       </td><td>○</td><td>○</td><td> </td><td>○</td><tr>
969         *   <tr><td>UNION_CLM   </td><td>列合成  </td><td>マスタとキー一致するスレーブのカラム情報を追加         </td><td>○</td><td>○</td><td>○</td><td> </td><tr>
970         *   <tr><td>ADD_CLM     </td><td>列追加  </td><td>UNION_CLMとの違いは、カラムのみ追加することです。   </td><td> </td><td> </td><td>○</td><td> </td><tr>
971         *   <tr><td>GROUP       </td><td>グループ</td><td>マスタのユニーク部化                                                                       </td><td> </td><td> </td><td> </td><td> </td><tr>
972         * </table>
973         *
974         * ※:マスタテーブルオブジェクトは、常に必須
975         * 1:スレーブテーブルオブジェクト必須
976         * 2:masterKeys 属性必須
977         * 3:unionClms 属性必須(スレーブテーブルのカラム名または追加するカラム名)
978         * 4:diffKeys 属性必須(DIFFERENCE時の差分カラム名)、modifyClms 属性使用可能
979         *
980         * @param       action アクション(UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|ADD_CLM|GROUP|UNION_SELROW)
981         */
982        public void setAction( final String action ) {
983                this.action = nval( getRequestParameter( action ),this.action );
984        }
985
986        /**
987         * 【TAG】出力先のtableIdを指定します
988         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
989         *
990         * @og.tag
991         * 集合処理結果の DBTableModel をメモリにセットする場合のキー(tableId)を指定します。
992         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
993         *
994         * @param       tableId 出力先のtableId
995         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
996         */
997        public void setTableId( final String tableId ) {
998                this.tableId = nval( getRequestParameter( tableId ),this.tableId );
999        }
1000
1001        /**
1002         * 【TAG】出力先のscopeを指定します(初期値:session)。
1003         *
1004         * @og.tag
1005         * 集合処理結果の DBTableModel をメモリにセットする場合のスコープを指定します。
1006         * ここでは、マスタやスレーブのスコープ設定が必要な為、superクラスのメソッドを
1007         * オーバーライドしてこのオブジェクト内でキープしています。
1008         * 初期値は、session です。
1009         *
1010         * @param       scope   出力先のscope
1011         */
1012        @Override
1013        public void setScope( final String scope ) {
1014                this.scope = nval( getRequestParameter( scope ),this.scope );
1015        }
1016
1017        /**
1018         * 【TAG】マスタテーブルのtableIdを指定します
1019         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1020         *
1021         * @og.tag
1022         * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。
1023         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1024         *
1025         * @param       masterTableId   マスタテーブルのtableId
1026         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
1027         */
1028        public void setMasterTableId( final String masterTableId ) {
1029                this.masterTableId = nval( getRequestParameter( masterTableId ),this.masterTableId );
1030        }
1031
1032        /**
1033         * 【TAG】マスタテーブルのscopeを指定します(初期値:session)。
1034         *
1035         * @og.tag
1036         * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のスコープを指定します。
1037         * 初期値は、session です。
1038         *
1039         * @param       masterScope     マスタテーブルのscope
1040         */
1041        public void setMasterScope( final String masterScope ) {
1042                this.masterScope = nval( getRequestParameter( masterScope ),this.masterScope );
1043        }
1044
1045        /**
1046         * 【TAG】マスタテーブルの集合処理を行う主キーを指定します。
1047         *
1048         * @og.tag
1049         * 集合処理を行う場合の、カラム名を、カンマ区切り文字(CSV形式)で指定します。
1050         * このキーの組み合わせを元に、集合処理の突合せを行います。
1051         * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と
1052         * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。
1053         * アクションが全体集合(UNION_ALL)以外の場合は、必須属性になります。
1054         *
1055         * @param       masterKeys      マスタテーブルの主キーをCSV形式で指定
1056         */
1057        public void setMasterKeys( final String masterKeys ) {
1058                this.masterKeys = nval( getRequestParameter( masterKeys ),this.masterKeys );
1059        }
1060
1061        /**
1062         * 【TAG】スレーブテーブルの集合処理を行う主キーを指定します。
1063         *
1064         * @og.tag
1065         * 集合処理を行う場合の、カラム名を、カンマ区切り文字(CSV形式)で指定します。
1066         * このキーの組み合わせを元に、集合処理の突合せを行います。
1067         * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と
1068         * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。
1069         * null の場合は、masterKeys と同じとします。
1070         *
1071         * @param       slaveKeys       スレーブテーブルの主キーをCSV形式で指定
1072         */
1073        public void setSlaveKeys( final String slaveKeys ) {
1074                this.slaveKeys = nval( getRequestParameter( slaveKeys ),this.slaveKeys );
1075        }
1076
1077        /**
1078         * 【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します。
1079         *
1080         * @og.tag
1081         * アクションが差分処理(DIFFERENCE)の場合に、差分チェックを行うカラム名を、
1082         * カンマ区切り文字(CSV形式)で指定します。
1083         * 差分処理とは、masterKeys で指定されたキーでは、マスタ、スレーブともに存在し
1084         * かつ、このキー(diffKeys)で指定されたキーの値が異なるマスタレコードを
1085         * 抜き出します。
1086         * つまり、主キーは存在し、属性が異なる情報のピックアップになりますので、
1087         * データ更新(UPDATE)対象を見つける場合に使用できます。
1088         * アクションが差分処理(DIFFERENCE)の場合は、必須属性になります。
1089         *
1090         * @param       diffKeys        マスタテーブルの差分カラム名をCSV形式で指定
1091         * @see         #setMasterKeys( String )
1092         */
1093        public void setDiffKeys( final String diffKeys ) {
1094                this.diffKeys = nval( getRequestParameter( diffKeys ),this.diffKeys );
1095        }
1096
1097        /**
1098         * 【TAG】スレーブテーブルのtableIdを指定します
1099         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1100         *
1101         * @og.tag
1102         * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。
1103         * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。
1104         * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
1105         *
1106         * @param       slaveTableId    スレーブテーブルのtableId
1107         * @see         org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY
1108         */
1109        public void setSlaveTableId( final String slaveTableId ) {
1110                this.slaveTableId = nval( getRequestParameter( slaveTableId ),this.slaveTableId );
1111        }
1112
1113        /**
1114         * 【TAG】スレーブテーブルのscopeを指定します(初期値:session)。
1115         *
1116         * @og.tag
1117         * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のスコープを指定します。
1118         * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。
1119         * 初期値は、session です。
1120         *
1121         * @param       slaveScope      スレーブテーブルのscope
1122         */
1123        public void setSlaveScope( final String slaveScope ) {
1124                this.slaveScope = nval( getRequestParameter( slaveScope ),this.slaveScope );
1125        }
1126
1127        /**
1128         * 【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します。
1129         *
1130         * @og.tag
1131         * アクションが列合成(UNION_CLM)または列追加(ADD_CLM)の場合に使用されます。
1132         * 列合成(UNION_CLM)は、マスタとスレーブの主キーに対して、ここで指定のスレーブの
1133         * カラム列名を、マスタの列に追加します。主キーがマッチしない行に関しては、
1134         * カラムの初期値が適用されたデータを作成します。
1135         * 列追加(ADD_CLM)は、マスタテーブルに指定のカラムを追加するだけです、スレーブテーブルは
1136         * 参照しません。よって、主キーも指定不要です。
1137         *
1138         * @param       unionClms       列合成するカラム名をCSV形式で指定
1139         */
1140        public void setUnionClms( final String unionClms ) {
1141                this.unionClms = nval( getRequestParameter( unionClms ),this.unionClms );
1142        }
1143
1144        /**
1145         * 【TAG】スレーブからマスタへ値を更新するカラム名をCSV形式で指定します。
1146         *
1147         * @og.tag
1148         * アクションが差分処理(DIFFERENCE)の場合に、結果にマスタテーブルが抜き出されますが、
1149         * 更新する場合に、スレーブ特有のユニークキー(例:UNIQ)を用いて更新する場合、
1150         * 指定のカラム値は、スレーブの値にセットしておきたい場合があります。
1151         * ここでは、指定のカラムについて、値だけスレーブからマスタへセットします。
1152         * なお、値の更新については、マスタとスレーブが同一キーという制約があります。
1153         *
1154         * @param       modifyClms      値を更新するカラム名をCSV形式で指定
1155         */
1156        public void setModifyClms( final String modifyClms ) {
1157                this.modifyClms = nval( getRequestParameter( modifyClms ),this.modifyClms );
1158        }
1159
1160        /**
1161         * 【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します。
1162         *
1163         * @og.tag
1164         * masterKeysで集合処理するときに、通常、最初に見つかった行データのみ残りますが、
1165         * ここに指定したカラムについては、発生都度、自分自身の情報に、CSV形式で連結して
1166         * いきます。
1167         * この操作により、本来削除された情報が、1行のCSV形式で取得できる効果が得られます。
1168         * これは、value タグの action="APPEND" を、DBTableModel に対して実施するような感じです。
1169         *
1170         * @og.rev 5.1.4.0 (2010/03/01) 新規追加
1171         *
1172         * @param       groupAddClms    相違データをCSV連結して残すカラム名をCSV形式で指定
1173         */
1174        public void setGroupAddClms( final String groupAddClms ) {
1175                this.groupAddClms = nval( getRequestParameter( groupAddClms ),this.groupAddClms );
1176        }
1177
1178        /**
1179         * 【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある)。
1180         *
1181         * @og.tag
1182         * すべての処理で、DBTableModel に対して、ユニーク化やグループ化などの集合処理を
1183         * 行う過程で、マスタテーブルに対して直接処理を行うと、副作用が発生します。
1184         * 同様に、スレーブテーブルにおいても、一旦キー列でグループ化されるため、副作用が
1185         * 発生します。これは、無駄なメモリ領域の確保と、テーブル(マスタ、スレーブとも)の
1186         * コピー処理時間の節約になります。初期値の設定も副作用がある状態になっています。
1187         * ところが、都合によっては、色々な action を連続して行いたい場合など、毎回、
1188         * データベースを検索するよりもメモリ上でコピーしたほうが都合がよいケースでは、
1189         * 副作用が出ないように、noSideEffect="true" に設定します。
1190         * ただし、マスタ、スレーブともテーブルをコピーを行い、結果のテーブルも派生する為、
1191         * 通常、2つの領域(マスタと結果は同じテーブルに書かれる)で良い所を、5つの領域が
1192         * 作成されます。
1193         * 初期値は、副作用がある(noSideEffect="false")です。
1194         *
1195         * @param       noSideEffect    テーブルモデルに対する副作用 [true:ない/false:ある]
1196         */
1197        public void setNoSideEffect( final String noSideEffect ) {
1198                this.noSideEffect = nval( getRequestParameter( noSideEffect ),this.noSideEffect );
1199        }
1200
1201        /**
1202         * 【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true)。
1203         *
1204         * @og.tag
1205         * アクションが差分処理(DIFFERENCE)の場合に、結果にマスタテーブルが抜き出されますが、
1206         * 差分対象のスレーブデータと比較したい場合があります。
1207         * このフラグを true にセットすると、書込み禁止属性が付いた状態で、スレーブデータが
1208         * 結果テーブルに追加されます。
1209         * なお、この処理では、通常と異なり、マスタテーブルにはグループ化の副作用は発生しますが、
1210         * 結果テーブルは新規に作成され、先頭行に必ず WRITABLE カラムが付加されます。
1211         * 初期値は、true:追加する です。
1212         *
1213         * @param       flag    スレーブデータを結果テーブルに追加するかどうか(初期値:true)
1214         */
1215        public void setUseDiffData( final String flag ) {
1216                useDiffData = nval( getRequestParameter( flag ),useDiffData );
1217        }
1218
1219        /**
1220         * 【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうかを指定します(初期値:false)。
1221         *
1222         * @og.tag
1223         * 処理対象のマスタテーブルについて、選択行が指定された場合に、選択行のみを処理対象に
1224         * するか、全件を対象にするかを指定します。
1225         * 積集合や差分集合など通常は、全件を対象にすることになりますが、列合成や列追加など、
1226         * マスタテーブルに対してのみ作用を及ぼす処理では、選択行のみを対象に処理を行う事が
1227         * 考えられます。その場合、初期グループ化と同じで、対象とする行が選択行のみになります。
1228         * 初期値は、false:全件対象 です。
1229         *
1230         * @param       flag    マスタテーブルの選択行のデータのみを対象に処理を行うかどうか(初期値:false)
1231         */
1232        public void setUseCheckOnly( final String flag ) {
1233                useCheckOnly = nval( getRequestParameter( flag ),useCheckOnly );
1234        }
1235
1236        /**
1237         * 【TAG】マージの結果を表示するかどうかを指定します(初期値:true)。
1238         *
1239         * @og.tag
1240         * true で、マージ結果を表示します。 false では、何も表示しません(初期値:true)
1241         * マスタテーブルの件数は、通常、キーでグループ化されるため、入力件数と異なります。
1242         * 同様に、スレーブ件数も異なります。結果の件数は、処理結果が現実的かどうかの
1243         * 判断に使用されます。
1244         * 初期値は、true:表示する です。
1245         *
1246         * @param       flag    接続の結果を表示するかどうか(初期値:false)
1247         */
1248        public void setDisplay( final String flag ) {
1249                display = nval( getRequestParameter( flag ),display );
1250        }
1251
1252        /**
1253         * 【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])。
1254         *
1255         * @og.tag
1256         * 初期値は、false(続行する)です。
1257         *
1258         * @og.rev 5.7.6.2 (2014/05/16) 新規追加
1259         *
1260         * @param  cmd 検索結果が0件のとき、[true:処理を中止する/false:続行する]
1261         */
1262        public void setStopZero( final String cmd ) {
1263                stopZero = nval( getRequestParameter( cmd ),stopZero );
1264        }
1265
1266        /**
1267         * 【TAG】(通常使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false)。
1268         *
1269         * @og.tag
1270         * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが
1271         * ファイルダウンロードの対象の表になります。
1272         *
1273         * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。
1274         * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい
1275         * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から
1276         * 除外することができます。
1277         *
1278         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
1279         *
1280         * @param  flag メイントランザクションかどうか
1281         */
1282        public void setMainTrans( final String flag ) {
1283                isMainTrans = nval( getRequestParameter( flag ),isMainTrans );
1284        }
1285
1286        /**
1287         * 【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。
1288         *
1289         * @og.tag
1290         * groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。
1291         * 初期値は、"," に設定されています。
1292         *
1293         * @og.rev 5.3.1.0 (2011/01/01)
1294         *
1295         * @param   sepa        項目区切り文字(初期値:",")
1296         */
1297        public void setSeparator( final String sepa ) {
1298                separator = nval( getRequestParameter( sepa ),separator );
1299        }
1300
1301        /**
1302         * このオブジェクトの文字列表現を返します。
1303         * 基本的にデバッグ目的に使用します。
1304         *
1305         * @return このクラスの文字列表現
1306         */
1307        @Override
1308        public String toString() {
1309                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
1310                                .println( "VERSION"                     ,VERSION                )
1311                                .println( "action"                      ,action                 )
1312                                .println( "tableId"                     ,tableId                )
1313                                .println( "scope"                       ,scope                  )
1314                                .println( "masterTableId"       ,masterTableId  )
1315                                .println( "masterScope"         ,masterScope    )
1316                                .println( "slaveTableId"        ,slaveTableId   )
1317                                .println( "slaveScope"          ,slaveScope             )
1318                                .println( "masterKeys"          ,masterKeys             )
1319                                .println( "diffKeys"            ,diffKeys               )
1320                                .println( "unionClms"           ,unionClms              )
1321                                .println( "modifyClms"          ,modifyClms             )
1322                                .println( "noSideEffect"        ,noSideEffect   )
1323                                .println( "useDiffData"         ,useDiffData    )
1324                                .println( "useCheckOnly"        ,useCheckOnly   )
1325                                .println( "display"                     ,display                )
1326                                .println( "ACTION_LIST"         ,ACTION_LIST    )
1327                                .println( "Other..."    ,getAttributes().getAttribute() )
1328                                .fixForm().toString() ;
1329        }
1330}