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