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.DBConstValue;
021import org.opengion.fukurou.util.StringUtil;
022import org.opengion.fukurou.util.ToString;                                                                              // 6.1.1.0 (2015/01/17)
023
024import static org.opengion.fukurou.util.StringUtil.nval;
025
026import java.util.Map;
027import java.util.LinkedHashMap;
028import java.util.Locale;                                                                                                                // 6.4.1.2 (2016/01/22)
029
030/**
031 * TableUpdateTag にパラメーターを渡す為のタグクラスです。
032 *
033 * 汎用的なデータベース登録処理を行えるタグ tableUpdate タグを新規作成します。
034 * これは、具体的なSLQを作成する tableUpdateParam タグと組み合わせて使用できます。
035 * tableUpdate タグは、queryType に JDBCTableUpdate を指定します。基本的にこれだけ
036 * です。tableUpdateParam では、sqlType に、INSERT,COPY,UPDATE,MODIFY,DELETE の
037 * どれかを指定する事で、SQL文のタイプを指定します。COPY,MODIFY は command と
038 * 関連を持たす為に追加しているタイプで、UPDATE,INSERT と同じ処理を行います。
039 * tableUpdateParam の table には、作成したい SQL のテーブルを指定します。
040 * where 属性は、検索結果の DBTableModel の更新時に使用する条件を指定します。
041 *
042 * @og.formSample
043 * ●形式:<og:tableUpdate command="{@command}" queryType="JDBCTableUpdate" >
044 *            <og:tableUpdateParam
045 *                sqlType       = "{@sqlType}"       // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
046 *                table         = "{@TABLE_NAME}"    // 処理対象のテーブル名
047 *                names         = "{@names}"         // 処理対象のカラム名
048 *                omitNames     = "{@omitNames}"     // 処理対象外のカラム名
049 *                where         = "{@where}"         // 処理対象を特定するキー
050 *                whereNames    = "{@whereNames}"    // 処理対象を特定するキー条件(where句)をCSV形式
051 *                constKeys     = "{@constKeys}"     // 処理カラム名の中の固定情報カラム名
052 *                constVals     = "{@constVals}"     // 処理カラム名の中の固定情報設定値
053 *                asNames       = "{@asNames}"       // 別名を付けたカラム名(select A as B from TBL の B を指定)
054 *                orgNames      = "{@orgNames}"      // tableの実際のカラム名(select A as B from TBL の A を指定)
055 *                funcKeys      = "{@funcKeys}"      // 関数等を設定するカラム名
056 *                funcVals      = "{@funcVals}"      // 関数等の設定値
057 *                logicalDelete = "{@logicalDelete}" // sqlTypeがDELETEの場合にもUPDATE文を発行
058 *            />
059 *         </og:tableUpdate>
060 *
061 * ●body:なし
062 *
063 * ●Tag定義:
064 *   <og:tableUpdateParam
065 *       sqlType          ○【TAG】BODY部に書かれている SQLタイプを指定します(INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE)(必須)
066 *       table            ○【TAG】処理対象のテーブル名を指定します(必須)
067 *       names              【TAG】処理対象のカラム名をCSV形式で複数指定します
068 *       omitNames          【TAG】処理対象外のカラム名をCSV形式で複数指定します
069 *       where              【TAG】処理対象を特定するキー条件(where句)を指定します
070 *       whereNames         【TAG】処理対象を特定するキー条件(where句)をCSV形式で複数指定します
071 *       insertOnly         【TAG】true に設定すると、sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない(初期値:false) 7.4.1.0 (2021/04/23)
072 *       constKeys          【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します
073 *       constVals          【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します
074 *       funcKeys           【TAG】関数等を設定するカラム名をCSV形式で複数指定します
075 *       funcVals           【TAG】関数等の設定値をCSV形式で複数指定します
076 *       asNames            【TAG】別名を付けたカラム名(select A as B from TBL の B を指定)をCSV形式で複数指定します
077 *       orgNames           【TAG】tableの実際のカラム名(select A as B from TBL の A を指定)をCSV形式で複数指定します
078 *       quotCheck          【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_SQL_INJECTION_CHECK[=true])
079 *       constObjKey        【TAG】固定情報カラムの処理オブジェクトを特定するキーを設定します(初期値:SYSTEM_ID)
080 *       logicalDelete      【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false)
081 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
082 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
083 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
084 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
085 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
086 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
087 *   />
088 *
089 * ●使用例
090 *    ・【entry.jsp】
091 *         <og:tableUpdate command="{@command}" queryType="JDBCTableUpdate" >
092 *            <og:tableUpdateParam
093 *               sqlType  = "{@sqlType}"
094 *               table    = "{@MEM.TABLE_NAME}"
095 *               where    = "ROWID = [ROWID]"
096 *            />
097 *         </og:tableUpdate>
098 *
099 * @og.rev 3.8.8.0 (2007/12/22) 新規作成
100 * @og.rev 4.1.2.0 (2008/03/12) 実装の大幅な修正
101 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
102 * @og.group DB登録
103 *
104 * @version  4.0
105 * @author   Kazuhiko Hasegawa
106 * @since    JDK5.0,
107 */
108public class TableUpdateParamTag extends CommonTagSupport {
109        /** このプログラムのVERSION文字列を設定します。   {@value} */
110        private static final String VERSION = "8.3.0.3 (2022/08/26)" ;
111        private static final long serialVersionUID = 830320220826L ;
112
113        /** sqlType属性に設定できる値                    {@value} */
114//      public static final String SQL_TYPE  = "|INSERT|COPY|UPDATE|MODIFY|DELETE|" ;
115        public static final String SQL_TYPE  = "|INSERT|COPY|UPDATE|MODIFY|DELETE|MERGE|" ;             // 7.2.9.1 (2020/10/23)
116
117        // 3.8.0.4 (2005/08/08) 印刷時に使用するシステムID
118        private static final String SYSTEM_ID =HybsSystem.sys( "SYSTEM_ID" );
119
120        // 4.3.6.0 (2009/05/01) デフォルトで利用するconstObjのシステムリソース名
121        private static final String DEFAULT_CONST_OBJ = HybsSystem.sys( "DEFAULT_CONST_CLASS" );
122
123        private String          sqlType         ;                                                                                       // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
124        private String          table           ;                                                                                       // 処理対象のテーブル名
125        private String[]        names           ;                                                                                       // 処理対象のカラム名
126        private String          omitNames       = ",ROWID,ROWNUM,WRITABLE,";                            // 処理対象外のカラム名
127        private String          where           ;                                                                                       // 処理対象を特定するキー
128        private String          whereNames      ;                                                                                       // 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名
129        private boolean         insertOnly;                                                                                             // 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
130        private String[]        constKeys       ;                                                                                       // 処理カラム名の中の固定情報カラム名
131        private String[]        constVals       ;                                                                                       // 処理カラム名の中の固定情報設定値
132        private String[]        funcKeys        ;                                                                                       // 5.5.1.9 (2012/04/19) 関数等を設定するカラム名
133        private String[]        funcVals        ;                                                                                       // 5.5.1.9 (2012/04/19) 関数等の設定値
134        private String[]        asNames         ;                                                                                       // 5.5.1.9 (2012/04/19) 別名を付けたカラム名(select A as B from TBL の B を指定)
135        private String[]        orgNames        ;                                                                                       // 5.5.1.9 (2012/04/19) tableの実際のカラム名(select A as B from TBL の A を指定)
136        private String          constObjKey     = SYSTEM_ID;                                                            // 固定情報カラムの処理オブジェクトを特定するキー
137        private boolean         quotCheck       = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );
138        private boolean         logicalDelete;                                                                                  // 4.3.7.0 (2009/06/01) sqlTypeがDELETEの場合にもUPDATE文を発行
139
140        /**
141         * デフォルトコンストラクター
142         *
143         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
144         */
145        public TableUpdateParamTag() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
146
147        /**
148         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
149         *
150         * @og.rev 5.5.1.9 (2012/04/19) エラーチェックを先に行います。
151         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
152         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
153         *
154         * @return      後続処理の指示( SKIP_BODY )
155         */
156        @Override
157        public int doStartTag() {
158                if( !useTag() ) { return SKIP_BODY ; }                                                                  // 6.3.4.0 (2015/08/01)
159
160                // constKeys,constVals の個数チェック
161                if( constKeys != null ) {
162                        if( constVals == null || constKeys.length != constVals.length ) {
163//                              final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br />"
164                                final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br>"             // 7.0.1.0 (2018/10/15)
165                                                                        + " constKeys=[" + StringUtil.array2csv( constKeys ) + "]"
166                                                                        + " constVals=[" + StringUtil.array2csv( constVals ) + "]" ;
167                                throw new HybsSystemException( errMsg );
168                        }
169                }
170
171                // funcKeys,funcVals の個数チェック
172                if( funcKeys != null ) {
173                        if( funcVals == null || funcKeys.length != funcVals.length ) {
174//                              final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br />"
175                                final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br>"               // 7.0.1.0 (2018/10/15)
176                                                                        + " funcKeys=[" + StringUtil.array2csv( funcKeys ) + "]"
177                                                                        + " funcVals=[" + StringUtil.array2csv( funcVals ) + "]" ;
178                                throw new HybsSystemException( errMsg );
179                        }
180                }
181
182                // asNames,orgNames の個数チェック
183                if( orgNames != null ) {
184                        if( asNames == null || orgNames.length != asNames.length ) {
185//                              final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br />"
186                                final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br>"                // 7.0.1.0 (2018/10/15)
187                                                                        + " orgNames=[" + StringUtil.array2csv( orgNames ) + "]"
188                                                                        + " asNames=[" + StringUtil.array2csv( asNames ) + "]" ;
189                                throw new HybsSystemException( errMsg );
190                        }
191                }
192
193                return SKIP_BODY ;      // Body を評価しない
194        }
195
196        /**
197         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
198         *
199         * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応
200         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
201         * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
202         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
203         * @og.rev 8.3.0.3 (2022/08/26) sqlType="MERGE" の場合、UPDATE文 に追加関連固定カラムキーが追加される不具合対応
204         *
205         * @return      後続処理の指示
206         */
207        @Override
208        public int doEndTag() {
209                debugPrint();
210//              if( !useTag() ) { return EVAL_PAGE ; }                                                                  // 6.3.4.0 (2015/08/01)
211                if( !useTag() || sqlType == null ) { return EVAL_PAGE ; }                               // 7.2.9.1 (2020/10/23) sqlType == null の時は、何もしない。
212
213                final TableUpdateTag updateTag = (TableUpdateTag)findAncestorWithClass( this,TableUpdateTag.class );
214                if( updateTag == null ) {
215                        final String errMsg = "<b>" + getTagName() + "タグは、TableUpdateTagの内側(要素)に記述してください。</b>";
216                        throw new HybsSystemException( errMsg );
217                }
218
219                final String upSqlType = updateTag.getSqlType() ;
220
221                // 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
222                final boolean useInsert = "INSERT,COPY,MERGE".contains( sqlType ) ;
223                final boolean useUpdate = "UPDATE,MODIFY,MERGE".contains( sqlType ) || "DELETE".contains( sqlType ) && logicalDelete ;
224                final boolean useDelete = "DELETE".equals( sqlType ) && !logicalDelete ;
225                final boolean useMerge  = "MERGE".contains( sqlType ) ;                                 // 8.3.0.3 (2022/08/26) Add
226
227                if( upSqlType == null || upSqlType.equals( sqlType ) ) {
228                        // 通常の names カラム配列を設定します。
229                        if( names == null ) { names = updateTag.getNames(); }
230                        final NamesData namesData = makeNamesData( names );
231
232                        // 8.3.0.3 (2022/08/26) Modify
233                        if( useInsert ) {
234//                              updateTag.setQuery( "INSERT",getInsertSQL( namesData ) );
235                                final NamesData namesData1 = useMerge ? namesData.newInstance() : namesData;
236                                updateTag.setQuery( "INSERT",getInsertSQL( namesData1 ) );
237                        }
238                        if( useUpdate ) {
239                                if( insertOnly ) {                                                                                              // 7.4.1.0 (2021/04/23)
240                                        updateTag.setQuery( "SELECT",getSelectSQL() );
241                                }
242                                // 8.3.0.3 (2022/08/26) Modify
243                                else {
244//                                      updateTag.setQuery( "UPDATE",getUpdateSQL( namesData ) );
245                                        final NamesData namesData2 = useMerge ? namesData.newInstance() : namesData;
246                                        updateTag.setQuery( "UPDATE",getUpdateSQL( namesData2 ) );
247                                }
248                        }
249                        if( useDelete ) {
250                                updateTag.setQuery( "DELETE",getDeleteSQL() );
251                        }
252                }
253
254//              // 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
255//              if( upSqlType == null || upSqlType.equals( sqlType ) ||  ) {
256//                      String query = null;
257//                      if( "INSERT".equalsIgnoreCase( sqlType ) || "COPY".equalsIgnoreCase( sqlType ) ) {
258//                              query = getInsertSQL( namesData );
259//                      }
260//                      else if( "UPDATE".equalsIgnoreCase( sqlType ) || "MODIFY".equalsIgnoreCase( sqlType )
261//                                      || ( "DELETE".equalsIgnoreCase( sqlType ) && logicalDelete ) ) { // 4.3.7.0 (2009/06/01)
262//                              query = getUpdateSQL( namesData );
263//                      }
264//                      else if( "DELETE".equalsIgnoreCase( sqlType ) ) {
265//                              query = getDeleteSQL();
266//                      }
267//
268//                      jspPrint( query );
269//              }
270
271                return EVAL_PAGE ;
272        }
273
274        /**
275         * タグリブオブジェクトをリリースします。
276         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
277         *
278         * @og.rev 4.3.7.0 (2009/06/01) logicalDelete属性追加
279         * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加
280         * @og.rev 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名
281         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
282         */
283        @Override
284        protected void release2() {
285                super.release2();                                                                                                               // 3.5.6.0 (2004/06/18) 追加(抜けていた)
286                sqlType                 = null;                                                                                                 // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
287                table                   = null;                                                                                                 // 処理対象のテーブル名
288                names                   = null;                                                                                                 // 処理対象のカラム名
289                omitNames               = ",ROWID,ROWNUM,WRITABLE,";                                                    // 処理対象外のカラム名
290                where                   = null;                                                                                                 // 処理対象を特定するキー
291                whereNames              = null;                                                                                                 // 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名
292                insertOnly              = false;                                                                                                // 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
293                constKeys               = null;                                                                                                 // 処理カラム名の中の固定情報カラム名
294                constVals               = null;                                                                                                 // 処理カラム名の中の固定情報設定値
295                quotCheck               = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );
296                constObjKey             = SYSTEM_ID;                                                                                    // 固定情報カラムの処理オブジェクトを特定するキー
297                logicalDelete   = false;                                                                                                // 4.3.7.0 (2009/06/01)
298                funcKeys                = null;                                                                                                 // 5.5.1.9 (2012/04/19) 関数等を設定するカラム名
299                funcVals                = null;                                                                                                 // 5.5.1.9 (2012/04/19) 関数等の設定値
300                asNames                 = null;                                                                                                 // 5.5.1.9 (2012/04/19) 別名を付けたカラム名(select A as B from TBL の B を指定)
301                orgNames                = null;                                                                                                 // 5.5.1.9 (2012/04/19) tableの実際のカラム名(select A as B from TBL の A を指定)
302        }
303
304        /**
305         * 【TAG】BODY部に書かれている SQLタイプを指定します。
306         *
307         * @og.tag
308         * SQLタイプは、INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE の中から指定する
309         * 必要があります。これらは、内部に書かれるSQLの形式を指定するのに使用します。
310         * 内部処理は、DBTableModelの改廃コード(A,C,D)に対して使用される
311         * SQL を選択する場合の情報に使用されます。
312         * なお、COPY と MODIFY は、command で指定できる簡易機能として用意しています。
313         * 上位の TableUpdateTag の sqlType 属性 と同じsqlType 属性の場合のみ、SQL文を
314         * 合成・出力します。(上位のsqlTypeがnullの場合は、無条件実行します。)
315         * 指定のタイプが、異なる場合は、なにも処理を行いません。
316         *
317         * @param       type    SQLタイプ [INSERT/COPY/UPDATE/MODIFY/DELETE/MERGE]
318         */
319        public void setSqlType( final String type ) {
320                sqlType = nval( getRequestParameter( type ),sqlType );
321                if( sqlType != null && SQL_TYPE.indexOf( "|" + sqlType + "|" ) < 0 ) {
322                        sqlType = null;
323                }
324        }
325
326        /**
327         * 【TAG】処理対象のテーブル名を指定します。
328         *
329         * @og.tag
330         * テーブル名を指定することで、sqlTypeに応じた QUERYを生成することが出来ます。
331         * 生成する場合のカラムを特定する場合は、names 属性で指定できます。
332         * また、WHERE条件は、where属性で指定します。
333         *
334         * @param       tbl     テーブル名
335         * @see         #setNames( String )
336         * @see         #setWhere( String )
337         * @see         #setSqlType( String )
338         */
339        public void setTable( final String tbl ) {
340                table = nval( getRequestParameter( tbl ),table );
341        }
342
343        /**
344         * 【TAG】処理対象のカラム名をCSV形式で複数指定します。
345         *
346         * @og.tag
347         * 生成するQUERYのカラム名をCSV形式(CSV)で複数指定します。
348         * 指定がない場合は、DBTableModel の全カラム(※)を使用して、QUERYを構築します。
349         * 一般に、テーブル結合してDBTableModelを構築した場合は、登録すべきカラムを
350         * 指定する必要があります。
351         * (※)正確には、DBTableModel の全カラムのうち、ROWID,ROWNUM,WRITABLE カラムは
352         * 無視します。
353         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
354         *
355         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
356         *
357         * @param       nms     カラム名 (CSV形式)
358         * @see         #setTable( String )
359         * @see         #setOmitNames( String )
360         */
361        public void setNames( final String nms ) {
362                names = StringUtil.csv2Array( getRequestParameter( nms ) );
363                if( names.length == 0 ) { names = null; }
364        }
365
366        /**
367         * 【TAG】処理対象外のカラム名をCSV形式で複数指定します。
368         *
369         * @og.tag
370         * 生成するQUERYのカラム名に指定しないカラム名をCSV形式(CSV)で複数指定します。
371         * 指定がない場合は、DBTableModel の全カラム(※)を使用して、QUERYを構築します。
372         * テーブル結合などで、処理したくないカラム数の方が少ない場合に、names ですべてを
373         * 指定するより少ない記述ですみます。
374         * (※)正確には、DBTableModel の全カラムのうち、ROWID,ROWNUM,WRITABLE カラムは
375         * 無視します。
376         *
377         * @param       nms     カラム名 (CSV形式)
378         * @see         #setTable( String )
379         * @see         #setNames( String )
380         */
381        public void setOmitNames( final String nms ) {
382                omitNames = omitNames + nval( getRequestParameter( nms ),"" ) + ",";
383        }
384
385        /**
386         * 【TAG】処理対象を特定するキー条件(where句)を指定します。
387         *
388         * @og.tag
389         * 生成するQUERYのwhere 句を指定します。通常の WHERE 句の書き方と同じで、
390         * DBTableModelの値を割り当てたい箇所に[カラム名] を記述します。
391         * 文字列の場合、設定値をセットするときに、シングルコーテーションを
392         * 使用しますが、[カラム名]で指定する場合は、その前後に、(')シングル
393         * コーテーションは、不要です。
394         * {&#064;XXXX}変数を使用する場合は、パース時に固定文字に置き換えられる為、
395         * 文字列指定時の(')シングルコーテーションが必要になります。
396         * ※ 5.5.8.5 (2012/11/27) whereNames 属性と併用した場合は、where が、and を付けて、文字列結合されます。
397         * 例:FGJ='1' and CLM=[CLM] and SYSTEM_ID in ([SYSID],'**') and KBSAKU='{&#064;KBSAKU}'
398         *
399         * @param       wr      検索条件 (where句)
400         */
401        public void setWhere( final String wr ) {
402                where = nval( getRequestParameter( wr ),where );
403        }
404
405        /**
406         * 【TAG】処理対象を特定するキー条件(where句)をCSV形式で複数指定します。
407         *
408         * @og.tag
409         * 生成するQUERYのwhere 句を指定する方法として、複数のカラム名をCSV指定し、内部で
410         * KEY=[KEY] 文字列を作成します。
411         * ここでは、カラム名は、データベースのカラム名と同じで、かつ、DBTableModel にも
412         * 同じカラムのデータが存在していること、という条件付きとします。
413         * また、where 条件との併用を行いますが、こちらの条件が先に使用され、where 条件は、
414         * and を付けて、文字列結合されます。
415         * 例: CLM,SYSTEM_ID,KBSAKU   ⇒   CLM=[CLM] and SYSTEM_ID=[SYSTEM_ID] and KBSAKU=[KBSAKU]
416         *
417         * @og.rev 5.5.8.5 (2012/11/27) 新規追加
418         *
419         * @param       wrnm    検索条件カラム (where句)作成のためのカラム名(CSV形式)
420         */
421        public void setWhereNames( final String wrnm ) {
422                whereNames = nval( getRequestParameter( wrnm ),whereNames );
423        }
424
425        /**
426         *【TAG】true に設定すると、sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない(初期値:false)。
427         *
428         * @og.tag
429         * true に設定すると、sqlType="MERGE" 時に、where条件で、検索して、存在しない場合は、追加します。
430         * 存在する場合は、何もしません。
431         * 何もしない(更新しない)ところが、通常のMERGEと異なる箇所です。
432         * 動作としては、UPDATEの代わりに、SELECT で判定します。
433         * 初期値は、false で、あれば更新、なければ追加処理を行います。
434         *
435         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
436         *
437         * @param       flag    "true" に設定すると、sqlType="MERGE" 時に、where条件で検索
438         */
439        public void setInsertOnly( final String flag ) {
440                insertOnly = nval( getRequestParameter( flag ),insertOnly );
441        }
442
443        /**
444         * 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します。
445         *
446         * @og.tag
447         * names 属性のカラムや table 属性より、QUERYを作成して、DBTableModelの値を
448         * 割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を
449         * 割り当てたい場合に、そのカラム名をCSV形式(CSV)で複数指定します。
450         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
451         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
452         *
453         * @param       keys    固定値カラム (CSV形式)
454         * @see         #setConstVals( String )
455         */
456        public void setConstKeys( final String keys ) {
457                constKeys = getCSVParameter( keys );
458        }
459
460        /**
461         * 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します。
462         *
463         * @og.tag
464         * names 属性のカラムや table 属性より、QUERYを作成して、DBTableModelの
465         * 値を割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を
466         * 割り当てたい場合に、そのカラム名に対応する設定値をCSV形式(CSV)で
467         * 複数指定します。ここで指定する設定値は、constKeys 属性と対応させます。
468         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
469         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
470         *
471         * @param       vals    設定値(CSV形式)
472         * @see         #setConstKeys( String )
473         */
474        public void setConstVals( final String vals ) {
475                constVals = getCSVParameter( vals );
476        }
477
478        /**
479         * 【TAG】関数等を設定するカラム名をCSV形式で複数指定します。
480         *
481         * @og.tag
482         * constVals 属性で設定する値は、必ずシングルクオートが付与されます。
483         * その場合、関数などを設定したい場合でも、文字列として設定しようとします。
484         * ここで指定するカラム名(funcKeys)自身は、constKeys と同じ書式です。
485         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
486         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
487         *
488         * @og.rev 5.5.1.9 (2012/04/19) 新規追加
489         *
490         * @param       keys    関数カラム (CSV形式)
491         * @see         #setFuncVals( String )
492         */
493        public void setFuncKeys( final String keys ) {
494                funcKeys = getCSVParameter( keys );
495        }
496
497        /**
498         * 【TAG】関数等の設定値をCSV形式で複数指定します。
499         *
500         * @og.tag
501         * funcKeys 属性に対応する 関数などの設定値を割り当てます。
502         * constVals 属性との違いは、funcVals の設定値は、そのままの形で、SQL文の
503         * 構築に使われます。
504         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
505         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
506         *
507         * @og.rev 5.5.1.9 (2012/04/19) 新規追加
508         *
509         * @param       vals    関数設定値 (CSV形式)
510         * @see         #setFuncKeys( String )
511         */
512        public void setFuncVals( final String vals ) {
513                funcVals = getCSVParameter( vals );
514        }
515
516        /**
517         * 【TAG】別名を付けたカラム名(select A as B from TBL の B を指定)をCSV形式で複数指定します。
518         *
519         * @og.tag
520         * SELECT 文を記述したとき、別名を付けていたり、SELECTしたテーブルと別のテーブルに
521         * DBTableModelの値を書き込む場合、DBTableModel の持っているカラム名と、実際に
522         * 書き込むカラム名が異なります。そのようなケースに、元の別名カラムを指定します。
523         * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります。
524         * このカラム名は、DBTableModel には持っているが、テーブル側には持っていない値
525         * なので、内部的に omitNames 属性に値を設定します。利用者は、omitNames に
526         * 書き込む必要はありません。
527         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
528         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
529         *
530         * @og.rev 5.5.1.9 (2012/04/19) 新規追加
531         *
532         * @param       keys    別名カラム (CSV形式)
533         * @see         #setOrgNames( String )
534         */
535        public void setAsNames( final String keys ) {
536                asNames = getCSVParameter( keys );
537        }
538
539        /**
540         * 【TAG】tableの実際のカラム名(select A as B from TBL の A を指定)をCSV形式で複数指定します。
541         *
542         * @og.tag
543         * SELECT 文を記述したとき、別名を付けていたり、SELECTしたテーブルと別のテーブルに
544         * DBTableModelの値を書き込む場合、DBTableModel の持っているカラム名と、実際に
545         * 書き込むカラム名が異なります。そのようなケースに、テーブルの実カラムを指定します。
546         * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります。
547         * このカラム名は、DBTableModel には持っていませんが、テーブル側には持っている値
548         * なので、このカラム名で、SQL文を構築します。 UPDATE TBL SET A=[B] WHERE … となります。
549         * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
550         * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
551         *
552         * @og.rev 5.5.1.9 (2012/04/19) 新規追加
553         *
554         * @param       keys    実カラム (CSV形式)
555         * @see         #setAsNames( String )
556         */
557        public void setOrgNames( final String keys ) {
558                orgNames = getCSVParameter( keys );
559        }
560
561        /**
562         * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します
563         *              (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
564         *
565         * @og.tag
566         * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
567         * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
568         * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
569         * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
570         * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
571         * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
572         * (初期値:システム定数のUSE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
573         *
574         * @param       flag    クォートチェック [true:する/それ以外:しない]
575         * @see         org.opengion.hayabusa.common.SystemData#USE_SQL_INJECTION_CHECK
576         */
577        public void setQuotCheck( final String flag ) {
578                quotCheck = nval( getRequestParameter( flag ),quotCheck );
579        }
580
581        /**
582         * 【TAG】固定情報カラムの処理オブジェクトを特定するキーを設定します(初期値:SYSTEM_ID)。
583         *
584         * @og.tag
585         * 固定情報カラム をシステム単位にJavaクラスで管理できます。
586         * そのクラスオブジェクトは、org.opengion.hayabusa.db.DBConstValue インターフェースを
587         * 継承した、plugin クラスになります。
588         * そのクラスを特定するキーワードを指定します。
589         * 初期値は、SYSTEM_ID でシステム単位にクラスを作成します。
590         * もし、他のシステムと共通の場合は、継承だけさせることも可能です。
591         * 対応したDBConstValueクラスがプラグインとして存在しない場合は、
592         * システムリソースのDEFAULT_CONST_CLASSで指定されたクラスが利用されます。
593         * 固定情報カラムを使用しない場合は、constObjKey="" をセットしてください。
594         *
595         * 初期値は、SYSTEM_ID です。
596         *
597         * @og.rev 6.9.8.0 (2018/05/28) 固定情報カラムを使用しない場合は、constObjKey="" をセット。
598         *
599         * @param       key     固定カラムキー
600         */
601        public void setConstObjKey( final String key ) {
602//              constObjKey = nval( getRequestParameter( key ),constObjKey );
603                final String objKey = getRequestParameter( key );
604                if( objKey != null ) { constObjKey = objKey; }
605        }
606
607        /**
608         * 【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false)。
609         *
610         * @og.tag
611         * sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します。
612         * trueが指定された場合は、DELETE文ではなく、UPDATE文が発行されます。
613         * falseが指定された場合は、DELETE文が発行されます。
614         * さらに論理削除を行う場合、org.opengion.hayabusa.db.DBConstValue インターフェースに
615         * 定義されている、getLogicalDeleteKeys()及びgetLogicalDeleteValsを実装することで、
616         * 論理削除する際のフラグの更新方法を統一的に管理することが可能になります。
617         * 初期値は、false(物理削除する)です
618         *
619         * @param       flag    論理削除可否 [true:UPDATE文/false:DELETE文]
620         */
621        public void setLogicalDelete( final String flag ) {
622                logicalDelete = nval( getRequestParameter( flag ),logicalDelete );
623        }
624
625        /**
626         * データをインサートする場合に使用するSQL文を作成します。
627         *
628         * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値セットを採用
629         * @og.rev 4.3.6.4 (2009/05/01) デフォルト設定をシステムリソースで設定可能にする
630         * @og.rev 5.3.4.0 (2011/04/01) DEFAULT_CONST_OBJの初期値変更(null→ゼロ文字列)
631         * @og.rev 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。
632         *
633         * @param       namesData       NamesDataオブジェクト
634         *
635         * @return      インサートSQL
636         * @og.rtnNotNull
637         */
638        private String getInsertSQL( final NamesData namesData ) {
639                String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ;
640
641                // 4.3.6.4 (2009/05/01) 標準の追加
642                if( cls == null){
643                        cls = DEFAULT_CONST_OBJ;
644                }
645
646                if( cls != null && !cls.isEmpty() ) {
647                        final DBConstValue constVal = HybsSystem.newInstance( cls );
648                        // 4.2.1.0 (2008/04/16) 初期化追加
649                        constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) );
650                        final String[] keys = constVal.getInsertKeys();
651                        final String[] vals = constVal.getInsertVals();
652                        namesData.add( keys,vals );
653                }
654
655                final String[] nms = namesData.getNames();
656                final String[] vls = namesData.getVals();
657
658                // 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。
659                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
660                        .append( "INSERT INTO " ).append( table )
661                        .append( " ( " )
662                        .append( String.join( "," , nms ) )             // 6.2.3.0 (2015/05/01)
663                        .append( " ) VALUES ( " )
664                        .append( String.join( "," , vls ) )             // 6.2.3.0 (2015/05/01)
665                        .append( " )" );
666
667                return sql.toString();
668        }
669
670        /**
671         * データをアップデートする場合に使用するSQL文を作成します。
672         *
673         * where と whereNames が同時に指定された場合は、whereNames が先に処理され
674         * where 条件は、and 結合されます。
675         *
676         * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値セットを採用
677         * @og.rev 4.3.6.4 (2009/05/01) デフォルト設定をシステムリソースで設定可能にする
678         * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応
679         * @og.rev 5.3.7.0 (2011/07/01) DEFAULT_CONST_OBJの初期値変更(null→ゼロ文字列) 対応忘れ
680         * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応
681         *
682         * @param       namesData       NamesDataオブジェクト
683         *
684         * @return      アップデートSQL
685         * @og.rtnNotNull
686         */
687        private String getUpdateSQL( final NamesData namesData ) {
688                String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ;
689
690                // 4.3.6.4 (2009/05/01) 標準の追加
691                if( cls == null){
692                        cls = DEFAULT_CONST_OBJ;
693                }
694
695                if( cls != null && !cls.isEmpty() ) {           // 5.3.7.0 (2011/07/01)
696                        final DBConstValue constVal = HybsSystem.newInstance( cls );
697                        // 4.2.1.0 (2008/04/16) 初期化追加
698                        constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) );
699                        // 4.3.7.0 (2009/06/01) 論理削除対応
700                        String[] keys = null;
701                        String[] vals = null;
702                        if( "DELETE".equalsIgnoreCase( sqlType ) ) {
703                                keys = constVal.getLogicalDeleteKeys();
704                                vals = constVal.getLogicalDeleteVals();
705                        }
706                        else {
707                                keys = constVal.getUpdateKeys();
708                                vals = constVal.getUpdateVals();
709                        }
710                        namesData.add( keys,vals );
711                }
712
713                final String[] nms = namesData.getNames();
714                final String[] vls = namesData.getVals();
715
716                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
717                        .append( "UPDATE " ).append( table ).append( " SET " )
718                        .append( nms[0] ).append( '=' ).append( vls[0] );                                       // 6.0.2.5 (2014/10/31) char を append する。
719
720                for( int i=1; i<nms.length; i++ ) {
721                        sql.append( ',' ).append( nms[i] ).append( '=' ).append( vls[i] );      // 6.0.2.5 (2014/10/31) char を append する。
722                }
723
724                // 5.5.8.5 (2012/11/27) whereNames 対応
725                String whereAnd = " WHERE " ;
726                if( whereNames != null && whereNames.length() > 0 ) {
727                        final String[] wnms = whereNames.split(",");
728                        sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );        // 6.0.2.5 (2014/10/31) char を append する。
729
730                        for( int i=1; i<wnms.length; i++ ) {
731                                sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。
732                        }
733                        whereAnd = " AND " ;            // whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
734                }
735
736                // 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
737                if( where != null && where.length() > 0 ) {
738                        sql.append( whereAnd ).append( where );
739                }
740
741                return sql.toString();
742        }
743
744        /**
745         * データをデリートする場合に使用するSQL文を作成します。
746         *
747         * where と whereNames が同時に指定された場合は、whereNames が先に処理され
748         * where 条件は、and 結合されます。
749         *
750         * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応
751         *
752         * @return      デリートSQL
753         * @og.rtnNotNull
754         */
755        private String getDeleteSQL() {
756                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE );
757                sql.append( "DELETE FROM " ).append( table );
758
759                // 5.5.8.5 (2012/11/27) whereNames 対応
760                String whereAnd = " WHERE " ;
761                if( whereNames != null && whereNames.length() > 0 ) {
762                        final String[] wnms = whereNames.split(",");
763                        sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );        // 6.0.2.5 (2014/10/31) char を append する。
764
765                        for( int i=1; i<wnms.length; i++ ) {
766                                sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。
767                        }
768                        whereAnd = " AND " ;            // whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
769                }
770
771                // 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
772                if( where != null && where.length() > 0 ) {
773                        sql.append( whereAnd ).append( where );
774                }
775                return sql.toString();
776        }
777
778        /**
779         * データを検索する場合に使用するSQL文を作成します。
780         *
781         * where と whereNames が同時に指定された場合は、whereNames が先に処理され
782         * where 条件は、and 結合されます。
783         *
784         * これは、sqlType="MERGE" 時に、insertOnly="true" が指定された時に、呼ばれます。
785         *
786         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
787         *
788         * @return      検索SQL
789         * @og.rtnNotNull
790         */
791        private String getSelectSQL() {
792                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE );
793                sql.append( "SELECT count(*) FROM " ).append( table );
794
795                // 5.5.8.5 (2012/11/27) whereNames 対応
796                String whereAnd = " WHERE " ;
797                if( whereNames != null && whereNames.length() > 0 ) {
798                        final String[] wnms = whereNames.split(",");
799                        sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );        // 6.0.2.5 (2014/10/31) char を append する。
800
801                        for( int i=1; i<wnms.length; i++ ) {
802                                sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。
803                        }
804                        whereAnd = " AND " ;            // whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
805                }
806
807                // 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
808                if( where != null && where.length() > 0 ) {
809                        sql.append( whereAnd ).append( where );
810                }
811                return sql.toString();
812        }
813
814        /**
815         * names,constKeys,omitNames から、必要なキー情報と、属性情報を持った NamesData を作成します。
816         *
817         * @og.rev 4.1.2.1 (2008/03/17) 固定値の constVals の前後に、"'" を入れる。
818         * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加
819         *
820         * @param       nms     カラム名配列(可変長引数)
821         *
822         * @return      属性情報を持ったNamesData
823         */
824        private NamesData makeNamesData( final String... nms ) {
825                final NamesData namesData = new NamesData();
826
827                // 5.5.1.9 (2012/04/19) omitNames に、asNames配列の値を設定しておきます。
828                if( asNames != null ) {
829                        for( int i=0; i<asNames.length; i++ ) {
830                                if( asNames[i] != null && asNames[i].length() > 0 ) {
831                                        omitNames = omitNames + asNames[i] + ",";
832                                }
833                        }
834                }
835
836                // names で指定されたカラム名
837                for( int i=0; i<nms.length; i++ ) {
838                        final String nm = nms[i];
839                        if( nm != null && nm.length() > 0 && omitNames.indexOf( "," + nm + "," ) < 0 ) {
840                                namesData.add( nm,"[" + nm + "]" ) ;
841                        }
842                }
843
844                // 固定値の constKeys カラム配列を設定します。
845                if( constKeys != null && constKeys.length > 0 ) {
846                        for( int j=0; j<constKeys.length; j++ ) {
847                                final String nm = constKeys[j];
848                                if( nm != null && nm.length() > 0 ) {
849                                        namesData.add( nm,"'" + constVals[j] + "'" ) ;  // constVals は、シングルクオートで囲います。
850                                }
851                        }
852                }
853
854                // 関数値の funcKeys カラム配列を設定します。
855                if( funcKeys != null && funcKeys.length > 0 ) {
856                        for( int j=0; j<funcKeys.length; j++ ) {
857                                final String nm = funcKeys[j];
858                                if( nm != null && nm.length() > 0 ) {
859                                        namesData.add( nm, funcVals[j] ) ;              // funcVals は、シングルクオートで囲いません。
860                                }
861                        }
862                }
863
864                // 別名の asNames,orgNames カラム配列を設定します。
865                if( orgNames != null && orgNames.length > 0 ) {
866                        for( int j=0; j<orgNames.length; j++ ) {
867                                final String onm = orgNames[j];
868                                if( onm != null && onm.length() > 0 ) {
869                                        namesData.add( onm,"[" + asNames[j] + "]" ) ;
870                                }
871                        }
872                }
873
874                return namesData ;
875        }
876
877        /**
878         * 内部データを受け渡す為の、簡易クラスです。
879         * 更新するカラム名と値のセット配列を管理しています。
880         *
881         * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
882         *    ※ classのfinal化と変数のprivate化、メソッドの修飾子なし(パッケージプライベート)化を行います。
883         * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します。
884         */
885        private static final class NamesData {
886                private final Map<String,String> nameMap = new LinkedHashMap<>() ;
887
888                /**
889                 * 内部のMapをコピーした新しいインスタンスを返します。
890                 * 
891                 * @og.rev 8.3.0.3 (2022/08/26) sqlType="MERGE" の場合、UPDATE文 に追加関連固定カラムキーが追加される不具合対応
892                 *
893                 * @return      新しいインスタンス
894                 */
895                /* default */ NamesData newInstance () {
896                        final NamesData newNames = new NamesData();
897                        newNames.nameMap.putAll( nameMap );
898                        return newNames;
899                }
900
901                /**
902                 * キーと値のセットを追加します。
903                 *
904                 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
905                 * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します。
906                 *
907                 * @param       nm      キー(大文字のみ。内部で変換しておきます。)
908                 * @param       val     値
909                 */
910                /* default */ void add( final String nm,final String val ) {
911                        nameMap.put( nm.toUpperCase(Locale.JAPAN),val );
912                }
913
914                /**
915                 * キー配列と対応する、値配列のセットを追加します。
916                 *
917                 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
918                 *
919                 * @param       nms             キー配列
920                 * @param       vals    値配列
921                 */
922                /* default */ void add( final String[] nms,final String[] vals ) {
923                        if( nms != null ) {
924                                for( int i=0; i<nms.length; i++ ) {
925                                        nameMap.put( nms[i].toUpperCase(Locale.JAPAN),vals[i] );
926                                }
927                        }
928                }
929
930                /**
931                 * キー配列を返します。
932                 *
933                 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
934                 *
935                 * @return      キー配列
936                 * @og.rtnNotNull
937                 */
938                /* default */ String[] getNames() {
939                        return nameMap.keySet().toArray( new String[nameMap.size()] );
940                }
941
942                /**
943                 * 値配列を返します。
944                 *
945                 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
946                 *
947                 * @return      値配列
948                 * @og.rtnNotNull
949                 */
950                /* default */ String[] getVals()  {
951                        return nameMap.values().toArray( new String[nameMap.size()] );
952                }
953        }
954
955        /**
956         * このオブジェクトの文字列表現を返します。
957         * 基本的にデバッグ目的に使用します。
958         *
959         * @return      このクラスの文字列表現
960         * @og.rtnNotNull
961         */
962        @Override
963        public String toString() {
964                return ToString.title( this.getClass().getName() )
965                                .println( "VERSION"                     ,VERSION                )
966                                .println( "sqlType"                     ,sqlType                )
967                                .println( "table"                       ,table                  )
968                                .println( "names"                       ,names                  )
969                                .println( "omitNames"           ,omitNames              )
970                                .println( "where"                       ,where                  )
971                                .println( "whereNames"          ,whereNames             )
972                                .println( "constKeys"           ,constKeys              )
973                                .println( "constVals"           ,constVals              )
974                                .println( "logicalDelete"       ,logicalDelete  )
975                                .fixForm().toString() ;
976        }
977}