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.io.IOException;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Locale;
023import java.util.Map;
024import java.util.Set;                                                                                   // 6.4.3.4 (2016/03/11)
025
026import javax.script.ScriptEngine;
027import javax.script.ScriptEngineManager;
028import javax.script.ScriptException;
029import javax.servlet.ServletException;
030
031import org.opengion.fukurou.db.DBUtil;
032import org.opengion.fukurou.db.Transaction;
033import org.opengion.fukurou.model.Formatter;
034import org.opengion.fukurou.util.ErrorMessage;
035import org.opengion.fukurou.util.StringUtil;
036import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
037import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
038import org.opengion.hayabusa.common.HybsSystem;
039import org.opengion.hayabusa.common.HybsSystemException;
040import org.opengion.hayabusa.db.DBTableModel;
041import org.opengion.hayabusa.resource.ResourceManager;
042
043import static org.opengion.fukurou.util.StringUtil.nval;
044
045/**
046 * 画面で入力されたデータのチェックを行うためのタグです。
047 *
048 * commandがNEWの場合は検索条件等のリクエストパラメータに対してチェックを行います。
049 * commandがENTRYの場合は、登録時のDBテーブルモデルに対するチェックを行います。
050 * (値の取得は、先に選択された行のみについて、実行されます。)
051 *
052 * チェックを行うための定義は、SQL文 又は JavaScriptの式が記述可能です。
053 * これらの式はタグのボディー部分に記述します。
054 *
055 * SQL文によりチェックを行う場合は、必ず件数が返されるように記述して下さい(select count(*) ・・・ 等)
056 * このSQL文で取得された件数とexistの属性値とを照合しチェックを行います。
057 * いずれの場合も、成立時は、正常とみなします。
058 * (「true:存在する」 には、データが存在した場合に、OKで、なければエラーです。)
059 *
060 * JavaScript式を記述する場合は、必ずtrue or falseを返す式を指定して下さい。
061 * この式を評価した結果falseが返される場合は、エラーとみなします。
062 * 式に不等号等を使用する場合は、CDATAセクションで囲うようにして下さい。
063 *
064 * また、いずれのチェック方法の場合でも、引数部に[カラム名]を用いたHybs拡張SQL文を
065 * 指定することが可能です。
066 * メッセージIDの{0},{1}にはそれぞれ[カラム名]指定されたカラム名及びデータがCSV形式で
067 * 自動的に設定されます。
068 *
069 * ※ このタグは、Transaction タグの対象です。
070 *
071 * @og.formSample
072 * <pre>
073 * ●形式:
074 *       ・&lt;og:dataCheck
075 *                    command       = "{&#064;command}"
076 *                    exist         = "[auto|true|false|one|notuse]"
077 *                    errRemove     = "[true|false]"
078 *                    lbl           = "{&#064;lbl}"
079 *                    lblParamKeys  = "ZY03"      : メッセージリソースのキーをCSV形式で指定。{2} 以降にセット
080 *                    sqlType       = "{&#064;sqlType}"
081 *                    execType      = "INSERT|COPY|UPDATE|MODIFY|DELETE"  : sqlType を含む場合、実行
082 *                    conditionKey  = "FGJ"        : 条件判定するカラムIDを指定(初期値は columnId )
083 *                    conditionList = "0|1|8|9"    : 条件判定する値のリストを、"|"で区切って登録(初期値は、無条件)
084 *                    uniqCheckClms = "CLM,LANG"   : DBTableModel内でのユニークキーチェックを行うためのカラム
085 *         &gt;
086 *
087 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
088 *         (SQL文 又は JavaScript式)
089 *       :なし( from属性、where属性を使用して、SQL文を内部で作成します)
090 *
091 * ●Tag定義:
092 *   &lt;og:dataCheck
093 *       command            【TAG】コマンド (NEW or ENTRY)をセットします
094 *       exist              【TAG】データベースのチェック方法[auto/true/false/one/notuse]を指定します(初期値:auto[自動])
095 *       tableId            【TAG】(通常は使いません)結果をDBTableModelに書き込んで、sessionに登録するときのキーを指定します
096 *       dbid               【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します(初期値:null)
097 *       lbl                【TAG】ラベルリソースIDを指定します
098 *       lblParamKeys       【TAG】ラベルリソースの引数をCSV形式で指定します
099 *       errRemove          【TAG】エラー時の選択行を取り除いて継続処理を行うかどうか[true/false]を指定します(初期値:false)
100 *       sqlType            【TAG】このチェックを行う、SQLタイプ を指定します
101 *       execType           【TAG】このチェックを行う、実行タイプ を指定します
102 *       conditionKey       【TAG】条件判定するカラムIDを指定します
103 *       conditionList      【TAG】条件判定する値のリストを、"|"で区切って登録します(初期値:無条件)
104 *       uniqCheckClms      【TAG】指定されたキーに従って、メモリ上のテーブルに対してユニークキーチェックを行います
105 *       beforeErrorJsp     【TAG】エラーが発生した際に、エラーメッセージの表示前にincludeするJSPを指定します
106 *       afterErrorJsp      【TAG】エラーが発生した際に、エラーメッセージの表示後にincludeするJSPを指定します
107 *       selectedAll        【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
108 *       msg                【廃止】メッセージIDを指定します(lbl 属性を使用してください)
109 *       msgParamKeys       【廃止】メッセージリソースの引数をCSV形式で指定します(lblParamKeys 属性を使用してください)
110 *       from               【TAG】tableExist タグ廃止に伴う、簡易機能追加。チェックするデータベース名(from 句)を指定します。
111 *       where              【TAG】tableExist タグ廃止に伴う、簡易機能追加。チェックする検索条件(where句)を指定します。
112 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
113 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
114 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
115 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
116 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
117 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
118 *   &gt;   ... Body ...
119 *   &lt;/og:dataCheck&gt;
120 *
121 * ●使用例
122 *       ・&lt;og:dataCheck
123 *                    command   = "ENTRY"
124 *                    exist     = "true"
125 *                    lbl       = "MSG0001"
126 *         &gt;
127 *             select count(*) from GEA03 where clm = [CLM]
128 *         &lt;/og:dataCheck&gt;
129 *
130 *          ・exist 属性の値に応じて、チェック方法が異なります。
131 *            [ auto , true , false , one , notuse が指定できます。]
132 *
133 *       ・&lt;og:dataCheck
134 *                    command   = "ENTRY"
135 *                    lbl       = "MSG0001"
136 *         &gt;
137 *           &lt;![CDATA[
138 *             [DYSTART] &lt; [DY] &amp;&amp; [DY] &lt; [DYEND]
139 *           ]]&gt;
140 *         &lt;/og:dataCheck&gt;
141 *
142 *         ・&lt;og:dataCheck
143 *                    command   = "ENTRY"
144 *                    lbl       = "MSG0001"
145 *         &gt;
146 *           &lt;![CDATA[
147 *             [GOKEI] &lt; [TANKA] * [RITU]
148 *           ]]&gt;
149 *         &lt;/og:dataCheck&gt;
150 *
151 *    ※ og:tableExist タグが廃止されました。og:dataCheckタグで置き換えてください。
152 *       ・&lt;og:tableExist
153 *                    command = "{&#064;command}"
154 *                    names   = "USERID,SYSTEM_ID"
155 *                    from    = "GE10"
156 *                    where   = "USERID=? AND SYSTEM_ID=?"
157 *                    exist   = "true"
158 *         /&gt;
159 * 
160 *        ⇒
161 *       ・&lt;og:dataCheck
162 *                    command = "{&#064;command}"
163 *                    exist   = "true"
164 *                    from    = "GE10"
165 *                    where   = "USERID=[USERID] AND SYSTEM_ID=[SYSTEM_ID]"
166 *         /&gt;
167 * 
168 *       ・&lt;og:tableExist
169 *                    command = "{&#064;command}"
170 *                    from    = "GE10"
171 *                    where   = "USERID=[USERID] AND SYSTEM_ID=[SYSTEM_ID]"  /&gt;
172 *        ⇒
173 *       ・&lt;og:dataCheck
174 *                    command = "{&#064;command}"
175 *                    from    = "GE10"
176 *                    where   = "USERID=[USERID] AND SYSTEM_ID=[SYSTEM_ID]"  /&gt;
177 *         /&gt;
178 *
179 * </pre>
180 *
181 * @og.rev 4.1.1.1 (2008/02/22) 新規作成
182 * @og.group DB登録
183 *
184 * @version  4.0
185 * @author       Hiroki Nakamura
186 * @since    JDK5.0,
187 */
188public class DataCheckTag extends CommonTagSupport {
189        /** このプログラムのVERSION文字列を設定します。   {@value} */
190        private static final String VERSION = "6.4.3.4 (2016/03/11)" ;
191        private static final long serialVersionUID = 643420160311L ;
192
193        /** command 引数に渡す事の出来る コマンド {@value} */
194        public static final String              CMD_NEW                         = "NEW";
195
196        /** command 引数に渡す事の出来る コマンド {@value} */
197        public static final String              CMD_ENTRY                       = "ENTRY";
198
199        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
200        private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_ENTRY, CMD_NEW );
201
202        /** 内部変数 */
203        private transient DBTableModel  table           ;
204        private transient boolean               isSql           ;
205        private transient boolean               isUniqCheck     ;               // 4.3.4.0 (2008/12/01) 追加
206        private transient ScriptEngine  jsEngine        ;
207        private transient String                bodyStr         ;               // 4.3.4.0 (2008/12/01) 追加
208
209        /** タグで設定する属性 */
210        private String          command                 = CMD_ENTRY;
211        private String          exist                   = "auto";
212        private String          tableId                 = HybsSystem.TBL_MDL_KEY;
213        private String          dbid                    ;
214        private String          lbl                             ;
215        private String[]        lblParamKeys    ;                       // 4.2.0.1 (2008/03/27)
216        private boolean         errRemove               ;
217        private String          sqlType                 ;                       // INSERT,COPY,UPDATE,MODIFY,DELETE
218        private String          execType                ;                       // INSERT,COPY,UPDATE,MODIFY,DELETE
219
220        private String          conditionKey    ;                       // 4.2.0.1 (2008/03/27)
221        private String          conditionList   ;                       // 4.2.0.1 (2008/03/27)
222        private String          from                    ;                       // 4.2.0.1 (2008/03/27)
223        private String          where                   ;                       // 5.7.6.2 (2014/05/16) tableExist タグに伴う便利機能追加
224        private String[]        uniqCheckClms   ;                       // 4.3.4.0 (2008/12/01)
225
226        private String          beforeErrorJsp  ;                       // 5.1.9.0 (2010/08/01)
227        private String          afterErrorJsp   ;                       // 5.1.9.0 (2010/08/01)
228        private boolean         selectedAll             ;                       // 5.1.9.0 (2010/08/01)
229
230        private boolean         isExec                  ;                       // 6.3.4.0 (2015/08/01) パラメータではなく毎回設定します。
231
232        /**
233         * デフォルトコンストラクター
234         *
235         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
236         */
237        public DataCheckTag() { super(); }              // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
238
239        /**
240         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
241         *
242         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
243         * @og.rev 4.1.2.0 (2008/03/12) sqlType,execType 判定
244         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
245         *
246         * @return      後続処理の指示
247         */
248        @Override
249        public int doStartTag() {
250                isExec = useTag() && ( sqlType == null || execType == null || execType.indexOf( sqlType ) >= 0 ) ;
251
252                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
253                return isExec
254                                        ? EVAL_BODY_BUFFERED            // Body を評価する
255                                        : SKIP_BODY ;                           // Body を評価しない
256
257        }
258
259        /**
260         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
261         *
262         * @og.rev 4.3.4.0 (2008/12/01) 新規追加
263         * @og.rev 6.3.1.1 (2015/07/10) BodyString,BodyRawStringは、CommonTagSupport で、trim() します。
264         *
265         * @return      後続処理の指示(SKIP_BODY)
266         */
267        @Override
268        public int doAfterBody() {
269                bodyStr = getBodyString();
270                return SKIP_BODY ;
271        }
272
273        /**
274         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
275         *
276         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
277         * @og.rev 4.1.2.0 (2008/03/12) sqlType,execType 判定
278         * @og.rev 4.2.0.1 (2008/03/27) from を取得
279         * @og.rev 4.2.1.0 (2008/04/11) ErrMessageManager対応
280         * @og.rev 4.3.4.0 (2008/12/01) ユニークキーチェック対応。bodyContentの取得を#doAfterBody()で行う。
281         * @og.rev 5.1.9.0 (2010/08/01) エラーメッセージの表示前後にincludeするJSPを指定できるようにする。
282         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
283         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 、Transaction対応で、close処理を入れる。
284         * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
285         *
286         * @return      後続処理の指示
287         */
288        @Override
289        public int doEndTag() {
290                debugPrint();
291                int rtnCode = EVAL_PAGE;
292
293                // 4.1.2.0 (2008/03/12) 実行条件 isExec を評価
294                if( isExec && check( command, COMMAND_SET ) ) {
295                        // exist="notuse"の場合はチェックしない
296                        if( "notuse".equalsIgnoreCase( exist ) ) { return rtnCode; }
297
298                        // パラメーターから処理のタイプを判別
299                        checkParam();
300
301                        // エラーメッセージを管理するクラスを作成します。
302                        final ErrMessageManager manager = new ErrMessageManager();
303                        manager.setTitle( "Data Check Error!" );
304                        manager.setParamKeys( lblParamKeys );
305                        manager.setResourceManager( getResource() );
306                        manager.setFrom( from );
307
308                        // 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
309                        try( final Transaction tran = getTransaction() ) {
310                                // command="NEW"の場合
311                                if( CMD_NEW.equals( command ) ) {
312                                        if( isSql ) {
313                                                checkSql( bodyStr, manager, null, 0, DBTableModel.UPDATE_TYPE, tran );          // 5.1.9.0 (2010/08/01)
314                                        }
315                                        else {
316                                                checkJs( bodyStr, manager, null, 0, jsEngine );
317                                        }
318                                }
319                                // command="ENTRY"の場合(テーブルモデルが存在しない場合は処理しない)
320                                else if( CMD_ENTRY.equals( command ) ) {
321                                        table = (DBTableModel) getObject( tableId );
322                                        if( table != null && table.getRowCount() > 0 ) {
323                                                manager.setDBTableModel( table );
324                                                if( isUniqCheck ) {
325                                                        checkUnique( manager );
326                                                }
327                                                else {
328                                                        checkRows( bodyStr, manager, tran );            // 5.1.9.0 (2010/08/01)
329                                                }
330                                        }
331                                        else {
332                                                System.out.println( "DBTableModel doesn't exist!! need this when command=\"ENTRY\"" );
333                                        }
334                                }
335                                tran.commit();                          // 6.3.6.1 (2015/08/28)
336                        }
337
338                        // エラーが発生した場合は、エラーメッセージを表示して以降の処理を行わない。
339                        final ErrorMessage errMessage = manager.getErrMessage() ;
340                        if( errMessage != null && !errMessage.isOK() && !errRemove ) {
341                                rtnCode = SKIP_PAGE;
342
343                                // 5.1.9.0 (2010/08/01) エラーメッセージの表示前にincludeするJSPを指定
344                                if( beforeErrorJsp != null && beforeErrorJsp.length() > 0 ) {
345                                        includeJsp( beforeErrorJsp );
346                                }
347
348                                jspPrint( TaglibUtil.makeHTMLErrorTable( errMessage, getResource() ) );
349
350                                // 5.1.9.0 (2010/08/01) エラーメッセージの表示後にincludeするJSPを指定
351                                if( afterErrorJsp != null && afterErrorJsp.length() > 0 ) {
352                                        includeJsp( afterErrorJsp );
353                                }
354                        }
355                }
356
357                return rtnCode ;
358        }
359
360        /**
361         * タグリブオブジェクトをリリースします。
362         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
363         *
364         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
365         * @og.rev 4.1.2.0 (2008/03/12) sqlType , execType , isExec 追加
366         * @og.rev 4.2.0.1 (2008/03/27) conditionKey , conditionList , msgParamKeys 追加
367         * @og.rev 5.1.9.0 (2010/08/01) beforeErrorJsp , afterErrorJsp, selectedAll 追加
368         * @og.rev 5.7.6.2 (2014/05/16) where 追加。tableExist タグに伴う便利機能追加
369         * @og.rev 6.3.4.0 (2015/08/01) isExec は、パラメータではなく、ローカル変数。
370         */
371        @Override
372        protected void release2() {
373                super.release2();
374                tableId                 = HybsSystem.TBL_MDL_KEY;
375                dbid                    = null;
376                command                 = CMD_ENTRY;
377                table                   = null;
378                exist                   = "auto";
379                errRemove               = false;
380                lbl                     = null;
381                lblParamKeys    = null;         // 4.2.0.1 (2008/03/27)
382                isSql                   = false;
383                isUniqCheck             = false;        // 4.3.4.0 (2008/12/01)
384                jsEngine                = null;
385                sqlType                 = null;         // INSERT,COPY,UPDATE,MODIFY,DELETE
386                execType                = null;         // INSERT,COPY,UPDATE,MODIFY,DELETE
387                conditionKey    = null;         // 4.2.0.1 (2008/03/27)
388                conditionList   = null;         // 4.2.0.1 (2008/03/27)
389                from                    = null;         // 4.2.0.1 (2008/03/27)
390                where                   = null;         // 5.7.6.2 (2014/05/16) tableExist タグに伴う便利機能追加
391                bodyStr                 = null;         // 4.3.4.0 (2008/12/01))
392                uniqCheckClms   = null;         // 4.3.4.0 (2008/12/01)
393                beforeErrorJsp  = null;         // 5.1.9.0 (2010/08/01)
394                afterErrorJsp   = null;         // 5.1.9.0 (2010/08/01)
395                selectedAll             = false;        // 5.1.9.0 (2010/08/01)
396        }
397
398        /**
399         * 引数及びボディー部分のチェックを行い、処理のタイプを判別します。
400         *
401         * @og.rev 5.5.8.0 (2012/11/01) タイプ判別変更
402         * @og.rev 5.6.1.1 (2013/02/08) FROM 部の切り出し位置修正
403         * @og.rev 5.7.6.2 (2014/05/16) tableExist タグに伴う便利機能追加。from属性とwhere属性追加
404         */
405        private void checkParam() {
406                isUniqCheck = uniqCheckClms != null && uniqCheckClms.length > 0 ;
407                if( isUniqCheck ) {
408                        if( !CMD_ENTRY.equals( command ) ) {
409                                final String errMsg = "ユニークキーチェックは、command=\"ENTRY\"の場合のみ使用可能です。"
410                                                        + " command=" + command ;               // 5.1.8.0 (2010/07/01) errMsg 修正
411                                throw new HybsSystemException( errMsg );
412                        }
413                }
414                // 5.7.6.2 (2014/05/16) tableExist タグに伴う便利機能追加。from属性とwhere属性追加
415                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
416                else if( from == null ) {
417                        if( bodyStr == null || bodyStr.isEmpty() ) {
418                                final String errMsg = "Body部分にチェック定義を記述して下さい。";
419                                throw new HybsSystemException( errMsg );
420                        }
421
422                        // SQLチェックかJavaScriptによるチェックかの判定
423                        final String query = bodyStr.toUpperCase( Locale.JAPAN );               // 4.2.0.1 (2008/03/27)
424                        if( query.indexOf( "SELECT" ) == 0 ) { // 5.5.8.0 (2012/11/01) 先頭に限定する。(trim済のため)
425                                isSql = true;
426                                final int st = query.indexOf( "FROM" ) ;
427                                final int ed = query.indexOf( "WHERE" ) ;
428                                if( st > 0 && st < ed ) {
429                                        from = query.substring( st+"FROM".length(),ed ).trim();         // 5.6.1.1 (2013/02/08)
430                                }
431                        }
432                        else {
433                                jsEngine = new ScriptEngineManager().getEngineByName( "JavaScript" );
434                        }
435                }
436                else {
437                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
438                                .append( "SELECT count(*) FROM " ).append( from );
439                        if( where != null ) { buf.append( " WHERE " ).append( where ); }
440                        bodyStr = buf.toString();
441                        isSql = true;
442                }
443
444        }
445
446        /**
447         * SQLによるデータチェックを行います。
448         * チェック方法は、exist属性の指定に依存します。
449         * autoの場合は、テーブルモデルの改廃Cから自動でチェック方法が決定されます。
450         *
451         * @param       str             実行するSQL文
452         * @param       manager ErrMessageManagerオブジェクト
453         * @param       values  SQL文のパラメータ
454         * @param       row             行番号
455         * @param       modifyType      改廃C
456         * @param       tran    トランザクションオブジェクト
457         *
458         * @return      処理の成否
459         *
460         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
461         * @og.rev 4.2.1.0 (2008/04/11) ErrMessageManager対応
462         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
463         */
464        private boolean checkSql( final String str, final ErrMessageManager manager, final String[] values
465                                                        , final int row, final String modifyType, final Transaction tran ) {
466
467                final int cnt = DBUtil.dbExist( str, values, tran, dbid );                      // 5.1.9.0 (2010/08/01)
468
469                boolean okFlag = true;
470                String id = null;
471                if( ( "true".equalsIgnoreCase( exist ) || ( "auto".equalsIgnoreCase( exist )
472                                && ( DBTableModel.UPDATE_TYPE.equals( modifyType ) || DBTableModel.DELETE_TYPE.equals( modifyType ) ) ) ) && cnt <= 0 ) {
473                        // ERR0025=データ未登録エラー。キー={0}、値={1} のデータは、存在していません。
474                        id = ( lbl == null ? "ERR0025" : lbl );
475                        okFlag = false;
476                }
477                else if( ( "false".equalsIgnoreCase( exist ) || ( "auto".equalsIgnoreCase( exist )
478                                && DBTableModel.INSERT_TYPE.equals( modifyType ) ) ) && cnt > 0 ) {
479                        // ERR0026=データ登録済みエラー。キー={0}、値={1} のデータは、すでに存在しています。
480                        id = ( lbl == null ? "ERR0026" : lbl );
481                        okFlag = false;
482                }
483                else if( "one".equalsIgnoreCase( exist ) && cnt > 1 ) {
484                        // ERR0027=データ2重登録エラー。キー={0}、値={1} のデータは、重複して存在しています。
485                        id = ( lbl == null ? "ERR0027" : lbl );
486                        okFlag = false;
487                }
488
489                if( !okFlag ) {
490                        manager.addMessage( row, id, values );
491
492                }
493                return okFlag;
494        }
495
496        /**
497         * JavaScriptの式を実行します。
498         * 実行した結果がboolean型でない場合はエラーとなります。
499         *
500         * @param str  実行するSQL文
501         * @param       manager オブジェクト
502         * @param values 値配列
503         * @param row 行番号
504         * @param engine JavaScriptエンジン
505         *
506         * @return 処理の成否
507         *
508         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
509         * @og.rev 4.2.0.1 (2008/03/27) getClass().getName() から、instanceof に変更
510         * @og.rev 4.2.1.0 (2008/04/11) ErrMessageManager対応
511         */
512        private boolean checkJs(  final String str, final ErrMessageManager manager, final String[] values
513                                                        , final int row, final ScriptEngine engine ) {
514                // JavaScriptエンジンによる評価
515                Object obj = null;
516                try {
517                        obj = engine.eval( str );
518                }
519                catch( final ScriptException ex ) {
520                        final String errMsg = "JavaScript式のパースに失敗しました。[" + str + "]";
521                        throw new HybsSystemException( errMsg , ex );
522                }
523
524                // 返り値がBoolean型かチェック
525                boolean okFlag = false;
526                // 4.2.0.1 (2008/03/27) instanceof に変更
527                if( obj instanceof Boolean ) {  // 4.3.1.1 (2008/08/23) instanceof チェックは、nullチェック不要
528                        okFlag = ((Boolean)obj).booleanValue();
529                }
530                else {
531                        final String errMsg = "JavaScript式には true 若しくは false が返るように設定して下さい"
532                                                + " Object=" + obj ;                    // 5.1.8.0 (2010/07/01) errMsg 修正
533                        throw new HybsSystemException( errMsg );
534                }
535
536                if( !okFlag ) {
537                        // ERR0030=入力したデータが不正です。key={0} value={1} 形式={2}
538                        final String id = ( lbl == null ? "ERR0030" : lbl );
539
540                        manager.addMessage( row, id, values );
541                }
542
543                return okFlag;
544        }
545
546        /**
547         * DBテーブルモデルの各行に対してデータチェックを行います。
548         *
549         * @param str           チェック対象の文字列
550         * @param manager       ErrMessageManagerオブジェクト
551         * @param tran          トランザクションオブジェクト
552         *
553         * @og.rev 4.1.1.0 (2008/02/22) 新規作成
554         * @og.rev 4.2.0.1 (2008/03/27) conditionKey,conditionList 対応
555         * @og.rev 4.2.1.0 (2008/04/11) ErrMessageManager対応
556         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
557         * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいコンストラクターを追加する。
558         */
559        private void checkRows( final String str, final ErrMessageManager manager, final Transaction tran ) {
560
561                final int[] rowNo = getParameterRows(); // 4.0.0 (2005/01/31)
562                if( rowNo.length == 0 ) { return; }
563
564                final Formatter format = new Formatter( table,str );            // 6.4.3.4 (2016/03/11)
565                final int[] clmNo = format.getClmNos();
566                // 4.2.0.1 (2008/03/27) カラム名のメッセージリソース文字列を作成します。
567                manager.setClmNos( clmNo );
568
569                // SQL文の場合のみ[xxx]を?に変換したSQL文を取得(JavaScriptの場合はループ内で各行毎に取得
570                String query = null;
571                if( isSql ) {
572                        query = format.getQueryFormatString();
573                }
574
575                // 4.2.0.1 (2008/03/27) conditionKey,conditionList 対応
576                int cndKeyNo = -1;
577                if( conditionKey != null && conditionList != null ) {
578                        cndKeyNo = table.getColumnNo( conditionKey );           // 不正指定はエラー
579                }
580
581                final List<Integer> list = new ArrayList<>();
582                boolean okFlag = false;
583                for( int i=0; i<rowNo.length; i++ ) {
584                        final int row = rowNo[i] ;
585                        final String[] values = getTableModelData( row, clmNo );
586                        // 4.2.0.1 (2008/03/27) 条件指定がされている場合に、
587                        // Listに含まれない場合は、実行されない。
588                        // 4.2.1.0 (2008/04/11) 厳密に処理します。
589                        if( cndKeyNo >= 0 && conditionList.indexOf( table.getValue( row,cndKeyNo ) ) < 0 ) {
590                                final String conVal = "|" + table.getValue( row,cndKeyNo ) + "|" ;
591                                if( conditionList.indexOf( conVal ) < 0 ) { continue; }
592                        }
593
594                        if( isSql ) {
595                                okFlag = checkSql( query, manager, values, row, table.getModifyType( row ), tran );
596                        }
597                        else {
598                                final String jsStr = format.getFormatString( row, "\"" );
599                                okFlag = checkJs( jsStr, manager, values, row, jsEngine );
600                        }
601
602                        if( errRemove && okFlag ) {
603                                list.add( row );
604                        }
605                }
606
607                if( errRemove ) {
608                        final Integer[] in = list.toArray( new Integer[list.size()] );
609                        int[] newRowNo = new int[in.length];
610                        for( int i=0; i<in.length; i++ ) {
611                                newRowNo[i] = in[i].intValue();
612                        }
613                        setParameterRows( newRowNo );
614                }
615        }
616
617        /**
618         * DBテーブルモデルの各行にユニークキーのチェックを行います。
619         *
620         * @og.rev 4.3.4.0 (2008/12/01) 新規作成
621         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
622         *
623         * @param manager ErrMessageManagerオブジェクト
624         */
625        private void checkUnique( final ErrMessageManager manager ) {
626                final int[] rowNo = getParameterRows();
627                if( rowNo.length == 0 ) { return; }
628
629                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
630                if( uniqCheckClms == null ) { return; }
631
632                int[] clmNo = new int[uniqCheckClms.length];
633                for( int i=0; i<clmNo.length; i++ ) {
634                        clmNo[i] = table.getColumnNo( uniqCheckClms[i] );
635                }
636
637                manager.setClmNos( clmNo );
638
639                final List<Integer> list = new ArrayList<>();
640                final Map<String,Integer> map = new HashMap<>();
641                for( int i=0; i<rowNo.length; i++ ) {
642                        final int row = rowNo[i] ;
643                        final String[] values = getTableModelData( row, clmNo );
644                        final String key = StringUtil.array2line( values, " + " );
645
646                        if( map.get( key ) == null ) {
647                                map.put( key, 1 );
648                                if( errRemove ) {
649                                        list.add( row );
650                                }
651                        }
652                        else {
653                                // ERR0027=データ2重登録エラー。キー={0}、値={1} のデータは、重複して存在しています。
654                                id = ( lbl == null ? "ERR0027" : lbl );
655                                manager.addMessage( row, id, values );
656                        }
657                }
658
659                if( errRemove ) {
660                        final Integer[] in = list.toArray( new Integer[list.size()] );
661                        int[] newRowNo = new int[in.length];
662                        for( int i=0; i<in.length; i++ ) {
663                                newRowNo[i] = in[i].intValue();
664                        }
665                        setParameterRows( newRowNo );
666                }
667        }
668
669        /**
670         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
671         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
672         *
673         * @og.tag
674         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
675         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
676         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
677         * この tableId 属性を利用して、メモリ空間を分けます。
678         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
679         *
680         * @param       id テーブルID (sessionに登録する時のID)
681         */
682        public void setTableId( final String id ) {
683                tableId = nval( getRequestParameter( id ), tableId );
684        }
685
686        /**
687         * 【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します(初期値:null)。
688         *
689         * @og.tag Queryオブジェクトを作成する時のDB接続IDを指定します。
690         *
691         * @param       id データベース接続ID
692         */
693        public void setDbid( final String id ) {
694                dbid = nval( getRequestParameter( id ), dbid );
695        }
696
697        /**
698         * 【TAG】コマンド (NEW or ENTRY)をセットします。
699         *
700         * @og.tag
701         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
702         * フィールド定数値のいづれかを、指定できます。
703         *
704         * @param       cmd コマンド (public static final 宣言されている文字列)
705         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.DataCheckTag.CMD_NEW">コマンド定数</a>
706         */
707        public void setCommand( final String cmd ) {
708                final String cmd2 = getRequestParameter( cmd );
709                if( cmd2 != null && cmd2.length() > 0 ) {
710                        command = cmd2.toUpperCase( Locale.JAPAN );
711                }
712        }
713
714        /**
715         * 【TAG】データベースのチェック方法[auto/true/false/one/notuse]を指定します(初期値:auto[自動])。
716         *
717         * @og.tag
718         * exist 属性に指定された 、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、
719         * の値は、いずれの場合も、成立時は、正常とみなします。
720         * 「auto:自動」は、DBTableModeleのmodifyType(A,C,D)に応じて、チェックします。
721         * A,C,D は、entryタグにコマンドを渡してデータを作成したときに、内部で作成されます。
722         * (command="NEW"の場合は、trueと同じ動きになります。)
723         * notuse は、チェックを行いません。これは、このタグを共有使用する場合に、外部で
724         * チェックを行うかどうかを指定できるようにするために使用します。
725         * (「true:存在する」 には、データが存在した場合に、OKで、なければエラーです。)
726         * 初期値は、「auto:自動」です。
727         *
728         * @param       ext チェック方法 [auto:自動/true:存在する/false:存在しない/one:ひとつ以下/notuse:チェックしない]
729         */
730        public void setExist( final String ext ) {
731                exist = nval( getRequestParameter( ext ), exist );
732                if( !"auto".equalsIgnoreCase( exist )
733                                && !"true".equalsIgnoreCase( exist )
734                                && !"false".equalsIgnoreCase( exist )
735                                && !"one".equalsIgnoreCase( exist )
736                                && !"notuse".equalsIgnoreCase( exist ) ) {
737                        final String errMsg = "exist 属性は、(auto,true,false,one,notuse)を指定してください。 [" + exist + "]" + CR;
738                        throw new HybsSystemException( errMsg );
739                }
740        }
741
742        /**
743         * 【TAG】エラー時の選択行を取り除いて継続処理を行うかどうか[true/false]を指定します(初期値:false)。
744         *
745         * @og.tag
746         * exist 属性に指定された 、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、
747         * に対して、エラーが発生した選択行番号を、取り除いて以下の処理を継続するかどうかを
748         * 指定します。
749         * true に設定した場合は、エラーデータを削除し、継続処理を行うことができます。
750         * flase の場合は、エラーデータを表示して、継続処理を停止します。
751         * 初期値は、「false:エラー時停止」です。
752         *
753         * @param       flag エラーデータを除外 [true:継続処理/false:エラー時停止]
754         */
755        public void setErrRemove( final String flag ) {
756                errRemove = nval( getRequestParameter( flag ), errRemove );
757        }
758
759        /**
760         * 【TAG】ラベルリソースのラベルIDを指定します。
761         *
762         * @og.tag ラベルリソースIDを指定します。
763         * 各処理に応じた初期設定のラベルリソースIDは、以下の通りです。
764         *   exist="true"   ERR0025=データ未登録エラー。キー={0}、値={1} のデータは、存在していません。
765         *   exist="false"  ERR0026=データ登録済みエラー。キー={0}、値={1} のデータは、すでに存在しています。
766         *   exist="one"    ERR0027=データ2重登録エラー。キー={0}、値={1} のデータは、重複して存在しています。
767         *   JavaScript     ERR0030=入力したデータが不正です。key={0} value={1} 形式={2}
768         * 引数のパラメータには、通常、チェックに使用した実データが、DBTableModel から取得されます。
769         * 引数を変更する場合は、lblParamKeys を使用してください。
770         *
771         * @param id メッセージID
772         * @see    #setLblParamKeys( String )
773         */
774        @Override
775        public void setLbl( final String id ) {
776                // 継承親のメソッドを使わない。
777                lbl = nval( getRequestParameter( id ), lbl );
778        }
779
780        /**
781         * 【TAG】ラベルリソースの引数をCSV形式で指定します。
782         *
783         * @og.tag
784         * ラベルリソースのキーをCSV形式で指定することで、設定します。
785         * ラベルに引数( {0},{1} など ) がある場合、ここで指定した値を
786         * 順番に、{0},{1},{2}・・・ に当てはめていきます。
787         * キーワードは、CSV形式で指定し、それを分解後、ラベルリソースで
788         * リソース変換を行います。(つまり、記述された値そのものでは在りません)
789         * PL/SQL では、"{#PN}" などと指定していた分は、同様に "PN" と指定しです。
790         * 内部的に、where 条件に指定されたキーと値は、&#064;KEY と &#064;VAL に、
791         * from と where の間の文字列は、&#064;TBL に対応付けられます。
792         * {&#064;XXXX} 変数も使用できます。実データの値を取出したい場合は、[PN]と
793         * すれば、DBTableModel の PN の値を取出します。
794         * なにも指定しない場合は、キー={0} 、値={1}、from={2} です。
795         *
796         * @og.rev 4.2.0.1 (2008/03/27) 新規追加
797         *
798         * @param keys メッセージリソースのキー(CSV)
799         * @see    #setLbl( String )
800         */
801        public void setLblParamKeys( final String keys ) {
802                lblParamKeys = getCSVParameter( keys );
803        }
804
805        /**
806         * 【TAG】このチェックを行う、SQLタイプ を指定します。
807         *
808         * @og.tag
809         * SQLタイプは、INSERT,COPY,UPDATE,MODIFY,DELETE などの記号を指定します。
810         * 一般には、result 画面から update 画面へ遷移するときの、command と
811         * 同じにしておけばよいでしょう。
812         * これは、execType とマッチした場合のみ、このチェックが処理されます。
813         * 簡易 equals タグの代役に使用できます。
814         * なにも指定しない場合は、チェックは実行されます。
815         *
816         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
817         *
818         * @param       type このチェックを行うSQLタイプ
819         */
820        public void setSqlType( final String type ) {
821                sqlType = nval( getRequestParameter( type ),sqlType );
822        }
823
824        /**
825         * 【TAG】このチェックを行う、実行タイプ を指定します。
826         *
827         * @og.tag
828         * 実行タイプは、sqlType とマッチした場合のみ、このチェックが処理されます。
829         * 簡易 equals タグの代役に使用できます。
830         * execType は、複数指定が可能です。単純な文字列マッチで、sqlType を
831         * 含めば、実行されます。
832         * 例えば、sqlType={&#064;sqlType} execType="INSERT|COPY" とすれば、
833         * sqlType に、INSERT または、COPY が登録された場合にチェックが掛かります。
834         * なにも指定しない場合は、チェックは実行されます。
835         *
836         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
837         *
838         * @param       type このチェックを行う実行タイプ
839         */
840        public void setExecType( final String type ) {
841                execType = nval( getRequestParameter( type ),execType );
842        }
843
844        /**
845         * 【TAG】条件判定するカラムIDを指定します(初期値:null)。
846         *
847         * @og.tag
848         * 指定のカラムIDの値と、conditionList の値を比較して、
849         * 存在する場合は、check処理を実行します。
850         * この処理が有効なのは、command="ENTRY" の場合のみです。
851         *
852         * @og.rev 4.2.0.1 (2008/03/27) 新規追加
853         *
854         * @param       key カラムID
855         * @see         #setConditionList( String )
856         */
857        public void setConditionKey( final String key ) {
858                conditionKey = nval( getRequestParameter( key ),null ) ;
859        }
860
861        /**
862         * 【TAG】条件判定する値のリストを、"|"で区切って登録します(初期値:無条件)。
863         *
864         * @og.tag
865         * conditionKey とペアで指定します。ここには、カラムの設定値のリストを
866         * 指定することで、複数条件(OR結合)での比較を行い、リストにカラム値が
867         * 存在する場合のみ、check処理を実行します。
868         * この処理が有効なのは、command="ENTRY" の場合のみです。
869         * 設定しない場合は、無条件に実行します。
870         *
871         * @og.rev 4.2.0.1 (2008/03/27) 新規追加
872         *
873         * @param       list 条件判定する値("|"で区切)
874         * @see         #setConditionKey( String )
875         */
876        public void setConditionList( final String list ) {
877                conditionList = nval( getRequestParameter( list ),null ) ;
878                if( conditionList != null ) {
879                        conditionList = "|" + conditionList + "|" ;
880                }
881        }
882
883        /**
884         * 【TAG】指定されたキーに従って、メモリ上のテーブルに対してユニークキーチェックを行います。
885         *
886         * @og.tag
887         * ユニークキーチェックを行うキーを指定します。ここで、指定されたキーに対して、
888         * DBTableModelの値をチェックし、全てのキーに同じ値となっている行が存在すればエラーとなります。
889         * このチェックは、command="ENTRY"の場合のみ有効です。
890         * また、このチェックは他のチェック(DB存在チェックなど)と同時に処理することはできません。
891         * キーが指定され手いる場合は、ボディ部分に記述されている定義は無視されます。
892         * errRemoveの属性がtrueに指定されている場合、重複行は、DBTableModelの並び順から見て、
893         * 最初の行のみ処理され、2つめ以降の重複行は無視されます。
894         * なお、キーはCSV形式(CSV形式)で複数指定が可能です。
895         *
896         * @og.rev 4.3.4.0 (2008/12/01) 新規追加
897         *
898         * @param       clm チェックキー(CSV形式)
899         */
900        public void setUniqCheckClms( final String clm ) {
901                final String tmp = nval( getRequestParameter( clm ),null );
902                uniqCheckClms = StringUtil.csv2Array( tmp );
903        }
904
905        /**
906         * 【TAG】エラーが発生した際に、エラーメッセージの表示前にincludeするJSPを指定します。
907         *
908         * @og.tag
909         * エラーが発生した際に、エラーメッセージの表示前にincludeするJSPを指定します。
910         * エラーが発生していない場合は、ここで指定されたJSPが処理されることはありません。
911         * 通常は、戻るリンクなどを指定します。
912         *
913         * 指定の方法は、相対パス、絶対パスの両方で指定することができます。
914         * 但し、絶対パスで指定した場合、その基点は、コンテキストのルートディレクトリになります。
915         * 例) beforeErrorJsp = "/jsp/common/history_back.jsp"
916         *
917         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
918         *
919         * @param jsp 表示前にincludeするJSPファイル名
920         */
921        public void setBeforeErrorJsp( final String jsp ) {
922                beforeErrorJsp = nval( getRequestParameter( jsp ),beforeErrorJsp );
923        }
924
925        /**
926         * 【TAG】エラーが発生した際に、エラーメッセージの表示後にincludeするJSPを指定します。
927         *
928         * @og.tag
929         * エラーが発生した際に、エラーメッセージの表示前にincludeするJSPを指定します。
930         * エラーが発生していない場合は、ここで指定されたJSPが処理されることはありません。
931         *
932         * 指定の方法は、相対パス、絶対パスの両方で指定することができます。
933         * 但し、絶対パスで指定した場合、その基点は、コンテキストのルートディレクトリになります。
934         * 例) afterErrorJsp = "/jsp/common/history_back.jsp"
935         *
936         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
937         *
938         * @param jsp 表示後にincludeするJSPファイル名
939         */
940        public void setAfterErrorJsp( final String jsp ) {
941                afterErrorJsp = nval( getRequestParameter( jsp ),afterErrorJsp );
942        }
943
944        /**
945         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
946         *
947         * @og.tag
948         * 全てのデータを選択済みデータとして扱って処理します。
949         * 全件処理する場合に、(true/false)を指定します。
950         * 初期値は false です。
951         *
952         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
953         *
954         * @param  all 選択済み処理可否 [true:全件選択済み/false:通常]
955         */
956        public void setSelectedAll( final String all ) {
957                selectedAll = nval( getRequestParameter( all ),selectedAll );
958        }
959
960        /**
961         * 【TAG】チェックするデータベース名(from 句)を指定します。
962         *
963         * @og.tag
964         * これは、tableExist タグ廃止に伴う便利機能で、通常、BODYに記述された
965         * SELECT count(*) from XXXX where XXXXX で、チェックしますが、
966         * from 属性 と、where 属性を指定する事で、内部で、チェック用のSQL文を
967         * 作成します。
968         * from が指定された場合は、BODY は無視されますので、ご注意ください。
969         *
970         * @og.rev 5.7.6.2 (2014/05/16) 新規追加
971         *
972         * @param  frm チェックするテーブルID
973         */
974        public void setFrom( final String frm ) {
975                from = nval( getRequestParameter( frm ),from );
976        }
977
978        /**
979         * 【TAG】チェックする検索条件(where句)を指定します。
980         *
981         * @og.tag
982         * これは、tableExist タグ廃止に伴う便利機能で、通常、BODYに記述された
983         * SELECT count(*) from XXXX where XXXXX で、チェックしますが、
984         * from 属性 と、where 属性を指定する事で、内部で、チェック用のSQL文を
985         * 作成します。
986         * where は、from が指定された場合のみ、有効ですし、where を指定しなければ、
987         * 全件検索になります。
988         * tableExist タグと異なるのは、where の指定の仕方で、tableExist タグでは、
989         * names 属性と、対応する where には、? で記述していましたが、
990         * dataCheck タグでは、通常の [] でDBTableModelの値を指定します。
991         *
992         * @og.rev 5.7.6.2 (2014/05/16) 新規追加
993         *
994         * @param  whr チェックするWHERE条件
995         */
996        public void setWhere( final String whr ) {
997                where = nval( getRequestParameter( whr ),where );
998        }
999
1000        /**
1001         * 指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。
1002         *
1003         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
1004         * 処理の対象とします。
1005         *
1006         * @og.rev 4.2.0.1 (2008/03/27) row と clm を入れ替えます。(他とあわせます)
1007         *
1008         * @param       row   行番号
1009         * @param       clmNo カラムNo配列(可変長引数)
1010         *
1011         * @return      行番号とカラムNo配列に対応した、値の配列
1012         */
1013        private String[] getTableModelData( final int row, final int... clmNo ) {
1014                String[] values = new String[clmNo.length];
1015                for( int i=0; i<values.length; i++ ) {
1016                        values[i] = table.getValue( row, clmNo[i] );
1017                }
1018                return values;
1019        }
1020
1021        /**
1022         * エラーメッセージの前後に処理するJSPをインクルードします。
1023         *
1024         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
1025         *
1026         * @param jsp JSP名
1027         */
1028        private void includeJsp( final String jsp ) {
1029                try {
1030                        pageContext.include( jsp, false );
1031                } catch( final IOException ex ) {
1032                        final String errMsg = jsp + " の include に失敗しました。 ";
1033                        throw new HybsSystemException( errMsg,ex );
1034                } catch( final ServletException ex ) {
1035                        final String errMsg = jsp + " の include に失敗しました。 ";
1036                        throw new HybsSystemException( errMsg,ex );
1037                }
1038        }
1039
1040        /**
1041         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。
1042         *
1043         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
1044         *
1045         * @return      選択行の配列
1046         * @og.rtnNotNull
1047         */
1048        @Override
1049        protected int[] getParameterRows() {
1050                final int[] rowNo ;
1051                if( selectedAll ) {
1052                        final int rowCnt = table.getRowCount();
1053                        rowNo = new int[ rowCnt ];
1054                        for( int i=0; i<rowCnt; i++ ) {
1055                                rowNo[i] = i;
1056                        }
1057                } else {
1058                        rowNo = super.getParameterRows();
1059                }
1060                return rowNo ;
1061        }
1062
1063        /**
1064         * ErrMessage を管理している メソッド集約型内部クラス
1065         *
1066         * 繰返し処理部と、固定部が混在したエラーメッセージで、固定部を先に処理し、
1067         * 繰返し部は、必要時に処理するようにしました。
1068         * また、実際にエラーが発生して必要になるまで、実行遅延させます。
1069         *
1070         * @og.rev 4.2.1.0 (2008/04/11) 新規追加
1071         * @og.rev 4.3.0.0 (2008/07/24) クラス宣言をstatic化
1072         */
1073        private static final class ErrMessageManager {
1074                // 引数として初期設定される変数
1075                private ResourceManager resource;
1076                private DBTableModel    table   ;
1077                private String          title           ;
1078                private String          from            ;
1079                private String[]        lblKeys         ;
1080                private int[]           clmNo           ;
1081
1082                // 内部引数として処理されたキャッシュ値
1083                private ErrorMessage errMessage ;
1084                private String          names           ;
1085                private String          fromLbl         ;
1086                private String[]        lblVals         ;
1087
1088                private boolean isFirst  = true;                // 初期化されていない=true
1089
1090                /**
1091                 * ErrMessage のタイトルを設定します。
1092                 *
1093                 * @param       title   タイトル
1094                 */
1095                public void setTitle( final String title ) { this.title = title; }
1096
1097                /**
1098                 * 処理対象のテーブル名を設定します。
1099                 *
1100                 * @param       from    テーブル名
1101                 */
1102                public void setFrom( final String from ) { this.from = from; }
1103
1104                /**
1105                 * 処理対象のテーブルオブジェクトを設定します。
1106                 *
1107                 * @param table DBTableModelオブジェクト
1108                 */
1109                public void setDBTableModel( final DBTableModel table ) { this.table = table; }
1110
1111                /**
1112                 * ResourceManagerオブジェクトを設定します。
1113                 *
1114                 * @param resource ResourceManagerオブジェクト
1115                 */
1116                public void setResourceManager( final ResourceManager resource ) { this.resource = resource; }
1117
1118                /**
1119                 * lblParamKeys 属性の配列を設定します。
1120                 *
1121                 * @param       lblKeys 属性の配列(可変長引数)
1122                 */
1123                public void setParamKeys( final String... lblKeys ) { this.lblKeys = lblKeys; }
1124
1125                /**
1126                 * カラム名列を設定します。
1127                 *
1128                 * @param       clmNo   カラム名配列(可変長引数)
1129                 */
1130                public void setClmNos( final int... clmNo ) { this.clmNo = clmNo ; }
1131
1132                /**
1133                 * 初期処理を行います。
1134                 * エラー処理は、エラー時のみ実行する為、必要のない場合は、処理が不要です。
1135                 * 最初のエラー出力までは、内部オブジェクトの構築処理を行いません。
1136                 * 2回目以降は、内部変数にキャッシュされた変換値を利用して、高速化します。
1137                 *
1138                 * @og.rev 4.2.3.2 (2008/06/20) from が、null なら、なにもしない。
1139                 */
1140                private void firstExecute() {
1141                        errMessage = new ErrorMessage( title );
1142
1143                        // テーブル(from) をキーにラベルリソースから値を取得します。
1144                        // 4.2.3.2 (2008/06/20) from が、null なら、なにもしない。
1145                        if( from != null ) {
1146                                fromLbl  = resource.getLabel( from );
1147                        }
1148
1149                        // カラム番号配列から、カラム名のラベルリソース情報のCSV文字列を作成します。
1150                        names = getKeysLabel( clmNo );
1151
1152                        if( lblKeys != null && lblKeys.length > 0 ) {
1153                                final int size = lblKeys.length;
1154                                lblVals = new String[size] ;
1155
1156                                for( int i=0; i<size; i++ ) {
1157                                        final String key = lblKeys[i] ;
1158                                        if( key != null ) {
1159                                                if(              "@KEY".equals( key ) ) { lblVals[i] = names;   }
1160                                                else if( "@TBL".equals( key ) ) { lblVals[i] = fromLbl;}
1161                                                else if( key.startsWith( "{#" ) && key.endsWith( "}" )  ) {
1162                                                        lblVals[i] = resource.getLabel( key.substring( 2,key.length()-1 ));
1163                                                }
1164                                                else {
1165                                                        lblVals[i] = key;
1166                                                }
1167                                        }
1168                                }
1169                        }
1170                }
1171
1172                /**
1173                 * カラムNo配列(int[])に対応したカラム名のメッセージリソース文字列を返します。
1174                 *
1175                 * @param       clmNo カラムNo配列(可変長引数)
1176                 * @return      カラムNo配列に対応した、カラム名のメッセージリソース
1177                 * @og.rtnNotNull
1178                 */
1179                private String getKeysLabel( final int... clmNo ) {
1180                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
1181                        if( table != null && clmNo.length > 0 ) {
1182                                String key = table.getColumnName( clmNo[0] );
1183                                buf.append( resource.getLabel( key ) );
1184                                for( int i=1; i<clmNo.length; i++ ) {
1185                                        key = table.getColumnName( clmNo[i] );
1186                                        buf.append( ',' ).append( resource.getLabel( key ) );           // 6.0.2.5 (2014/10/31) char を append する。
1187                                }
1188                        }
1189
1190                        return buf.toString();
1191                }
1192
1193                /**
1194                 * カラム名列を設定します。
1195                 *
1196                 * @og.rev 4.3.5.7 (2008/03/22) エラーメッセージの行番号を実際の行番号と一致させる。
1197                 *
1198                 * @param       row     カラム名
1199                 * @param       id      カラム名
1200                 * @param       values  指定の行に対する値配列(可変長引数)
1201                 */
1202                public void addMessage( final int row, final String id, final String... values ) {
1203                        if( isFirst ) { firstExecute(); isFirst = false; }
1204
1205                        final String vals = StringUtil.array2csv( values );
1206                        if( lblVals == null ) {
1207                                errMessage.addMessage( row + 1, ErrorMessage.NG, id, names, vals, fromLbl );
1208                        }
1209                        else {
1210                                final int size = lblKeys.length;
1211                                String[] args = new String[size] ;
1212
1213                                for( int i=0; i<size; i++ ) {
1214                                        final String key = lblVals[i] ;
1215                                        if( key != null ) {
1216                                                if(              "@VAL".equals( key ) ) { args[i] = vals; }
1217                                                else if( StringUtil.startsChar( key,'[' ) && key.endsWith( "]" )  ) {           // 6.4.1.1 (2016/01/16) 1文字 String.startsWith
1218                                                        if( table != null ) {
1219                                                                args[i] = table.getValue( row,key.substring( 1,key.length()-1 ) );
1220                                                        }
1221                                                }
1222                                                else {
1223                                                        args[i] = key;
1224                                                }
1225                                        }
1226                                }
1227                                errMessage.addMessage( row + 1, ErrorMessage.NG, id, args );
1228                        }
1229                }
1230
1231                /**
1232                 * ErrorMessageオブジェクトを返します。
1233                 *
1234                 * @return ErrorMessageオブジェクト
1235                 */
1236                public ErrorMessage getErrMessage() { return errMessage; }
1237        }
1238
1239        /**
1240         * このオブジェクトの文字列表現を返します。
1241         * 基本的にデバッグ目的に使用します。
1242         *
1243         * @return このクラスの文字列表現
1244         * @og.rtnNotNull
1245         */
1246        @Override
1247        public String toString() {
1248                return ToString.title(this.getClass().getName() )
1249                .println( "VERSION", VERSION )
1250                .println( "tableId", tableId )
1251                .println( "dbid", dbid )
1252                .println( "command", command )
1253                .println( "exist", exist )
1254                .println( "lbl", lbl )
1255                .println( "Other...", getAttributes().getAttribute() ).fixForm().toString();
1256        }
1257}