    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 java.util.ArrayList;
    019import java.util.List;
    020import java.util.Set;                                                                                   // 6.4.3.4 (2016/03/11)
    021import java.util.regex.Pattern;                                                                 // 7.0.1.8 (2019/01/28)
    022
    023import org.opengion.fukurou.util.ErrorMessage;
    024import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
    025import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
    026import org.opengion.hayabusa.common.HybsSystem;
    027import org.opengion.hayabusa.common.HybsSystemException;
    028import org.opengion.hayabusa.db.DBTableModel;
    029import org.opengion.hayabusa.db.Query;
    030import org.opengion.hayabusa.resource.GUIInfo;
    031
    032import static org.opengion.fukurou.util.StringUtil.nval;
    033import static org.opengion.fukurou.system.HybsConst.BR;                 // 6.1.0.0 (2014/12/26) refactoring
    034
    035/**
    036 * SQL文を直接指定して、データベースに追加/更新/削除を行います(queryType="JDBCTableUpdate")。
    037 *
    038 * 存在チェックを行う場合は、tableExist タグと併用してください。
    039 * 複雑な処理が必要な場合は、従来より使用しています、PLSQLをCALLする、
    040 * plsqlUpdateタグを使用してください。
    041 * また、tableUpdateParam タグを使用する事で、テーブル名とsqlTypeの指定で動的に
    042 * SQL文を自動生成できます。これにより、追加、更新、削除やテーブルに関して、
    043 * 単一のJSP画面ですべて対応できるようになります。
    044 *
    045 * 7.2.9.3 (2020/11/06)
    046 *   queryType="JDBCTableMerge" と、"JDBCTableUpdate" を相互運用します。
    047 *   tableUpdateParam ﾀｸﾞのsqlType="MERGE" を指定しておくと、
    048 *   UPDATE文とINSERT文を両方とも作成して、有れば更新なければ追加処理を行います。
    049 *   その場合、queryTypeを、JDBCTableMerge に変更します。
    050 *   sqlType が従来の"INSERT"や"UPDATE" の場合は、queryTypeを、JDBCTableUpdate
    051 *   に変更します。
    052 *   この変換は、"JDBCTableMerge" と、"JDBCTableUpdate" を相互運用します。
    053 *
    054 * ※ このタグは、Transaction タグの対象です。
    055 *
    056 * @og.formSample
    057 * ●形式：&lt;og:tableUpdate command="…" names="…" queryType="JDBCTableUpdate" &gt;
    058 *             {&#064;SQL}
    059 *         &lt;/og:update&gt;
    060 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    061 *
    062 * ●Tag定義：
    063 *   &lt;og:tableUpdate
    064 *       queryType          【TAG】Query を発行する為のクラスID(JDBCTableUpdate,JDBCTableMerge)を指定します({@og.doc03Link queryType 初期値:JDBCTableUpdate})
    065 *       sqlType            【TAG】BODY部に書かれている Param の SQLタイプを指定します(INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE,無指定)
    066 *       command            【TAG】コマンド (NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)
    067 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session)
    068 *       displayMsg         【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します (初期値:VIEW_DISPLAY_MSG[=])
    069 *       resourceType       【特殊】クリアするリソースの種類[GEA03/GEA04/GEA08]を指定します
    070 *       conditionKey       【TAG】条件判定するカラムＩＤを指定します(初期値:null)
    071 *       conditionList      【TAG】条件判定する値のリストを、"|"で区切って登録します(初期値:無条件) 7.0.1.8 (2019/01/28) 正規表現に変更
    072 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
    073 *       dbid               【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します
    074 *       selectedAll        【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    075 *       selectedOne        【TAG】データを1件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    076 *       changeOnly         【TAG】変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false)　7.4.2.0 (2021/04/30)
    077 *       commitTableModel   【特殊】SQL実行後に結果をDBTableModelに反映させるかどうか[true/false]を指定します(初期値:true)
    078 *       followCdkh         【TAG】DBTableModelの改廃Cに従って処理を行うかを指定します
    079 *       quotCheck          【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します(初期値:false)
    080 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
    081 *                                                                              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
    082 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
    083 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
    084 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
    085 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
    086 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
    087 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
    088 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    089 *   &gt;   ... Body ...
    090 *   &lt;/og:tableUpdate&gt;
    091 *
    092 * ●使用例
    093 *    ・QUERYを他のJSPから渡す場合
    094 *    【copy.jsp】
    095 *        &lt;og:hidden name=&quot;SQL&quot; &gt;
    096 *          INSERT INTO GE41
    097 *               (CLM,NAME_JA,LABEL_NAME,KBSAKU,SYSTEM_ID,LANG,
    098 *               FGJ,DYSET,DYUPD,USRSET,USRUPD,PGUPD)
    099 *          VALUES
    100 *               ([CLM],[NAME_JA],[LABEL_NAME],[KBSAKU],[SYSTEM_ID],[LANG],
    101 *               &#39;1&#39;,&#39;{&#064;USER.YMDH}&#39;,&#39;{&#064;USER.YMDH}&#39;,&#39;{&#064;USER.ID}&#39;,&#39;{&#064;USER.ID}&#39;,&#39;{&#064;GUI.KEY}&#39;)
    102 *        &lt;/og:value&gt;
    103 *
    104 *    【entry.jsp】
    105 *        &lt;og:tableUpdate
    106 *            command   = &quot;{&#064;command}&quot;
    107 *            queryType = &quot;JDBCTableUpdate&quot;
    108 *        {&#064;SQL}
    109 *        &lt;/og:tableUpdate&gt;
    110 *
    111 *    ・tableUpdateParamを使用する場合
    112 *    【entry.jsp】
    113 *        &lt;og:tableUpdate
    114 *            command   = &quot;{&#064;command}&quot;
    115 *            queryType = &quot;JDBCTableUpdate&quot;
    116 *            sqlType   = &quot;{&#064;sqlType}&quot;        // tableUpdateParam の sqlType と一致
    117 *        &gt;
    118 *            &lt;og:tableUpdateParam
    119 *                sqlType     = &quot;{&#064;sqlType}"       // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
    120 *                table       = &quot;{&#064;TABLE_NAME}"    // 処理対象のテーブル名
    121 *                names       = &quot;{&#064;names}"         // 処理対象のカラム名
    122 *                omitNames   = &quot;{&#064;omitNames}"     // 処理対象外のカラム名
    123 *                where       = &quot;{&#064;where}"         // 処理対象を特定するキー
    124 *                constKeys   = &quot;{&#064;constKeys}"     // 処理カラム名の中の固定情報カラム名
    125 *                constVals   = &quot;{&#064;constVals}"     // 処理カラム名の中の固定情報設定値
    126 *            /&gt;
    127 *        &lt;/og:tableUpdate&gt;
    128 *
    129 *    ・処理の可否を指定する場合
    130 *    【entry.jsp】
    131 *        &lt;og:tableUpdate
    132 *            command   = &quot;{&#064;command}&quot;
    133 *            queryType = &quot;JDBCTableUpdate&quot;
    134 *            conditionKey  ="…"      : 条件判定するカラムＩＤを指定(初期値は columnId )
    135 *            conditionList ="…"      : 条件判定する値のリストを、"|"で区切って登録(初期値は、無条件)
    136 *        {&#064;SQL}
    137 *        &lt;/og:tableUpdate&gt;
    138 *
    139 *    ・JDBCTableUpdate のまま、sqlType="MERGE" を指定する場合。
    140 *      一つのtableUpdateParamで、両方(UPDATE,INSERT)の処理を実行します。
    141 *    【entry.jsp】
    142 *        &lt;og:tableUpdate
    143 *            command   = "{&#064;command}"
    144 *            queryType = "JDBCTableUpdate"
    145 *            &lt;og:tableUpdateParam
    146 *                sqlType     = "MERGE"                // INSERT or UPDATE
    147 *                table       = "{&#064;TABLE_NAME}"    // 処理対象のテーブル名
    148 *                names       = "{&#064;names}"         // 処理対象のカラム名
    149 *                omitNames   = "{&#064;omitNames}"     // 処理対象外のカラム名
    150 *                where       = "{&#064;where}"         // 処理対象を特定するキー（INSERT時には使われず、UPDAET時に使われる。）
    151 *                constKeys   = "{&#064;constKeys}"     // 処理カラム名の中の固定情報カラム名
    152 *                constVals   = "{&#064;constVals}"     // 処理カラム名の中の固定情報設定値
    153 *            /&gt;
    154 *        &lt;/og:tableUpdate&gt;
    155 *
    156 * @og.rev 3.8.8.0 (2007/12/22) 新規作成
    157 * @og.group ＤＢ登録
    158 *
    159 * @version  4.0
    160 * @author   Kazuhiko Hasegawa
    161 * @since    JDK5.0,
    162 */
    163public class TableUpdateTag extends QueryTag {
    164        /** このプログラムのVERSION文字列を設定します。   {@value} */
    165        private static final String VERSION = "7.4.2.0 (2021/04/30)" ;
    166        private static final long serialVersionUID = 742020210430L ;
    167
    168        /** command 引数に渡す事の出来る コマンド  登録{@value} */
    169        public static final String CMD_ENTRY  = "ENTRY" ;
    170        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
    171        private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_ENTRY );
    172
    173        // 処理を行う、リソースの種類を指定します。(GEA03,GEA04,GEA08 のどれか)
    174        private String  sqlType                 ;                       // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
    175        private String  resourceType    ;
    176        private int             resTypeColNo    = -1;
    177        private String  conditionKey    ;                       // 条件判定するカラムＩＤを指定(初期値は columnId )
    178        private String  conditionList   ;                       // 条件判定する値のリストを、"|"で区切って登録(初期値は、無条件) 7.0.1.8 (2019/01/28) 正規表現に変更
    179        private boolean selectedAll             ;
    180        private boolean selectedOne             ;                       // 7.4.2.0 (2021/04/30) 変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false)
    181        private boolean changeOnly              ;                       // 7.4.2.0 (2021/04/30) 変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false)
    182        private boolean commitTableModel= true;         // 4.0.2.0 (2007/12/25)
    183        private boolean followCdkh              ;                       // 4.3.2.0 (2008/09/09).
    184        private boolean quotCheck               ;                       // 5.1.7.0 (2010/06/01) quotCheckを指定できるようにする。※但し、初期値はfalse固定。タイミングを見て修正要
    185
    186        /**
    187         * デフォルトコンストラクター
    188         *
    189         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    190         */
    191        public TableUpdateTag() { super(); }            // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    192
    193        /**
    194         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    195         *
    196         * @og.rev 4.0.0.0 (2007/11/14) 0件の場合でもstartQueryTransactionを通すように変更
    197         * @og.rev 5.1.7.0 (2010/06/01) quotCheckを指定できるようにする。※但し、初期値はfalse固定。
    198         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
    199         *
    200         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
    201         */
    202        @Override
    203        public int doStartTag() {
    204                if( !useTag() ) { return SKIP_BODY ; }  // 6.3.4.0 (2015/08/01)
    205                dyStart = System.currentTimeMillis();
    206
    207                table = (DBTableModel)getObject( tableId );
    208                startQueryTransaction( tableId );               // 4.0.0.0 (2007/11/14) 0件の場合でもdoEndでPAGE_SKIPしないように位置変更。
    209                if( table == null || table.getRowCount() == 0 ||
    210                        ! check( command, COMMAND_SET ) ) { return SKIP_BODY ; }
    211                super.quotCheck = quotCheck;
    212
    213                return EVAL_BODY_BUFFERED ;     // Body を評価する。( extends BodyTagSupport 時)
    214        }
    215
    216        /**
    217         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    218         *
    219         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
    220         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
    221         * @og.rev 6.4.1.2 (2016/01/22) QueryTag.errMsgId  → QueryTag.ERR_MSG_ID  refactoring
    222         * @og.rev 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
    223         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
    224         *
    225         * @return      後続処理の指示
    226         */
    227        @Override
    228        public int doEndTag() {
    229                debugPrint();
    230                if( !useTag() ) { return EVAL_PAGE ; }  // 6.3.4.0 (2015/08/01)
    231
    232                String label  = "";                             // 4.0.0 (2005/11/30) 検索しなかった場合。
    233                if( check( command, COMMAND_SET ) ) {
    234                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    235                        if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
    236                                buf.append( executeCount )
    237                                        .append( getResource().getLabel( displayMsg ) )
    238                                        .append( BR );
    239                        }
    240
    241                        // 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
    242                        if( table != null && ! commitTableObject( tableId, table ) ) {
    243                                if( errMessage == null ) { errMessage = new ErrorMessage( "TableUpdateTag Query Error!" ); }
    244                                // ERR0041:検索処理中に割り込みの検索要求がありました。処理されません。
    245                                errMessage.addMessage( 0,ErrorMessage.NG,"ERR0041" );
    246                                errCode = ErrorMessage.NG;
    247                        }
    248
    249//                      final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
    250                        final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource(),useSLabel );         // 7.0.7.0 (2019/12/13)
    251                        if( err != null && err.length() > 0 ) {
    252                                buf.append( err );
    253                                // 6.4.1.2 (2016/01/22) QueryTag.errMsgId  → QueryTag.ERR_MSG_ID  refactoring
    254                                setSessionAttribute( ERR_MSG_ID,errMessage );
    255                        }
    256                        else {
    257                                // 6.4.1.2 (2016/01/22) QueryTag.errMsgId  → QueryTag.ERR_MSG_ID  refactoring
    258                                removeSessionAttribute( ERR_MSG_ID );
    259                        }
    260                        label = buf.toString();
    261
    262//                      // 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
    263//                      if( table != null && ! commitTableObject( tableId, table )  ) {
    264//                              // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
    265//                              jspPrint( "TableUpdateTag Query処理が割り込まれました。DBTableModel は登録しません。" );
    266//                              return SKIP_PAGE ;
    267//                      }
    268                }
    269
    270                jspPrint( label );
    271
    272                // セキュリティチェック(データアクセス件数登録)
    273                final long dyTime = System.currentTimeMillis()-dyStart;
    274                final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
    275                if( guiInfo != null ) { guiInfo.addWriteCount( executeCount,dyTime,sql ); }
    276
    277                if( useTimeView ) {             // 6.3.6.0 (2015/08/16)
    278                        jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );  // 3.5.6.3 (2004/07/12)
    279                }
    280                return errCode >= ErrorMessage.NG ? SKIP_PAGE : EVAL_PAGE ;
    281        }
    282
    283        /**
    284         * タグリブオブジェクトをリリースします。
    285         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    286         *
    287         * @og.rev 4.0.2.0 (2007/12/25) commitTableModel追加
    288         * @og.rev 4.1.2.0 (2008/03/12) sqlType追加
    289         * @og.rev 5.1.7.0 (2010/06/01) quotCheckを指定できるようにする。※但し、初期値はfalse固定。
    290         * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
    291         * @og.rev 7.4.2.0 (2021/04/30) selectedOne、changeOnlyを追加します
    292         */
    293        @Override
    294        protected void release2() {
    295                super.release2();
    296                sqlType                 = null;         // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
    297                resourceType    = null;
    298                resTypeColNo    = -1;
    299                conditionKey    = null;         // 条件判定するカラムＩＤを指定(初期値は columnId )
    300                conditionList   = null;         // 条件判定する値のリストを、"|"で区切って登録(初期値は、無条件) 7.0.1.8 (2019/01/28) 正規表現に変更
    301                selectedAll             = false;
    302                selectedOne             = false;        // 7.4.2.0 (2021/04/30)
    303                changeOnly              = false;        // 7.4.2.0 (2021/04/30)
    304                commitTableModel= true;         // 4.0.2.0 (2007/12/25)
    305                followCdkh              = false;        // 4.3.2.0 (2008/09/09)
    306                quotCheck               = false;        // 5.1.7.0 (2010/06/01)
    307        }
    308
    309        /**
    310         * Query を実行します。
    311         *
    312         * @og.rev 4.0.2.0 (2007/12/25) commitTableModel追加
    313         * @og.rev 6.3.6.1 (2015/08/28) close(),realClose() 廃止。Queryはキャッシュしません。
    314         *
    315         * @param   query オブジェクト
    316         */
    317        @Override
    318        protected void execute( final Query query ) {
    319                final int[] rowNo = getParameterRows();         // 4.0.0 (2005/01/31)
    320                if( rowNo.length > 0 ) {
    321                        query.execute( rowNo,table );
    322
    323                        errCode = query.getErrorCode();
    324                        errMessage = query.getErrorMessage();
    325
    326                        // リソースクリア処理
    327                        if( resourceType != null ) {
    328                                resTypeColNo = table.getColumnNo( "CLM" );              // キーは、CLM
    329                        }
    330
    331                        // 逆順にDELETEしないと、行番号がずれてしまう。
    332                        int row;
    333                        for( int j=rowNo.length-1; j>=0; j-- ) {
    334                                row = rowNo[j];
    335                                if( resTypeColNo >= 0 ) {
    336                                        clearResourceData( table.getValue( row,resTypeColNo ) );                // リソースのクリア
    337                                }
    338
    339                                if( commitTableModel ) {        // 4.0.2.0 (2007/12/25)
    340                                        if( DBTableModel.DELETE_TYPE.equals( table.getModifyType( row ) ) ) {
    341                                                table.removeValue( row );
    342                                        }
    343                                        else {
    344                                                table.resetModify( row );
    345                                        }
    346                                }
    347                        }
    348                }
    349        }
    350
    351        /**
    352         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行番号の
    353         * 配列を返します。
    354         * ここでは、conditionKey に値が設定されている場合は、そのカラムの値が
    355         * conditionList にマッチする場合のみ対象選択行として返します。
    356         * 値がセットされていない場合は、通常のCommonTagSupport#getParameterRows()
    357         * が呼ばれます。
    358         * なにも選ばれていない場合は、サイズ０の配列を返します。
    359         *
    360         * @og.rev 4.3.2.0 (2008/09/09) followCdkh属性対応
    361         * @og.rev 7.0.1.8 (2019/01/28) conditionListの判定を、正規表現に変更
    362         * @og.rev 7.4.2.0 (2021/04/30) selectedOne、changeOnlyを追加します
    363         *
    364         * @return       (選ばれていない場合は、サイズ０の配列を返す)
    365         * @og.rtnNotNull
    366         */
    367        @Override
    368        protected int[] getParameterRows() {
    369                int[] rowNo ;
    370                if( selectedAll ) {
    371                        final int rowCnt = table.getRowCount();         // 3.5.5.7 (2004/05/10)
    372                        rowNo = new int[ rowCnt ];
    373                        for( int i=0; i<rowCnt; i++ ) {
    374                                rowNo[i] = i;
    375                        }
    376                }
    377                else if( selectedOne ) {                                                // 7.4.2.0 (2021/04/30)
    378                        rowNo = new int[] {0};
    379                }
    380                else if( changeOnly ) {                                                 // 7.4.2.0 (2021/04/30)
    381                        rowNo = table.getChangeRowNos();
    382                }
    383                else {
    384                        rowNo = super.getParameterRows();                       // 4.0.0 (2005/01/31)
    385                }
    386
    387                // 7.0.1.8 (2019/01/28) conditionListの判定を、正規表現に変更
    388                if( conditionKey != null && conditionList != null ) {                   // 7.0.1.8 (2019/01/28)
    389                        final int col = table.getColumnNo( conditionKey );
    390                        final List<Integer> list = new ArrayList<>();
    391                        final Pattern ptn = Pattern.compile( conditionList );           // 7.0.1.8 (2019/01/28)
    392                        for( int i=0; i<rowNo.length; i++ ) {
    393                                final String val = nval( table.getValue( rowNo[i],col ) , "" );         // 7.0.1.8 (2019/01/28)
    394                                if( ptn.matcher( val ).matches() ) {
    395                                        list.add( Integer.valueOf( rowNo[i] ) );
    396                                }
    397                        }
    398
    399                        // 7.4.2.0 (2021/04/30) List<Integer> を、int[] に変換します。
    400                        rowNo = list.stream().mapToInt(i->i).toArray();
    401
    402//                      final int size = list.size();
    403//                      rowNo = new int[size];
    404//                      for( int i=0; i<size; i++ ) {
    405//                              rowNo[i] = list.get(i).intValue();
    406//                      }
    407                }
    408
    409                // 4.3.2.0 (2008/09/09)
    410                if( sqlType != null && sqlType.length() > 0 && followCdkh ) {
    411                        final List<Integer> flist = new ArrayList<>();
    412                        for( int i=0; i<rowNo.length; i++ ) {
    413                                final String cdkh = table.getModifyType( rowNo[i] );
    414                                // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
    415                                if(     ( "INSERT".equals( sqlType ) || "COPY".equals(   sqlType ) )    && DBTableModel.INSERT_TYPE.equals( cdkh )
    416//                                      ||      ( "UPDATE".equals( sqlType ) || "CHANGE".equals( sqlType ) )    && DBTableModel.UPDATE_TYPE.equals( cdkh )              // 7.2.9.1 (2020/10/23) CHANGE ⇒ MODIFY
    417                                        ||      ( "UPDATE".equals( sqlType ) || "MODIFY".equals( sqlType ) )    && DBTableModel.UPDATE_TYPE.equals( cdkh )
    418                                        ||        "DELETE".equals( sqlType )                                                                    && DBTableModel.DELETE_TYPE.equals( cdkh ) ) {
    419                                                flist.add( Integer.valueOf( rowNo[i] ) );
    420                                }
    421                        }
    422
    423                        // 7.4.2.0 (2021/04/30) List<Integer> を、int[] に変換します。
    424                        rowNo = flist.stream().mapToInt(i->i).toArray();
    425
    426//                      final int size = flist.size();
    427//                      rowNo = new int[size];
    428//                      for( int i=0; i<size; i++ ) {
    429//                              rowNo[i] = flist.get(i).intValue();
    430//                      }
    431                }
    432
    433                return rowNo;
    434        }
    435
    436//      /**
    437//       * BODY部の TableUpdateParamTag に書かれたquery文を受け取ります。
    438//       *
    439//       * typeには、UPDATEかINSERTが指定され、それに応じたqueryを各変数にセットします。
    440//       *
    441//       * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
    442//       * @og.rev 7.2.9.3 (2020/11/06) QueryTag に移動
    443//       *
    444//       * @param       type    Queryタイプ(UPDATE,INSERT,その他)
    445//       * @param       query   SQL文
    446//       */
    447//      protected void setQuery( final String type , final String query ) {
    448//              sql = query;                                                                                                    // とりあえず、セットしておく
    449//              sqlCnt++ ;                                                                                                              // 0の場合はBODYから、1の場合はそのまま、それ以上の場合は、マージ処理。QueryTag のprotected属性
    450//
    451//              if( "UPDATE".equalsIgnoreCase( type ) ) {
    452//                      updQuery = query;                                                                                       // 上位の QueryTag のprotected属性
    453//              }
    454//              else if( "INSERT".equalsIgnoreCase( type ) ) {
    455//                      insQuery = query;                                                                                       // 上位の QueryTag のprotected属性
    456//              }
    457//      }
    458
    459        /**
    460         * 【TAG】Query を発行する為のクラスID(JDBCTableUpdate,JDBCTableMerge)を指定します({@og.doc03Link queryType 初期値:JDBCTableUpdate})。
    461         *
    462         * @og.tag
    463         * 引数指定のUPDATE,INSERT,DELETE文を実行する場合の、queryType 属性を使用します。
    464         * このタグでは、execute( int[] ,DBTableModel )を実行します。
    465         * 代表的なクラスとして、"JDBCTableUpdate" が標準で用意されています。
    466         *
    467         * タグにより使用できる／出来ないがありますが、これは、org.opengion.hayabusa.db
    468         * 以下の Query_**** クラスの **** を与えます。
    469         * これらは、Query インターフェースを継承したサブクラスです。
    470         * {@og.doc03Link queryType Query_**** クラス}
    471         *
    472         * @param       id Queryタイプ
    473         * @see         org.opengion.hayabusa.db.Query  Queryのサブクラス
    474         * @see         org.opengion.hayabusa.db.Query#execute( int[] ,DBTableModel )
    475         */
    476        @Override
    477        public void setQueryType( final String id ) {
    478                super.setQueryType( nval( id,"JDBCTableUpdate" ) );
    479        }
    480
    481        /**
    482         * resourceType が設定されたときのみ使用される、キャッシュの初期化メソッドです。
    483         *
    484         * @param       key     初期化を行うキー
    485         */
    486        private void clearResourceData( final String key ) {
    487                getResource().clear( key );
    488        }
    489
    490        /**
    491         * 【特殊】クリアするリソースの種類[GEA03/GEA04/GEA08]を指定します。
    492         *
    493         * @og.tag
    494         * 注意：この属性は、リソース関連DBのメンテナンス時にのみ、内部リソースキャッシュを
    495         * クリアする目的で使用します。一般の属性としては、使用することはないため、
    496         * ご注意ください。
    497         * リソース関連のテーブルを更新した場合、リソースキャッシュをクリアして
    498         * 置かないと、データベースの値が反映されません。
    499         * 昔は、リソースの更新ごとに、全件クリアしていましたが、部分クリアが
    500         * できるようになったため、部分クリアを行います。
    501         * こでは、(GEA03,GEA04,GEA08) のどれかを指定してください。
    502         *
    503         * @param       type    クリアリソースタイプ [GEA03/GEA04/GEA08]
    504         */
    505        public void setResourceType( final String type ) {
    506                resourceType = nval( getRequestParameter(type),resourceType );
    507
    508                if( resourceType != null &&
    509                        "|GEA03|GEA04|GEA08|".indexOf( "|" + resourceType + "|" ) < 0 ) {
    510                                final String errMsg = "resourceTypeは GEA03,GEA04,GEA08 のどれかです。"
    511                                                        + "resourceType=" + type ;
    512                                throw new HybsSystemException( errMsg );
    513                }
    514        }
    515
    516        /**
    517         * 【TAG】条件判定するカラムＩＤを指定します(初期値:null)。
    518         *
    519         * @og.tag
    520         * 指定のカラムＩＤの値と、conditionList の値を比較して、
    521         * 存在する場合は、Query 処理を実行します。
    522         * 例えば、conditionKey="CDKH" として、conditionList="A" とすれば、
    523         * 改廃コードが"A"のデータで、かつ選択されたデータのみを処理します。
    524         * 設定しない場合は、通常の処理と同様に、選択行のみ処理されます。
    525         *
    526         * @param       key 条件判定カラム
    527         * @see         #setConditionList( String )
    528         */
    529        public void setConditionKey( final String key ) {
    530                conditionKey = nval( getRequestParameter( key ),null ) ;
    531        }
    532
    533        /**
    534         * 【TAG】条件判定する値のリストを、"|"で区切って登録します(初期値:無条件)。
    535         *
    536         * @og.tag
    537         * conditionKey とペアで指定します。ここには、カラムの設定値のリストを
    538         * 指定することで、複数条件(ＯＲ結合)での比較を行い、リストにカラム値が
    539         * 存在する場合のみ、Query 処理を実行します。
    540         * 値が設定されている場合は、その値とマッチする必要があります。なにもセット
    541         * されない場合、または、null の場合は、null データとマッチする場合のみ処理
    542         * されますので、ご注意ください。
    543         *
    544         * @og.rev 7.0.1.8 (2019/01/28) conditionListの判定を、正規表現に変更
    545         *
    546         * @param       list    条件判定値 ("|"区切のリスト)
    547         * @see         #setConditionKey( String )
    548         */
    549        public void setConditionList( final String list ) {
    550//              conditionList = "|" + nval( getRequestParameter( list ),"" ) + "|" ;
    551                conditionList = nval( getRequestParameter( list ),"" ) ;
    552        }
    553
    554        /**
    555         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
    556         *
    557         * @og.tag
    558         * 全てのデータを選択済みデータとして扱って処理します。
    559         * 全件処理する場合に、(true/false)を指定します。
    560         * 初期値は false です。
    561         *
    562         * changeOnly よりも selectedAll="true" が優先されます。 7.4.2.0 (2021/04/30)
    563         *
    564         * @param  all データを全件選択済み [true:全件選択済み/false:通常]
    565         */
    566        public void setSelectedAll( final String all ) {
    567                selectedAll = nval( getRequestParameter( all ),selectedAll );
    568        }
    569
    570        /**
    571         * 【TAG】データを1件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
    572         *
    573         * @og.tag
    574         * 先頭行の1件だけを選択済みとして処理します。
    575         * まとめ処理のデータを処理する場合などに使われます。
    576         * 初期値は false です。
    577         *
    578         * @og.rev 7.4.2.0 (2021/04/30) データを1件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    579         *
    580         * @param  one 先頭行の1件だけを選択済みとして処理するかどうか [true:処理する/false:通常]
    581         */
    582        public void setSelectedOne( final String one ) {
    583                selectedOne = nval( getRequestParameter( one ),selectedOne );
    584        }
    585
    586        /**
    587         * 【TAG】変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false)。
    588         *
    589         * @og.tag
    590         * 変更があったデータのみを処理します。
    591         * selectedAll="true" が指定された場合は、全件処理が優先されます。
    592         *
    593         * 通常は、チェックされた行のみか、selectedAll で全件処理しますが、空更新のデータは
    594         * 登録したくないケースに、元データと異なる行のみ処理します。
    595         * 初期値は false です。
    596         *
    597         * @og.rev 7.4.2.0 (2021/04/30) 変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false)
    598         *
    599         * @param  change 変更があったデータのみを処理するかどうか [true:変更分のみ/false:通常]
    600         */
    601        public void setChangeOnly( final String change ) {
    602                changeOnly = nval( getRequestParameter( change ),changeOnly );
    603        }
    604
    605        /**
    606         * 【特殊】SQL実行後に結果をDBTableModelに反映させるかどうか[true/false]を指定します(初期値:true)。
    607         *
    608         * @og.tag
    609         * 注意：この属性は、リソース関連DBのメンテナンス時に、複数DBへの登録を行うための、
    610         * 暫定対応として定義しています。
    611         * falseにした場合は、実データとDBTableModelの整合性が取れなくなるため、使用には十分注して下さい。
    612         * 初期値は true です。
    613         *
    614         * @og.rev 4.0.2.0 (2007/12/25) 新規作成
    615         *
    616         * @param  commitTblMdl 反映有無 [true:反映する/false:反映しない]
    617         */
    618        public void setCommitTableModel( final String commitTblMdl ) {
    619                commitTableModel = nval( getRequestParameter( commitTblMdl ),commitTableModel );
    620        }
    621
    622        /**
    623         * 引数の名称配列。
    624         *
    625         * @return      名称配列
    626         */
    627        protected String[] getNames() {
    628                return table.getNames() ;
    629        }
    630
    631        /**
    632         * 【TAG】BODY部に書かれている Param の SQLタイプを指定します。
    633         *
    634         * @og.tag
    635         * TableUpdateParamTag は、上位の TableUpdateTag の sqlType 属性 と同じ
    636         * sqlType 属性の場合のみ、SQL文を合成・出力します。
    637         * つまり、TableUpdateTag側のsqlType 属性をパラメータに、TableUpdateParamTag
    638         * の sqlType 属性を固定値にすることで、どのパラメータを使用するかを
    639         * 選択できる機能を実現する事が可能です。
    640         *
    641         * 7.2.9.3 (2020/11/06)
    642         *   複数指定できるようにします。
    643         *
    644         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    645         *
    646         * @param       type SQLタイプ
    647         */
    648        public void setSqlType( final String type ) {
    649                sqlType = nval( getRequestParameter( type ),sqlType );
    650        }
    651
    652        /**
    653         * 【TAG】DBTableModelの改廃Cに従って処理を行うかを指定します。
    654         *
    655         * @og.tag
    656         * この属性は、sqlTypeが指定されている場合のみ有効です。
    657         * sqlTypeが指定されている場合、そのsqlTypeに対応した、改廃Cが設定されている
    658         * 行のみを処理します。
    659         * 対応関係は、以下の通りです。
    660         *  sqlType = "INSERT" or "COPY" ⇒ 改廃C='A'のみ処理
    661//       *  sqlType = "UPDATE" or "CHANGE" ⇒ 改廃C='C'のみ処理
    662         *  sqlType = "UPDATE" or "MODIFY" ⇒ 改廃C='C'のみ処理
    663         *  sqlType = "DELETE" ⇒ 改廃C='D'のみ処理
    664         *
    665         * @og.rev 4.3.2.0 (2008/09/09) 新規追加
    666         *
    667         * @param       flag 改廃C処理 [true:行う/false:行わない]
    668         */
    669        public void setFollowCdkh( final String flag ) {
    670                followCdkh = nval( getRequestParameter( flag ),followCdkh );
    671        }
    672
    673        /**
    674         * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します(初期値:false)。
    675         *
    676         * @og.tag
    677         * ＳＱＬインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
    678         * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
    679         * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
    680         * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
    681         * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
    682         * (') が含まれていたエラーにする(true)／かノーチェックか(false)を指定します。
    683         * ※(他のタグは、システムリソースのUSE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])
    684         * ですが、JSPの互換性を考慮し、初期値を固定でfalseにしています)
    685         *
    686         * @og.rev 5.1.7.0 (2010/06/01) 新規追加
    687         *
    688         * @param   flag クォートチェック [true:する/それ以外:しない]
    689         */
    690        @Override
    691        public void setQuotCheck( final String flag ) {
    692                quotCheck = nval( getRequestParameter( flag ),quotCheck );
    693        }
    694
    695        /**
    696         * SQLタイプを返します。
    697         *
    698         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    699         *
    700         * @return      SQLタイプ
    701         */
    702        protected String getSqlType() {
    703                return sqlType ;
    704        }
    705
    706        /**
    707         * このオブジェクトの文字列表現を返します。
    708         * 基本的にデバッグ目的に使用します。
    709         *
    710         * @og.rev 4.0.2.0 (2007/12/25) resourceColumn、commitTableModel追加
    711         *
    712         * @return このクラスの文字列表現
    713         * @og.rtnNotNull
    714         */
    715        @Override
    716        public String toString() {
    717                return ToString.title( this.getClass().getName() )
    718                                .println( "VERSION"                     ,VERSION                )
    719                                .println( "resourceType"        ,resourceType   )
    720                                .println( "resTypeColNo"        ,resTypeColNo   )
    721                                .println( "conditionKey"        ,conditionKey   )
    722                                .println( "conditionList"       ,conditionList  )
    723                                .println( "followCdkh"          ,followCdkh             )
    724                                .println( "CMD_ENTRY"           ,CMD_ENTRY              )
    725                                .println( "commitTabelModel",commitTableModel )         // 4.0.2.0 (2007/12/25)
    726                                .println( "sql"                         ,sql                    )               // 4.1.2.0 (2008/03/12)
    727                                .println( "Other..."    ,getAttributes().getAttribute() )
    728                                .fixForm().toString()
    729                        + CR
    730                        + super.toString() ;
    731        }
    732}