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