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.Arrays;
019import java.util.Enumeration;
020import java.util.Locale;
021import java.util.Set;
022import java.util.TreeSet;
023
024import org.opengion.fukurou.util.ErrorMessage;
025import org.opengion.fukurou.util.StringUtil;
026import org.opengion.hayabusa.common.HybsSystem;
027import org.opengion.hayabusa.common.HybsSystemException;
028import org.opengion.hayabusa.db.DBColumn;
029import org.opengion.hayabusa.db.DBTableModel;
030import org.opengion.hayabusa.resource.ResourceManager;                  // 6.4.0.2 (2015/12/11)
031import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
032import static org.opengion.fukurou.util.StringUtil.nval;
033
034/**
035 * 登録データの物理チェック(整合性チェック、nullチェック)を行うタグです。
036 *
037 * コマンドに対応したチェック方式で、指定のカラムをチェックします。
038 * command="NEW" の場合は、columns 引数にCSV形式で指定したカラム名より
039 * リクエスト情報を取得して、値をチェックします。
040 * 引数チェックでは、"%" や "_" などのあいまい検索時に指定する記号を
041 * 含むとエラーになるカラムはチェック対象からはずす必要がある為、
042 * チェックすべきカラムを指定する必要があります。
043 * command="ENTRY" の場合は、columns 引数に無関係に、全てのリクエストされたカラム
044 * の値をチェックします。これは、先の検索時の場合と異なり、ENTRYでは
045 * データベースに値を設定する為、無条件にチェックする必要があります。
046 * nullCheck は、command に無関係に指定のカラムが null (ゼロ文字列)かどうかを
047 * 判定します。
048 * maxRowCount は、一覧検索時のチェックされた件数が、指定の値を超えた場合に
049 * エラーになります。minRowCount は、同様に、最小選択件数(設定値を含む)の指定です。
050 * このタグは、エラー時には、それ以降のJSP画面を評価しません。BODY 部に記述された
051 * 値は、エラー時のみ表示され、正常時には、なにも表示されません。これは、エラー時の
052 * メッセージや、ユーザーにエラー時のアクションを行ってもらう場合の処理(例えば、
053 * 画面を戻る為のボタンなど)を表示させます。
054 * useColumnLabel を true にセットすると、エラー時のカラム名(ラベル)を、カラムオブジェクトから
055 * 取得するようになります。動的カラム実行時や、DBTableModelに対して、ColumnEditor等で
056 * ラベルを書き換えた場合に、有効です。false の場合は、基本的にはラベルリソースからの
057 * 取得になりますが、ケースによっては、カラムオブジェクトから取得している場合もあります。
058 * この属性は、互換性を重視し、初期値が、false になっています。
059 *
060 * ERR0012 : 指定のデータがセットされていません。(NULLエラー)。key={0}
061 * ERR0017 : 選択エラー。選択行数({0} 件)が、最大制限値({1} 件)以上選ばれました。
062 * ERR0018 : 選択エラー。選択行数({0} 件)が、最小制限値({1} 件)以下選ばれました。
063 * ERR0036 : 選択必須エラー。以下のデータの内どれかは入力してください。key={0}
064 * ERR0037 : データ整合性エラー。指定のキーは整合性チェックの結果マッチしませんでした。key={0} val={1} regex={2}
065 *
066 * command="MODIFY" , "DELETE" 時には、強制的に、minRowCount="1" で処理します。
067 *
068 * @og.formSample
069 * ●形式:<og:columnCheck command = "…" />
070 * ●形式:<og:columnCheck command = "…" >エラー時のみ処理 </og:columnCheck>
071 * ●body:なし/あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
072 *
073 * ●Tag定義:
074 *   <og:columnCheck
075 *       command            【TAG】コマンド[NEW/RENEW/ENTRY]をセットします(初期値:NEW)
076 *       columns            【TAG】チェックすべきカラム列をCSV形式(CSV形式)で指定します
077 *       nullCheck          【TAG】NULL チェックすべきカラム列をCSV形式(CSV形式)りで指定します
078 *       mustAnyCheck       【TAG】選択必須カラム(指定のカラムの内最低ひとつがNULLでない)を"AA|BB|CC,XX|YY|ZZ" 形式で指定します
079 *       checkType          【TAG】チェック対象のデータ[AUTO/NEW/ENTRY]を指定します(初期値:AUTO)
080 *       maxRowCount        【TAG】ENTRY時にチェックで選択された行数の最大値を設定します
081 *       minRowCount        【TAG】ENTRY時にチェックで選択された行数の最小値を設定します
082 *       tableId            【TAG】(通常は使いません) sessionに登録されている DBTableModel を取り出すキーを指定します
083 *       matchKeys          【TAG】正規表現でのマッチングを行うカラム列をCSV形式(CSV形式)で指定します
084 *       matchVals          【TAG】正規表現でのマッチングを行うカラム列に対する値(正規表現)をCSV形式(CSV形式)で指定します
085 *       realTime           【TAG】(通常は使いません)リアルタイムチェックを行う場合に有効にします(初期値:false)
086 *       useStrict          【TAG】NEWの場合に、厳密にチェックするかどうか[true/false]を指定します(初期値:false)
087 *       useColumnLabel     【TAG】カラムラベルを使用するかどうか[true/false]を指定します(初期値:false)
088 *       checkNames         【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します
089 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
090 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
091 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
092 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
093 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
094 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
095 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
096 *   >   ... Body ...
097 *   </og:columnCheck>
098 *
099 * ●使用例
100 * <og:columnCheck
101 *     command = "{@command}"
102 *     columns = "AAA,BBB,CCC"  DB定義(DBColumnリソース)で定義した項目(桁数,タイプ等)でチェックします。
103 *     maxRowCount = "1"        チェックで選ばれた 最大選択件数(設定値を含む)を指定。
104 *     minRowCount = "1"        チェックで選ばれた 最小選択件数(設定値を含む)を指定。
105 *     nullCheck = "AAA,CCC"    NULLチェックを実行します。
106 * />
107 *
108 * [エラー時に、BODY部に記述された内容を出力する。正常時には、このBODY部の記述は出力されません。]
109 * <og:columnCheck
110 *     command = "{@command}"
111 *     columns = "AAA,BBB,CCC"      DB定義(DBColumnリソース)で定義した項目(桁数,タイプ等)でチェックします。
112 *     nullCheck = "AAA,CCC"        NULLチェックを実行します。
113 * >
114 *     <form method="POST" action="forward.jsp" target="RESULT">
115 *         <og:input type="button" onClick="history.back()" msg="MSG0049" accesskey="R" td="false" />
116 *     </form>
117 * </og:columnCheck>
118 *
119 * mustAnyCheck 属性に、選択必須カラムを指定します。
120 *      例:mustAnyCheck="AA|BB|CC"
121 *          AA,BB,CC のカラムで選択必須(すべてがnullならエラー)
122 *      例:mustAnyCheck="AA|BB|CC,XX|YY|ZZ"
123 *          AA,BB,CC のセットと、XX,YY,ZZのセットでそれぞれ選択必須。
124 *      例:mustAnyCheck="AA|XX,AA|YY,AA|ZZ"
125 *          AA に値があればすべて成立。そうでない場合は、XX と YY と ZZ がすべて必須。
126 *      例:mustAnyCheck="AA|BB,BB|CC,AA|CC"
127 *          AA,BB,CC の内、どれか2つが必須。AAが成立すればBBかCCが必須。同様に、
128 *          BBが成立すれば、AAかCCが必須。
129 *
130 *  例:query.jsp
131 *        <og:column name="AA" mustAny="true" />
132 *        <og:column name="BB" mustAny="true" />
133 *        <og:column name="XX" mustAny="XYZ"  />
134 *        <og:column name="YY" mustAny="XYZ"  />
135 *      result.jsp
136 *        <og:columnCheck mustAnyCheck="AA|BB,XX|YY" />
137 *      custom/custom.css
138 *          .XYZ { background-color: Green; }
139 *
140 *  例:動的カラムで、entry.jsp でのチェックを行う場合。
141 *     entry.jsp
142 *        <og:columnCheck command="NEW" columns="*" useColumnLabel="true" />
143 *
144 *     useColumnLabel で、動的に作成されたカラム(SAVE=TRUEでキャッシュ済み)を使って
145 *     チェックします。command="NEW" なので、対象カラムを指定するのに、"*" ですべてを選択します。
146 *
147 * @og.group 画面登録
148 *
149 * @version  4.0
150 * @author   Kazuhiko Hasegawa
151 * @since    JDK5.0,
152 */
153public class ColumnCheckTag extends CommonTagSupport {
154        /** このプログラムのVERSION文字列を設定します。   {@value} */
155        private static final String VERSION = "7.0.7.0 (2019/12/13)" ;
156        private static final long serialVersionUID = 707020191213L ;
157
158        /** command 引数に渡す事の出来る コマンド  新規 {@value} */
159        public static final String CMD_NEW      = "NEW" ;
160        /** command 引数に渡す事の出来る コマンド  再検索 {@value}  */
161        public static final String CMD_RENEW = "RENEW" ;
162        /** command 引数に渡す事の出来る コマンド  エントリー {@value} */
163        public static final String CMD_ENTRY   = "ENTRY" ;
164        /** command 引数に渡す事の出来る コマンド リスト  */
165
166        // 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
167        // 4.3.1.1 (2008/08/23) transient 追加
168
169        private String          tableId         = HybsSystem.TBL_MDL_KEY;               // 3.5.4.3 (2004/01/05)
170        private String          command         = CMD_NEW;      // 無指定時は、チェックを行う。
171        private String[]        columns         ;
172        private String[]        nullCheck       ;
173        private String[]        mustAnyCheck;                   // 3.8.0.9 (2005/10/17)
174        private int                     maxRowCount     = -1 ;          // 初期値として,無制限を指定
175        private int                     minRowCount     = -1 ;          // 初期値として,無制限を指定
176        private String          checkType       = "AUTO";
177        private String          bodyString      ;                       // 3.5.4.2 (2003/12/15)
178        private String[]        matchKeys       ;                       // 4.0.0 (2005/11/30)
179        private String[]        matchVals       ;                       // 4.0.0 (2005/11/30)
180        private boolean         isRealTime      ;                       // 4.3.7.0 (2009/06/01)
181        private boolean         isStrict        ;                       // 5.2.2.0 (2010/11/01) NEWの場合に、厳密にチェックするフラグを追加
182
183        private boolean         isColumnLabel;                  // 5.4.3.4 (2012/01/12) true の場合に、カラムラベルを使用します。
184
185        private String          checkNames      ;                       // 5.4.3.8 (2012/01/24)
186
187        private boolean         useSLabel       ;                       // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
188
189        /**
190         * デフォルトコンストラクター
191         *
192         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
193         */
194        public ColumnCheckTag() { super(); }            // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
195
196        /**
197         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
198         *
199         * @og.rev 3.5.4.2 (2003/12/15) エラー時に、BODY部に記述された内容を出力する。
200         * @og.rev 5.0.0.2 (2009/09/15) xss対応⇒チェックする
201         * @og.rev 5.7.8.0 (2014/07/04) caseKey,caseVal,caseNN,caseNull 属性を追加
202         *
203         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
204         */
205        @Override
206        public int doStartTag() {
207                // 5.0.0.2 (2009/09/15) XSSチェックしない⇒する
208                // useXssCheck( false );
209
210                // 5.7.8.0 (2014/07/04) 追加
211                return useTag() ? EVAL_BODY_BUFFERED : SKIP_BODY ;
212        }
213
214        /**
215         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
216         *
217         * @og.rev 3.5.4.2 (2003/12/15) エラー時に、BODY部に記述された内容を出力する。
218         *
219         * @return      後続処理の指示(SKIP_BODY)
220         */
221        @Override
222        public int doAfterBody() {
223                bodyString = getBodyString();
224
225                return SKIP_BODY ;
226        }
227
228        /**
229         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
230         *
231         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
232         * @og.rev 3.4.0.3 (2003/09/10) command="NEW" でエラー発生時には、DBTableModel をクリアする。
233         * @og.rev 3.5.4.2 (2003/12/15) HTMLTableViewForm クラス名変更(⇒ ViewForm_HTMLTable)
234         * @og.rev 3.5.4.2 (2003/12/15) エラー時に、BODY部に記述された内容を出力する。
235         * @og.rev 3.5.4.3 (2004/01/05) tableId 属性を追加。
236         * @og.rev 3.5.4.4 (2004/01/16) エラー結果を表示するテーブル形式のフォーム修正
237         * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
238         * @og.rev 4.1.2.1 (2008/03/13) table 属性を追加。
239         * @og.rev 4.3.3.0 (2008/10/01) viewSimple属性追加
240         * @og.rev 4.3.7.0 (2009/06/01) viewSimple属性名称変更 ⇒ isRealTime
241         * @og.rev 5.6.8.3 (2013/09/27) checkTypeがAutoの場合のセットをここで行い、tableModelチェックをcheckTypeで行う。
242         * @og.rev 5.7.8.0 (2014/07/04) caseKey,caseVal,caseNN,caseNull 属性を追加
243         * @og.rev 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
244         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
245         *
246         * @return      後続処理の指示
247         */
248        @Override
249        public int doEndTag() {
250                debugPrint();           // 4.0.0 (2005/02/28)
251
252                // 5.7.8.0 (2014/07/04) caseKey,caseVal,caseNN,caseNull 属性を追加
253                if( !useTag() ) { return EVAL_PAGE ; }          // ページの残りを評価する。
254
255                int rtnCode = EVAL_PAGE;
256
257                // 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
258
259                // 5.6.8.3 (2013/09/27) チェックタイプのautoを入れる位置変更
260                if( "AUTO".equals( checkType ) ) { checkType = command; }
261
262                // 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
263                        // 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
264                        final ErrorMessage errMsg = makeErrorDBTable();
265                        if( errMsg.getKekka() >= ErrorMessage.NG ) {
266                                if( bodyString != null ) { jspPrint( bodyString ); }    // 3.5.4.2 (2003/12/15)
267
268                                // 4.3.3.0 (2008/10/01)
269                                // 4.3.7.0 (2009/06/01)
270                                // 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
271//                              if( isRealTime ) { jspPrint( TaglibUtil.makeHTMLSimpleErrorList( errMsg, getResource() ) ); }
272//                              else {             jspPrint( TaglibUtil.makeHTMLErrorTable(      errMsg, getResource() ) ); }
273                                if( isRealTime ) { jspPrint( TaglibUtil.makeHTMLSimpleErrorList( errMsg, getResource(),useSLabel ) ); }
274                                else {             jspPrint( TaglibUtil.makeHTMLErrorTable(      errMsg, getResource(),useSLabel ) ); }
275
276                                if( CMD_NEW.equals( command ) ) {
277                                        removeObject( tableId );
278                                }
279                                rtnCode = SKIP_PAGE ;
280                        }
281                        else if( errMsg.getKekka() == ErrorMessage.WARNING ) {
282                                // 4.3.3.0 (2008/10/01)
283                                // 4.3.7.0 (2009/06/01)
284                                // 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
285//                              if( isRealTime ) { jspPrint( TaglibUtil.makeHTMLSimpleErrorList( errMsg, getResource() ) ); }
286//                              else {             jspPrint( TaglibUtil.makeHTMLErrorTable(      errMsg, getResource() ) ); }
287                                if( isRealTime ) { jspPrint( TaglibUtil.makeHTMLSimpleErrorList( errMsg, getResource(),useSLabel ) ); }
288                                else {             jspPrint( TaglibUtil.makeHTMLErrorTable(      errMsg, getResource(),useSLabel ) ); }
289                        }
290
291                return rtnCode ;
292        }
293
294        /**
295         * タグリブオブジェクトをリリースします。
296         *
297         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
298         *
299         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
300         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
301         * @og.rev 3.5.4.2 (2003/12/15) エラー時に、BODY部に記述された内容を出力する。
302         * @og.rev 3.5.4.3 (2004/01/05) tableId 属性を追加。
303         * @og.rev 3.8.0.9 (2005/10/17) mustAnyCheck 属性を追加。
304         * @og.rev 4.1.2.1 (2008/03/13) table 属性を追加。
305         * @og.rev 4.3.3.0 (2008/10/01) viewSimple属性追加
306         * @og.rev 4.3.7.0 (2009/06/01) viewSimple属性名称変更 ⇒ isRealTime
307         * @og.rev 5.2.2.0 (2010/11/01) NEWの場合に、厳密にチェックするフラグを追加
308         * @og.rev 5.4.3.4 (2012/01/12) isColumnLabelを追加
309         * @og.rev 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
310         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
311         */
312        @Override
313        protected void release2() {
314                super.release2();
315                command         = CMD_NEW;      // 無指定時は、チェックを行う。
316                columns         = null;
317                nullCheck       = null;
318                maxRowCount     = -1 ;          // 初期値として,無制限を指定
319                minRowCount     = -1 ;          // 初期値として,無制限を指定
320                checkType       = "AUTO";
321                bodyString      = null ;        // 3.5.4.2 (2003/12/15)
322                tableId         = HybsSystem.TBL_MDL_KEY;               // 3.5.4.3 (2004/01/05)
323                mustAnyCheck= null;             // 3.8.0.9 (2005/10/17)
324                matchKeys       = null;         // 4.0.0 (2005/11/30)
325                matchVals       = null;         // 4.0.0 (2005/11/30)
326                isRealTime      = false;        // 4.3.7.0 (2009/06/01)
327                isStrict        = false;        // 5.2.2.0 (2010/11/01) NEWの場合に、厳密にチェックするフラグを追加
328                isColumnLabel= false;   // 5.4.3.4 (2012/01/12) true の場合に、カラムラベルを使用します。
329                checkNames      = null;         // 5.4.3.8 (2012/01/24)
330                useSLabel       = false;        // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
331        }
332
333        /**
334         * DBColumn オブジェクトを作成して、DBColumn#valueCheck( String )で
335         * チェックを行う。その結果の、ErrorMessage オブジェクトを DBTableModel に
336         * 取り込んで、エラーの場合は、その表示を行う。
337         * 表示方法は、そのまま ViewForm オブジェクトを作成して、表示を行う。
338         *
339         * @og.rev 3.5.5.2 (2004/04/02) リターン値を、DBTableModel から ErrorMessage に変更
340         * @og.rev 4.0.0.0 (2005/01/31) リターン値の ErrorMessage は、必ず存在する。
341         * @og.rev 4.1.2.1 (2008/03/13) command="MODIFY" , "DELETE" 時には、強制的に、minRowCount="1" で処理します。
342         * @og.rev 5.6.8.3 (2013/09/27) checkTypeのAuto時設定位置をdoEndTagでするように変更
343         *
344         * @return      エラーメッセージのテーブルモデル
345         * @og.rtnNotNull
346         */
347        private ErrorMessage makeErrorDBTable() {
348                ErrorMessage errMsg = new ErrorMessage();
349
350                //if( "AUTO".equals( checkType ) ) { checkType = command; } // 5.6.8.3 (2013/09/27) move
351                if( CMD_ENTRY.equals( checkType ) ) {
352                        errMsg.setTitle( "Request Column Error!" );
353                        errMsg = makeEntryErrorMessage( errMsg );
354                        errMsg = makeMaxRowCountErrorMessage( errMsg );
355                        errMsg = makeMinRowCountErrorMessage( errMsg );
356                }
357                else if( CMD_NEW.equals( checkType ) ) {
358                        errMsg.setTitle( "Entry Column Error!" );
359                        errMsg = makeErrorMessage( errMsg );
360                        errMsg = makeMaxRowCountErrorMessage( errMsg );
361                        errMsg = makeMinRowCountErrorMessage( errMsg );
362                }
363                else if( minRowCount >= 0 || maxRowCount >= 0) {        // -1 は制限無し
364                        errMsg.setTitle( "Row Count Error Limited Error!" );
365                        errMsg = makeMaxRowCountErrorMessage( errMsg );
366                        errMsg = makeMinRowCountErrorMessage( errMsg );
367                }
368                // 4.1.2.1 (2008/03/13) command="MODIFY" , "DELETE" 時には、強制的に、minRowCount="1" で処理します。
369                // 6.3.9.0 (2015/11/06) 条件は効果がない(findbugs)
370                else if( "MODIFY,DELETE".indexOf( checkType ) >= 0 ) {
371                        minRowCount = 1;
372                        errMsg = makeMinRowCountErrorMessage( errMsg );
373                }
374                return errMsg;
375        }
376
377        /**
378         * ErrorMessageをセットします。
379         * 引数のカラム名配列よりリクエスト情報を取得して、値をチェックします。
380         * DBColumn#valueCheck( String ) の結果のErrorMessageをすべて append
381         * していきます。
382         *
383         * @og.rev 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加
384         * @og.rev 3.8.1.0 (2005/10/24) リクエスト情報の正規化変換(DBColumn#valueSet(String))中止
385         * @og.rev 3.8.5.3 (2006/06/30) リクエストに % , _ が含まれたときは、削除します。
386         * @og.rev 4.1.2.1 (2008/03/13) must , mustAny の自動取得追加
387         * @og.rev 5.0.0.2 (2009/09/15) 個別にxssチェックをfalse
388         * @og.rev 5.1.9.0 (2010/08/01) 同じ名前の項目は、1つにまとめる(ラジオボタン等)
389         * @og.rev 5.2.0.0 (2010/09/01) リアルタイムチェックの場合は、must,mustAnyの自動チェックは行わない
390         * @og.rev 5.2.2.0 (2010/11/01) DBColumn#valueCheck で、甘いチェックを行うように、引数に isStrict を追加
391         * @og.rev 5.4.3.4 (2012/01/12) getLabel( String )を、リソースからに統一するとともに、isColumnLabel 対応を行う。
392         * @og.rev 5.4.3.8 (2012/01/28) checkNames対応
393         * @og.rev 6.4.0.2 (2015/12/11) ResourceManagerの取得を、先に行っておく。
394         * @og.rev 6.8.4.2 (2017/12/25) カラムチェックで、#valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
395         *
396         * @param       errMsg  ErrorMessageオブジェクト
397         *
398         * @return      カラムキー + 値 のエラーメッセージオブジェクト
399         */
400        private ErrorMessage makeErrorMessage( final ErrorMessage errMsg ) {
401
402                // 4.1.2.1 (2008/03/13) must , mustAny の自動取得追加
403                // request から取出す 注意:mustAny 以外の自由形式の値は所得していません。
404                // 5.2.0.0 (2010/09/01) リアルタイムチェックの場合は、must,mustAnyの自動チェックは行わない
405                //  (基本的にパラメーターを送らない限り自動チェックは動かないが、RequestCacheにより動くことがある)
406                if( !isRealTime ) {
407                        if( nullCheck == null ) {
408                                nullCheck = getRequestValues( HybsSystem.MUST_KEY + "must" );
409                                // 5.1.9.0 (2010/08/01) 同じ名前の項目は、1つにまとめる(ラジオボタン等)
410                                if( nullCheck != null ) {
411                                        final Set<String> ss = new TreeSet<>();
412                                        ss.addAll( Arrays.asList( nullCheck ) );
413                                        nullCheck = ss.toArray( new String[ss.size()] );                // 5.1.9.0 (2010/08/01) K.H
414                                }
415                        }
416                        if( mustAnyCheck == null ) {
417                                final String[] mustAnyReq = getRequestValues( HybsSystem.MUST_KEY + "mustAny" );
418                                if( mustAnyReq != null && mustAnyReq.length > 0 ) {
419                                        mustAnyCheck = new String[] { StringUtil.array2line( mustAnyReq,"|" ) };
420                                }
421                        }
422                }
423
424                // 6.4.0.2 (2015/12/11) ResourceManagerの取得を、先に行っておく。
425                final ResourceManager resource = getResource();
426
427                if( nullCheck != null && nullCheck.length != 0 ) {
428                        for( int i=0; i<nullCheck.length; i++ ) {
429                                final String clmKey = nullCheck[i];             // 5.4.3.4 (2012/01/12)
430                                // String val  = getRequestValue( nullCheck[i] );
431                                final String val  = getRequestValue( clmKey, false ); // 5.0.0.2 (2009/09/15)
432                                if( val == null || val.isEmpty() ) {
433                                        // 5.4.3.4 (2012/01/12) isColumnLabel 追加
434                                        final String label = isColumnLabel ? getDBColumn( clmKey ).getLabel() : resource.getLabel( clmKey );
435                                        // ERR0012 : 指定のデータがセットされていません。(NULLエラー)。key={0}
436                                        errMsg.addMessage( 0,ErrorMessage.NG,"ERR0012",label );
437                                }
438                        }
439                }
440                // 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加
441                if( mustAnyCheck != null && mustAnyCheck.length != 0 ) {
442                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );           // 6.1.0.0 (2014/12/26) refactoring
443                        for( int i=0; i<mustAnyCheck.length; i++ ) {
444                                boolean flag = false;
445                                final String[] mustSub = StringUtil.csv2Array( mustAnyCheck[i],'|' );
446                                for( int j=0; j<mustSub.length; j++ ) {
447                                        // String val  = getRequestValue( mustSub[j] );
448                                        final String val  = getRequestValue( mustSub[j], false ); // 5.0.0.2 (2009/09/15)
449                                        if( val != null && val.length() > 0 ) {
450                                                flag = true; break;             // ひとつでもnullでなければ、OK
451                                        }
452                                }
453                                if( ! flag ) {
454                                        buf.setLength(0);                                                       // 6.1.0.0 (2014/12/26) refactoring
455                                        for( int j=0; j<mustSub.length; j++ ) {
456                                                final String clmKey = mustSub[j];               // 5.4.3.4 (2012/01/12)
457                                                // 5.4.3.4 (2012/01/12) isColumnLabel 追加
458                                                final String label = isColumnLabel ? getDBColumn( clmKey ).getLabel() : resource.getLabel( clmKey );
459                                                buf.append( label ).append( ',' );              // 6.0.2.5 (2014/10/31) char を append する。
460                                        }
461                                        // ERR0036 : 選択必須エラー。以下のデータの内どれかは入力してください。key={0}
462                                        errMsg.addMessage( 0,ErrorMessage.NG,"ERR0036",buf.toString() );
463                                }
464                        }
465                }
466                if( columns != null && columns.length != 0 ) {
467                        for( int i=0; i<columns.length; i++ ) {
468                                // 3.8.1.0 (2005/10/24) リクエスト情報の正規化変換(DBColumn#valueSet(String))中止
469                                // String clmVal = getRequestValue( columns[i] );
470                                String clmVal = getRequestValue( columns[i], false ); // 5.0.0.2 (2009/09/15)
471                                if( clmVal != null && clmVal.length() > 0 ) {
472                                        clmVal = StringUtil.replace( clmVal,"%","" );           // 3.8.5.3 (2006/06/30)
473                                        clmVal = StringUtil.replace( clmVal,"_","" );           // 3.8.5.3 (2006/06/30)
474                                        final DBColumn dbColumn = getDBColumn( columns[i] );
475                                        // 5.4.3.8 (2012/01/24) checkNames対応
476                                        if( ( "," + checkNames + "," ).indexOf( "," + columns[i] + "," ) >= 0 ) {
477                                                // 6.8.4.2 (2017/12/25) カラムチェックで、#valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
478                                                try {
479                                                        final String val = dbColumn.valueSet( clmVal );
480                                                        if( val != null ) { clmVal = val; }
481                                                }
482                                                catch( RuntimeException ex ) {
483                                                        System.err.println( ex.getMessage() );  // 8.0.0.0 (2021/07/31)
484//                                                      ;       // #valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
485                                                }
486                                        }
487                                        // 5.2.2.0 (2010/11/01) DBColumn#valueCheck で、甘いチェックを行うように、引数に isStrict を追加
488                                        errMsg.append( dbColumn.valueCheck( clmVal,isStrict ) );
489                                }
490                        }
491                }
492                // 4.0.0 (2005/11/30) 正規表現チェックの追加
493                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
494//              if( matchKeys != null && matchKeys.length != 0 ) {
495                if( matchKeys != null && matchKeys.length > 0 && matchVals != null && matchVals.length > 0 ) {
496                        for( int i=0; i<matchKeys.length; i++ ) {
497                                final String clmKey = matchKeys[i];             // 5.4.3.4 (2012/01/12)
498                                // String val  = getRequestValue( matchKeys[i] );
499                                final String val  = getRequestValue( clmKey, false ); // 5.0.0.2 (2009/09/15)
500                                if( val != null && ! val.matches( matchVals[i] ) ) {
501                                        // 5.4.3.4 (2012/01/12) isColumnLabel 追加
502                                        final String label = isColumnLabel ? getDBColumn( clmKey ).getLabel() : resource.getLabel( clmKey );
503                                        // ERR0037 : データ整合性エラー。指定のキーは整合性チェックの結果マッチしませんでした。key={0} val={1} regex={2}
504                                        errMsg.addMessage( 0,ErrorMessage.NG,"ERR0037",label,val,matchVals[i] );
505                                }
506                        }
507                }
508
509                return errMsg;
510        }
511
512        /**
513         * エントリーデータのErrorMessageをセットします。
514         * 引数のカラム名配列よりエントリーデータ形式のリクエスト情報を取得して、
515         * 値をチェックします。
516         * DBColumn#valueCheck( String ) の結果のErrorMessageをすべて append
517         * していきます。
518         *
519         * @og.rev 3.1.0.0 (2003/03/20) 名前と行番号の区切り記号を "^" から "__" に変更。
520         * @og.rev 3.5.5.0 (2004/03/12) 名前と行番号の区切り記号("__")を、HybsSystem.JOINT_STRING  に変更。
521         * @og.rev 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加
522         * @og.rev 4.1.2.1 (2008/03/13) must , mustAny の自動取得追加
523         * @og.rev 4.3.6.4 (2009/05/01) 削除時に必ずmustAnyチェックエラーになるバグを修正(書込み可能行のみを処理する
524         * @og.rev 4.3.7.0 (2009/06/01) リアルタイムチェックの場合は、must,mustAnyの自動チェックは行わない
525         * @og.rev 5.0.0.2 (2009/09/15) xssチェック
526         * @og.rev 5.4.3.4 (2012/01/12) getLabel( String )を、リソースからに統一するとともに、isColumnLabel 対応を行う。
527         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
528         * @og.rev 6.4.0.2 (2015/12/11) ResourceManagerの取得を、先に行っておく。
529         * @og.rev 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
530         * @og.rev 6.8.4.2 (2017/12/25) カラムチェックで、#valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
531         * @og.rev 6.9.3.1 (2018/04/02) カラムチェックで、mustAnyのチェック方法改善
532         *
533         * @param       errMsg  ErrorMessageオブジェクト
534         *
535         * @return      カラムキー + 値 のエラーメッセージオブジェクト
536         */
537        private ErrorMessage makeEntryErrorMessage( final ErrorMessage errMsg ) {
538                final int[] rowNo = getParameterRows();
539                if( rowNo.length == 0 ) { return errMsg; }
540
541                // 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
542                final DBTableModel table = (DBTableModel)getObject( tableId );
543                if( table == null ) {
544                        final String errMsgStr = "検索結果のオブジェクトが存在しません。"
545                                                        + " checkType=[" + checkType + "] , tableId=[" + tableId + "]" ;
546                        throw new HybsSystemException( errMsgStr );
547                }
548
549                // 4.1.2.1 (2008/03/13) must , mustAny の自動取得追加
550                // table から取出す (ソート済み)
551                // 4.3.7.0 (2009/06/01) リアルタイムチェックの場合は、must,mustAnyの自動チェックは行わない
552                if( !isRealTime ) {
553                                if( nullCheck    == null ) { nullCheck    = table.getMustArray();        }
554                                if( mustAnyCheck == null ) { mustAnyCheck = table.getMustAnyArray(); }
555                }
556
557                // 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加
558                boolean[][] rowAnyOne = null;
559                // 4.3.6.4 (2009/05/01)
560                boolean[] rowForCheck = null;
561                if( mustAnyCheck != null && mustAnyCheck.length != 0 ) {
562                        rowAnyOne   = new boolean[rowNo.length][mustAnyCheck.length];
563                        rowForCheck = new boolean[rowNo.length];
564        //              for( int i=0; i<rowNo.length; i++ ) {
565        //                      Arrays.fill( rowAnyOne[i],false );
566        //              }
567                }
568
569                // 6.4.3.2 (2016/02/19) tableId 単位にチェックするが、互換性のため、標準の tableId の処理は、従来どおりとする。
570                final boolean useTid = ! HybsSystem.TBL_MDL_KEY.equals( tableId );              // 反転:tableId を使っている=標準のtableIdではない。
571
572                DBColumn dbColumn = null;       // 5.4.3.4 (2012/01/12) isColumnLabel 対応
573                final Enumeration<?> enume = getParameterNames();               // 4.3.3.6 (2008/11/15) Generics警告対応
574                while( enume.hasMoreElements() ) {
575                        final String key  = (String)(enume.nextElement());
576                        final int    idx  = key.lastIndexOf(HybsSystem.JOINT_STRING);
577
578                        if( idx > 0 ) {
579                                final String column = key.substring(0,idx);
580                                // 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
581                                final int clmNo = table.getColumnNo( column,false );    // エラーを出さない。
582                                if( useTid && clmNo < 0 ) { continue; }                                 // 6.4.3.2 (2016/02/19) tableId指定のテーブルのカラムに存在しない場合は、スルーする。
583
584                                final int    row    = Integer.parseInt( key.substring(idx + 2) );
585                                final String val    = getRequestValue( key, false );    // 5.0.0.2 (2009/09/15)
586
587                                final int i = Arrays.binarySearch( rowNo,row );
588                                if( i >= 0 ) {
589                                        // 5.4.3.4 (2012/01/12) isColumnLabel 対応
590                                        // 6.4.3.2 (2016/02/19) tableはnullチェック済みで、clmNoは先に求めている。
591                                        dbColumn = isColumnLabel && clmNo >= 0 ? table.getDBColumn( clmNo ) : getDBColumn( column );
592
593                                        // 6.8.4.2 (2017/12/25) カラムチェックで、#valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
594                                        String val2 = val;
595                                        try {
596                                                val2 = dbColumn.valueSet( val );
597                                        }
598                                        catch( RuntimeException ex ) {
599                                                System.err.println( ex.getMessage() );  // 8.0.0.0 (2021/07/31)
600//                                              ;       // #valueSet(String) で、エラーが発生した場合でも、#valueCheck(String) を行うようにします。
601                                        }
602
603                                        errMsg.append( row+1,dbColumn.valueCheck( val2 ) );
604
605                                        if( nullCheck != null && nullCheck.length != 0 ) {
606                                                final int j = Arrays.binarySearch( nullCheck,column );  // 6.9.3.1 (2018/04/02) リクエストカラムが、nullCheck対象カラムの場合、正のアドレス
607                                                if( j>=0 && ( val2 == null || val2.isEmpty() )) {
608                                                        // ERR0012 : 指定のデータがセットされていません。(NULLエラー)。key={0}
609                                                        errMsg.addMessage( row+1,ErrorMessage.NG,"ERR0012",dbColumn.getLabel() );
610                                                }
611                                        }
612
613                                        // 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加 (mustAnyCheck="AA|BB|CC,XX|YY|ZZ" という形式を、カンマで分解した配列)
614                                        if( rowAnyOne != null ) {
615                                                // 6.9.3.1 (2018/04/02) データが飛んでこないかどうかの判定
616                                                for( int j=0; j<mustAnyCheck.length; j++ ) {
617                                                        if( mustAnyCheck[j].indexOf( column ) >= 0 ) {
618                                                                if( val2 == null || val2.isEmpty() ) {
619                                                                        rowForCheck[i] = true;          // (trueは、処理対象)
620                                                                }
621                                                                else {
622                                                                        rowAnyOne[i][j] = true;         // どれかが存在(trueは、処理対象外)
623                                                                }
624                                                        }
625
626//                                                      if( !rowAnyOne[i][j] &&
627//                                                              mustAnyCheck[j].indexOf( column ) >= 0 &&
628//                                                              val2 != null && val2.length() > 0 ) {
629//                                                                      rowAnyOne[i][j] = true; // どれかが存在(trueは、処理対象外)
630//                                                      }
631                                                }
632//                                              // 4.3.6.4 (2009/05/01)
633//                                              rowForCheck[i] = true;          // (trueは、処理対象)
634                                        }
635
636                                        // 4.0.0 (2005/11/30) 正規表現チェックの追加
637                                        // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
638//                                      if( matchKeys != null && matchKeys.length != 0 ) {
639                                        if( matchKeys != null && matchKeys.length > 0 && matchVals != null && matchVals.length > 0 ) {
640                                                for( int j=0; j<matchKeys.length; j++ ) {
641                                                        if( column.equals( matchKeys[j] ) ) {
642                                                                if( val2 != null && ! val2.matches( matchVals[j] ) ) {
643                                                                        // 互換性はなくなっているが、正規表現チェックは余り使っていないので、統一しておく。
644                                                                        // 5.4.3.4 (2012/01/12) isColumnLabel 対応
645                                                                        // 6.4.3.2 (2016/02/19) dbColumnは、先に、取得済み。
646
647                                                                        // ERR0037 : データ整合性エラー。指定のキーは整合性チェックの結果マッチしませんでした。key={0} val={1} regex={2}
648                                                                        errMsg.addMessage( row+1,ErrorMessage.NG,"ERR0037",dbColumn.getLabel(),val2,matchVals[i] );
649                                                                }
650                                                                break;          // 発見すれば、ループ終了
651                                                        }
652                                                }
653                                        }
654                                }
655                        }
656                }
657
658                // 6.4.0.2 (2015/12/11) ResourceManagerの取得を、先に行っておく。
659                final ResourceManager resource = getResource();
660
661                // 3.8.0.9 (2005/10/17) 選択必須 mustAnyCheck のチェック追加
662                if( rowAnyOne != null ) {
663                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
664                        for( int row=0; row<rowAnyOne.length; row++ ) {
665                                // 4.3.6.4 (2009/05/01) 書込み可能行のみを処理する
666                                if( rowForCheck[row] ) {
667                                        for( int i=0; i<mustAnyCheck.length; i++ ) {
668                                                if( !rowAnyOne[row][i] ) {
669                                                        final String[] mustSub = StringUtil.csv2Array( mustAnyCheck[i], '|' );
670
671                                                        buf.setLength(0);                                                                                       // 6.1.0.0 (2014/12/26) refactoring
672                                                        buf.append( "row=[" ).append( rowNo[row]+1 ).append( "] " );
673                                                        for( int j=0; j<mustSub.length; j++ ) {
674                                                                // ここの処理だけ、他と異なるのは、互換性重視のため。(選択必須は利用頻度が高いので。)
675                                                                // 5.4.3.4 (2012/01/12) isColumnLabel 対応
676                                                                String label = null ;
677                                                                // 6.9.8.0 (2018/05/28) FindBugs:null でないことがわかっている値の冗長な null チェック
678//                                                              if( isColumnLabel && table != null ) {
679                                                                if( isColumnLabel ) {                                                                   // tableは、nullチェック済み
680                                                                        final int clmNo = table.getColumnNo( mustSub[j],false );        // エラーを出さない。
681                                                                        if( clmNo >= 0 ) {
682                                                                                dbColumn = table.getDBColumn( clmNo );
683                                                                        }
684                                                                        if( dbColumn == null ) {
685                                                                                dbColumn = getDBColumn( mustSub[j] );
686                                                                        }
687                                                                        label = dbColumn.getLabel();
688                                                                }
689                                                                else {
690                                                                        label = resource.getLabel( mustSub[j] );                        // 6.4.0.2 (2015/12/11) 互換性のための処置。
691                                                                }
692                                                                buf.append( label ).append( '|' );              // 6.0.2.5 (2014/10/31) char を append する。
693                                                        }
694                                                        // ERR0036 : 選択必須エラー。以下のデータの内どれかは入力してください。key={0}
695                                                        errMsg.addMessage( rowNo[row]+1, ErrorMessage.NG, "ERR0036", buf.toString() );
696                                                }
697                                        }
698                                }
699                        }
700                }
701
702                return errMsg;
703        }
704
705        /**
706         * ErrorMessageをセットします。
707         * リクエストされた件数の最大値に制限を加えます。
708         * 1件だけにしたい場合は,通常はViewでチェックボックスを使用せずに
709         * ラジオボタンを使用してください。
710         *
711         * @param       errMsg  ErrorMessageオブジェクト
712         *
713         * @return      カラムキー + 値 のエラーメッセージオブジェクト
714         */
715        private ErrorMessage makeMaxRowCountErrorMessage( final ErrorMessage errMsg ) {
716                if( maxRowCount < 0 ) { return errMsg; }        // 無制限
717
718                // 件数制限のチェック
719                final int[] rowNo = getParameterRows();
720                final int rowCount = rowNo.length ;
721                if( rowCount > maxRowCount ) {
722                        // ERR0017 : 選択エラー。選択行数({0} 件)が、最大制限値({1} 件)以上選ばれました。
723                        final String arg0 = String.valueOf( rowCount );
724                        final String arg1 = String.valueOf( maxRowCount );
725                        errMsg.addMessage( 0,ErrorMessage.NG,"ERR0017",arg0,arg1 );
726                }
727
728                return errMsg;
729        }
730
731        /**
732         * ErrorMessageをセットします。
733         * リクエストされた件数の最小値に制限を加えます。
734         * 1件だけにしたい場合は,通常はViewでチェックボックスを使用せずに
735         * ラジオボタンを使用してください。
736         *
737         * @param       errMsg  ErrorMessageオブジェクト
738         *
739         * @return      カラムキー + 値 のエラーメッセージオブジェクト
740         */
741        private ErrorMessage makeMinRowCountErrorMessage( final ErrorMessage errMsg ) {
742                if( minRowCount < 0 ) { return errMsg; }        // 無制限
743
744                // 件数制限のチェック
745                final int[] rowNo = getParameterRows();
746                final int rowCount = rowNo.length ;
747                if( rowCount < minRowCount ) {
748                        // ERR0018 : 選択エラー。選択行数({0} 件)が、最小制限値({1} 件)以下選ばれました。
749                        final String arg0 = String.valueOf( rowCount );
750                        final String arg1 = String.valueOf( minRowCount );
751                        errMsg.addMessage( 0,ErrorMessage.NG,"ERR0018",arg0,arg1 );
752                }
753
754                return errMsg;
755        }
756
757        /**
758         * 【TAG】チェックすべきカラム列をCSV形式(CSV形式)で指定します。
759         *
760         * @og.tag columns="AAA,BBB,CCC,DDD"
761         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
762         * "*" を指定すると、command="NEW" のときの判定にすべてのリクエスト文字列を
763         * チェック対象とします。通常は、必要分だけ指定しますが、動的カラムなどの
764         * 場合は、カラム名を予め指定できないため、"*" で指定できるようにします。
765         * その場合、"h_" , "hX_" , maxRowCount , command , pageSize , pagePlus , debug ,
766         * GAMENID , BACK_JSPID , BACK_GAMENID の予約語は、対象から除きます。
767         *
768         * ※ 6.4.0.2 (2015/12/11)
769         *    columns="*" のときのカラムは、リクエスト変数から、上記の予約語も除きますが、
770         *    ResourceManager に存在しない(リソース未登録)カラムも除きます。
771         *
772         * @og.rev 3.5.6.2 (2004/07/05) 先に配列に分解してからリクエスト変数の値を取得
773         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
774         * @og.rev 5.4.3.4 (2012/01/12) command="NEW" のときの動的カラム対応( "*" ですべてのリクエスト)
775         * @og.rev 6.4.0.2 (2015/12/11) columns="*" のときのカラムは、リソースに存在する分のみとする。
776         *
777         * @param   clms カラム列(CSV形式)
778         */
779        public void setColumns( final String clms ) {
780                columns = StringUtil.csv2Array( getRequestParameter( clms ) );
781                if( columns.length == 0 ) { columns = null; }
782
783                // 5.4.3.4 (2012/01/12) command="NEW" のときの動的カラム対応( "*" ですべてのリクエスト)
784                if( columns != null && "*".equals( columns[0] ) ) {
785                        final Set<String> clmSet = new TreeSet<>();             // リクエストはバラバラなので、とりあえずソートしておきます。
786
787                        // 6.4.0.2 (2015/12/11) columns="*" のときのカラムは、リソースに存在する分のみとする。
788                        final ResourceManager resource = getResource();
789
790                        final Enumeration<?> enume = getParameterNames();               // Generics警告対応
791                        while( enume.hasMoreElements() ) {
792                                final String clm  = (String)(enume.nextElement());
793                                // 予約語以外を登録します。
794                                // 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined
795//                              if( clm != null && !clm.isEmpty() && !clm.startsWith( "h_" ) && !clm.startsWith( "hX_" ) ) {
796//                                      // 6.4.0.2 (2015/12/11) columns="*" のときのカラムは、リソースに存在する分のみとする。
797//                                      if( resource.getDBColumn( clm ) != null ) {             // 存在チェックのみで使わない。
798                                if( clm != null && !clm.isEmpty() && !clm.startsWith( "h_" ) && !clm.startsWith( "hX_" )
799                                        && resource.getDBColumn( clm ) != null ) {              // 存在チェックのみで使わない。
800                                                clmSet.add( clm );
801//                                      }
802                                }
803                        }
804                        // 予約語を削除します。
805                        clmSet.remove( "maxRowCount" );
806                        clmSet.remove( "command" );
807                        clmSet.remove( "pageSize" );
808                        clmSet.remove( "pagePlus" );
809                        clmSet.remove( "debug" );                                                               // 6.4.0.2 (2015/12/11)
810                        clmSet.remove( "GAMENID" );
811                        clmSet.remove( "BACK_JSPID" );
812                        clmSet.remove( "BACK_GAMENID" );
813
814                        columns = clmSet.toArray( new String[clmSet.size()] );
815                }
816        }
817
818        /**
819         * 【TAG】NULL チェックすべきカラム列をCSV形式(CSV形式)で指定します。
820         *
821         * @og.tag
822         * (must 属性のセットにより、自動処理されます)
823         * nullCheck="AAA,BBB,CCC,DDD"
824         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
825         *
826         * @og.rev 3.5.6.2 (2004/07/05) 先に配列に分解してからリクエスト変数の値を取得
827         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
828         *
829         * @param   clms カラム列(CSV形式)
830         */
831        public void setNullCheck( final String clms ) {
832                nullCheck = StringUtil.csv2Array( getRequestParameter( clms ) );
833                if( nullCheck.length == 0 ) { nullCheck = null; }
834                else {
835                        Arrays.sort( nullCheck );
836                }
837        }
838
839        /**
840         * 【TAG】選択必須カラム(指定のカラムの内最低ひとつがNULLでない)を"AA|BB|CC,XX|YY|ZZ" 形式で指定します。
841         *
842         * @og.tag
843         * (mustAny 属性のセットにより、自動処理されます)
844         * 複数のカラム属性の値のうち、どれかが null でない場合は正常とし、
845         * すべてが null の場合を警告します。
846         *
847         *  例:mustAnyCheck="AA|BB|CC"
848         *      AA,BB,CC のカラムで選択必須(すべてがnullならエラー)
849         *  例:mustAnyCheck="AA|BB|CC,XX|YY|ZZ"
850         *      AA,BB,CC のセットと、XX,YY,ZZのセットでそれぞれ選択必須。
851         *  例:mustAnyCheck="AA|XX,AA|YY,AA|ZZ"
852         *      AA に値があればすべて成立。そうでない場合は、XX と YY と ZZ がすべて必須。
853         *  例:mustAnyCheck="AA|BB,BB|CC,AA|CC"
854         *      AA,BB,CC の内、どれか2つが必須。AAが成立すればBBかCCが必須。同様に、
855         *      BBが成立すれば、AAかCCが必須。
856         *
857         * 選択必須は、must と同様に、色づけを行う(query.jsp)画面では、mustAny 属性を
858         * セットします。チェックを行う(result.jsp)画面では、columnCheck タグの
859         * mustAnyCheck 属性に、選択必須カラムを指定します。(上記参照)
860         * column タグ等の mustAny 属性に、mustAny="true" とセットすると、生成される
861         * HTMLは、class="mustAny" が出力されます。エンジン標準では、default.css に
862         * .mustAny が定義されています。( background-color: #CCFFFF; )
863         * なお、mustAny 属性に、true 以外の値をセットした場合(mustAny="XYZ")は、
864         * 生成されるHTMLに、class="XYZ" が出力されます。これは、複数のグループ間で
865         * 色を変えて、選択必須を指定する場合に使用します。色は、custom/custom.css
866         * で指定します。
867         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
868         *
869         * @og.rev 3.8.0.9 (2005/10/17) 新規追加
870         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
871         *
872         * @param   clms 選択必須カラム
873         */
874        public void setMustAnyCheck( final String clms ) {
875                mustAnyCheck = StringUtil.csv2Array( getRequestParameter( clms ) );
876                if( mustAnyCheck.length == 0 ) { mustAnyCheck = null; }
877                else {
878                        Arrays.sort( mustAnyCheck );
879                }
880        }
881
882        /**
883         * 【TAG】コマンド[NEW/RENEW/ENTRY]をセットします(初期値:NEW)。
884         *
885         * @og.tag
886         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
887         * フィールド定数値のいづれかを、指定できます。
888         * 何も設定されない、または、null の場合は、"NEW" が初期値にセットされます。
889         *
890         * @param       cmd コマンド (public static final 宣言されている文字列)
891         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.ColumnCheckTag.CMD_NEW">コマンド定数</a>
892         */
893        public void setCommand( final String cmd ) {
894                final String cmd2 = getRequestParameter( cmd );
895                if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
896        }
897
898        /**
899         * 【TAG】チェック対象のタイプ[AUTO/NEW/ENTRY]を指定します(初期値:AUTO)。
900         *
901         * @og.tag
902         * 通常のリクエストデータタイプは、キーそのものですが,エントリデータは表形式の
903         * データを一括で登録する為、(キー+"__" + 行番号)形式を、バラす必要があります。
904         *
905         * ENTRY は、DBTableModelのリクエスト情報をチェックします。
906         * これは、全データが対象になります。(columns/nullCheck 属性は無効)
907         * AUTO は、command が、上記 NEW か ENTRY かを判断して処理を振り分けます。
908         * 初期値は、AUTO です。
909         *
910         * @param   flag チェックタイプ [AUTO/NEW/ENTRY]
911         */
912        public void setCheckType( final String flag ) {
913                final String ct = getRequestParameter( flag );
914                if( ct != null && ct.length() > 0 ) { checkType = ct ; }
915        }
916
917        /**
918         * 【TAG】チェックで選択された行数の最大値を設定します(初期値:-1 無制限)。
919         *
920         * @og.tag
921         * 制限をかけたい行数(この件数と同じ場合は正常)を指定します。
922         * 最大選択件数を超えた場合は,エラーメッセージを返します。
923         * 例えば、1件のみを正常とする場合は、maxRowCount="1" とします。
924         *
925         * @param   count 最大件数
926         */
927        public void setMaxRowCount( final String count ) {
928                maxRowCount = nval( getRequestParameter( count ),maxRowCount );
929        }
930
931        /**
932         * 【TAG】チェックで選択された行数の最小値を設定します(初期値:-1 無制限)。
933         *
934         * @og.tag
935         * 最小選択件数を超えない場合は、エラーメッセージを返します。
936         * 例えば、1件のみを正常とする場合は、minRowCount="1" とします。
937         *
938         * @param   count 制限をかけたい行数 (この件数と同じ場合は正常)
939         */
940         public void setMinRowCount( final String count ) {
941                minRowCount = nval( getRequestParameter( count ), minRowCount );
942         }
943
944        /**
945         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
946         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
947         *
948         * @og.tag
949         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
950         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
951         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
952         * この tableId 属性を利用して、メモリ空間を分けます。
953         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
954         *
955         * @og.rev 6.4.3.2 (2016/02/19) ENTRY時にチェックする対象を、tableId指定のテーブルのカラムに限定します。
956         *
957         * @param       id テーブルID (sessionに登録する時のID)
958         */
959        public void setTableId( final String id ) {
960                tableId = nval( getRequestParameter( id ),tableId );
961        }
962
963        /**
964         * 【TAG】正規表現でのマッチングを行うカラム列をCSV形式(CSV形式)で指定します。
965         *
966         * @og.tag matchKeys="AAA,BBB,CCC,DDD"
967         * matchKeys属性とmatchVals属性の個数は、同じでなければなりません。
968         * 先に配列に分解してからリクエスト変数の値を取得するようにします。
969         * こうする事で、リクエストにCSV形式の値を設定できるようになります。
970         *
971         * @og.rev 4.0.0.0 (2005/11/30) 新規作成
972         *
973         * @param   keys カラム列(CSV形式)
974         * @see     #setMatchVals( String )
975         */
976        public void setMatchKeys( final String keys ) {
977                matchKeys = getCSVParameter( keys );
978
979                if( matchVals != null && matchKeys.length != matchVals.length ) {
980                        final String errMsg = "matchKeys属性とmatchVals属性の個数が合いません。"
981                                                + CR
982                                                + " matchKeys=[" + matchKeys.length + "]:KEYS="
983                                                + StringUtil.array2csv( matchKeys ) + CR
984                                                + " matchVals=[" + matchVals.length + "]:VLAS="
985                                                + StringUtil.array2csv( matchVals ) + CR ;
986                        throw new HybsSystemException( errMsg );
987                }
988        }
989
990        /**
991         * 【TAG】正規表現でのマッチングを行うカラム列に対する値(正規表現)をCSV形式(CSV形式)で指定します。
992         *
993         * @og.tag matchVals="AAA,BBB,CCC,DDD"
994         * matchKeys属性とmatchVals属性の個数は、同じでなければなりません。
995         * 先に配列に分解してからリクエスト変数の値を取得するようにします。
996         * こうする事で、リクエストにCSV形式の値を設定できるようになります。
997         *
998         * @og.rev 4.0.0.0 (2005/11/30) 新規作成
999         *
1000         * @param   vals カラム列に対する値(正規表現)(CSV形式)
1001         * @see     #setMatchKeys( String )
1002         */
1003        public void setMatchVals( final String vals ) {
1004                matchVals = getCSVParameter( vals );
1005
1006                if( matchKeys != null && matchKeys.length != matchVals.length ) {
1007                        final String errMsg = "matchKeys属性とmatchVals属性の個数が合いません。"
1008                                                + CR
1009                                                + " matchKeys=[" + matchKeys.length + "]:KEYS="
1010                                                + StringUtil.array2csv( matchKeys ) + CR
1011                                                + " matchVals=[" + matchVals.length + "]:VLAS="
1012                                                + StringUtil.array2csv( matchVals ) + CR ;
1013                        throw new HybsSystemException( errMsg );
1014                }
1015        }
1016
1017        /**
1018         * 【TAG】(通常は使いません)リアルタイムチェックを行う場合に有効にします(初期値:false)。
1019         *
1020         * @og.tag
1021         * リアルタイムチェックを行う場合に有効にする属性です。
1022         * trueが指定された場合、通常のチェックと比較し、以下の差異があります。
1023         * ①エラー結果を簡易フォーマットで出力します。
1024         *   (ViewFormType="HTMLSimpleErrorList"で表示されます。)
1025         *   この簡易フォーマットで出力した場合は、ラベル(短)の定義で出力され、
1026         *   カラム名やデータ等は出力されません。
1027         * ②must,mustAny属性のチェックを行いません。
1028         *   必須及び選択必須は、視覚的に判別できるため、リアルタイムチェックの
1029         *   対象外とします。
1030         *
1031         * @og.rev 4.3.3.0 (2008/10/01) 新規作成
1032         * @og.rev 4.3.7.0 (2009/06/01) viewSimple属性名称変更 ⇒ isRealTime
1033         *
1034         * @param   flag リアルタイムチェック [true:する/false:しない]
1035         */
1036        public void setRealTime( final String flag ) {
1037                isRealTime = nval( getRequestParameter( flag ),isRealTime );
1038        }
1039
1040        /**
1041         * 【TAG】NEWの場合に、厳密にチェックするかどうか[true/false]を指定します(初期値:false)。
1042         *
1043         * @og.tag
1044         * データをチェックするに当たり、あいまい検索などで厳密にチェックすると
1045         * エラーになるケースがあるため、command="NEW" のチェック時には、甘い目の
1046         * チェックを行っています。DBColumn#valueCheck( String , false )
1047         * command="ENTRY" のチェックは、厳密なチェックです。
1048         * このフラグは、検索時でも、厳密なチェックを行いたい場合に、true をセットします。
1049         *
1050         * @og.rev 5.2.2.0 (2010/11/01) 新規作成
1051         *
1052         * @param   flag 厳密チェックか [true:厳密/false:甘い]
1053         */
1054        public void setUseStrict( final String flag ) {
1055                isStrict = nval( getRequestParameter( flag ),isStrict );
1056        }
1057
1058        /**
1059         * 【TAG】カラムラベルを使用するかどうか[true/false]を指定します(初期値:false)。
1060         *
1061         * @og.tag
1062         * カラムラベルはラベルローダーを利用せずにアプリケーション側で
1063         * 追加したラベルをResourceManagerにキャッシュしている所から取り出す
1064         * かどうかを指定します。
1065         * カラムラベルに存在しない場合は、通常のラベルから検索します。
1066         *
1067         * @og.rev 5.4.3.4 (2012/01/12) 新規作成
1068         *
1069         * @param   flag カラムラベル使用 [true:する/false:しない]
1070         */
1071        public void setUseColumnLabel( final String flag ) {
1072                isColumnLabel = nval( getRequestParameter( flag ),isColumnLabel );
1073        }
1074
1075        /**
1076         * 【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します。
1077         *
1078         * @og.tag
1079         * カラムチェックで正規化が行われていないプラグインに対して強制的に正規化を行います。
1080         * この属性を利用したチェックを行った場合は、queryタグでPL/SQLを動作させる場合にも
1081         * 同名属性でカラムの指定を行う必要があります。
1082         * (queryタグでcheckNamesを利用しない場合はこの属性はnullにしてください)
1083         * この属性はチェックタイプがNEWの場合のみ有効です。
1084         * リアルタイムチェックでこの機能を利用したい場合は、columnTag等のoptionAttributes
1085         * 属性にrtOption='checkNames=VIEW_DATE'のように記述してください。
1086         *
1087         * @og.rev 5.4.3.8 (2012/01/24) 新規追加
1088         * @og.rev 5.4.4.0 (2012/02/01) コメント修正
1089         * @og.rev 5.6.9.4 (2013/10/31) 空白削除を行う
1090         * @og.rev 6.9.2.1 (2018/03/12) 使用箇所が、1箇所だけなので、StringUtilから移動する。
1091         *
1092         * @param       nm 正規化を行うカラム(CSV形式)
1093         */
1094        public void setCheckNames( final String nm ) {
1095//              checkNames = StringUtil.deleteWhitespace( nval( getRequestParameter( nm ),checkNames ) );       // 5.6.9.4 (2013/10/31)
1096                final String tmpNm = nval( getRequestParameter( nm ),checkNames );
1097                checkNames = StringUtil.isNull( tmpNm ) ? checkNames :  tmpNm.replaceAll( "\\s", "" );          // 6.9.2.1 (2018/03/12)
1098        }
1099
1100        /**
1101         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
1102         *
1103         * @og.tag
1104         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
1105         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
1106         * 標準はfalse:利用しない=ラベル(長)です。
1107         * true/false以外を指定した場合はfalse扱いとします。
1108         *
1109         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
1110         *
1111         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
1112         *
1113         * @param prm SLABEL利用 [true:利用する/false:利用しない]
1114         */
1115        public void setUseSLabel( final String prm ) {
1116                useSLabel = nval( getRequestParameter( prm ),useSLabel );
1117        }
1118
1119        /**
1120         * このオブジェクトの文字列表現を返します。
1121         * 基本的にデバッグ目的に使用します。
1122         *
1123         * @return このクラスの文字列表現
1124         * @og.rtnNotNull
1125         */
1126        @Override
1127        public String toString() {
1128                return ToString.title( this.getClass().getName() )
1129                                .println( "VERSION"                     ,VERSION                )
1130                                .println( "tableId"                     ,tableId                )
1131                                .println( "command"                     ,command                )
1132                                .println( "columns"                     ,columns                )
1133                                .println( "nullCheck"           ,nullCheck              )
1134                                .println( "mustAnyCheck"        ,mustAnyCheck   )
1135                                .println( "maxRowCount"         ,maxRowCount    )
1136                                .println( "minRowCount"         ,minRowCount    )
1137                                .println( "checkType"           ,checkType              )
1138                                .println( "matchKeys"           ,matchKeys              )
1139                                .println( "matchVals"           ,matchVals              )
1140                                .println( "realTime"            ,isRealTime             )
1141                                .println( "bodyString"          ,StringUtil.htmlFilter( bodyString ) )
1142                                .println( "Other..."            ,getAttributes().getAttribute() )
1143                                .fixForm().toString() ;
1144        }
1145}