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