001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.taglib; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.StringUtil; 021import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 022 023import static org.opengion.fukurou.util.StringUtil.nval ; 024 025import java.util.List; // 6.4.3.2 (2016/02/19) 026import java.util.stream.Stream; // 6.4.3.2 (2016/02/19) 027import java.util.stream.Collectors; // 6.4.3.2 (2016/02/19) 028 029/** 030 * Where句を作成するための条件を指定します。 031 * 032 * このタグのvalue 値に、{@XXXX} 変数が含まれている場合、そのリクエスト値が 033 * ない場合は、このタグそのものがなにも出力しません。(つまり条件から消えます。) 034 * startKeyは、value を連結する場合の頭に置かれる文字列で、where句の最初には表示されず、 035 * それ以降について、表示されます。(つまり、where VALUE1 and VALUE2 and VALUE3 … です。) 036 * startKey の初期値は、"and" です。 037 * multi は、{@XXXX} 変数に、値が複数含まれている場合の処理を規定します。 038 * 複数の値とは、同一nameでチェックボックス指定や、メニューでの複数指定した場合、 039 * リクエストが配列で送られます。multi="true" とすると、'xx1','xx2','xx3', ・・・ という 040 * 形式に変換されます。 041 * 具体的には、"where PN in ( {@PN} )" という文字列に対して、 042 * "where PN in ( 'xx1','xx2','xx3' )" を作成することができます。 043 * multi の初期値は、"false" です。 044 * SystemData の USE_SQL_INJECTION_CHECK が true か、quotCheck 属性が true の場合は、 045 * SQLインジェクション対策用のシングルクォートチェックを行います。リクエスト引数に 046 * シングルクォート(')が含まれると、エラーになります。 047 * 同様にUSE_XSS_CHECKがtrueか、xssCheck属性がtrueの場合は、 048 * クロスサイトススクリプティング(XSS)対策のためless/greater than signのチェックを行います。 049 * 050 * 各属性は、{@XXXX} 変数が使用できます。 051 * これは、ServletRequest から、XXXX をキーに値を取り出し,この変数に割り当てます。 052 * つまり、このXXXXをキーにリクエストすれば、この変数に値をセットすることができます。 053 * 054 * @og.formSample 055 * ●形式:<og:and startKey="[and|or|…]" value="…" multi="[false|true]" /> 056 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 057 * 058 * ●Tag定義: 059 * <og:and 060 * startKey 【TAG】SQL条件句の最初の演算子を指定します(初期値:and) 061 * value 【TAG】条件の値を セットします 062 * multi 【TAG】複数の引数に対して処理するかどうか[true/false]を設定します(初期値:false) 063 * separator 【TAG】multi アクション時の文字列を分割する項目区切り文字をセットします 064 * instrVals 【TAG】スペースで区切られた複数の値すべてを含む条件を作成します 065 * instrType 【TAG】instrValsで複数の値を条件にする際の方法を指定します(初期値:and) 066 * range 【TAG】数値型カラムに対して、ハイフンで範囲指定をカンマに分解するかどうか[true/false]を設定します(初期値:false) 6.5.0.0 (2016/09/30) 067 * quotCheck 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_SQL_INJECTION_CHECK[=true]) 068 * xssCheck 【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true]) 069 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 070 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 071 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 072 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 073 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 074 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 075 * > ... Body ... 076 * </og:and> 077 * 078 * ●使用例 079 * <og:query command="NEW"> 080 * select PN,YOBI,NMEN,HINM from XX01 081 * <og:where> 082 * <og:and value="PN = '{@PN}'" /> 083 * <og:and value="YOBI like '{@YOBI}%'" /> 084 * </og:where> 085 * order by PN 086 * </og:query> 087 * 088 * ・検索条件が入力された時(PN=AAA , YOBI=BBB) 089 * 作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = 'AAA' and YOBI like 'BBB%' order by PN 090 * 091 * ・検索条件が片方入力されなかった時(PNがNULLのとき, YOBI=BBB) 092 * 作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where YOBI like 'BBB%' order by PN 093 * 094 * ・検索条件が入力されなかった時(PNがNULL, YOBIがNULL) WHERE句がなくなる。 095 * 作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 order by PN 096 * 097 * 注意:WhereTagを使わない場合に、検索条件が入力されなかった場合は、下記のようになります。 098 * select PN,YOBI,NMEN,HINM from XX01 where PN = '' and YOBI like '%' order by PN 099 * 100 * -------------------------------------------------------------------------------------------------------------- 101 * 102 * <og:query command="NEW"> 103 * select PN,YOBI,NMEN,HINM from XX01 where PN="11111" 104 * <og:where startKey="and"> 105 * <og:and value="YOBI in ({@YOBI})" multi="true" /> 106 * <og:and value="HINM like '{@HINM}%'" /> 107 * </og:where> 108 * order by PN 109 * </og:query> 110 * 111 * ・YOBI を複数選択し、in で検索する時(YOBI=AA,BB,CC を選択) 112 * 作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = '11111' 113 * and YOBI in ( 'AA','BB','CC' ) and HINM like 'BBB%' order by PN 114 * 115 * @og.group 画面部品 116 * 117 * @version 4.0 118 * @author Kazuhiko Hasegawa 119 * @since JDK5.0, 120 */ 121public class SqlAndTag extends CommonTagSupport { 122 /** このプログラムのVERSION文字列を設定します。 {@value} */ 123 private static final String VERSION = "6.8.5.0 (2018/01/09)" ; 124 private static final long serialVersionUID = 685020180109L ; 125 126 private String startKey = "and"; 127 private String value = ""; 128 private String instrVals ; // 3.8.8.1 (2007/01/06) 129 private String instrType = "and"; // 5.4.1.0 (2011/11/01) 130 private boolean multi ; 131 private boolean quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); // 4.0.0 (2005/08/31) 132 private boolean xssCheck = HybsSystem.sysBool( "USE_XSS_CHECK" ); // 5.0.0.2 (2009/09/15) 133 private boolean range ; // 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 134 135 private boolean allNull ; // 5.0.0.2 (2009/09/15) 136 private boolean localReq ; // 6.1.1.0 (2015/01/17) このクラスの getRequestValue を呼び出すキー 137 138 private String separator ; // 5.2.2.0 (2010/11/01) 項目区切り文字 139 140 /** 141 * デフォルトコンストラクター 142 * 143 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 144 */ 145 public SqlAndTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 146 147 /** 148 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 149 * 150 * @og.rev 4.0.0.0 (2006/12/05) BODY 部の値を value に使用する機能追加 151 * @og.rev 4.0.0.0 (2005/08/31) useQuotCheck() によるSQLインジェクション対策 152 * @og.rev 5.0.0.2 (2009/09/15) XSS対策 153 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 154 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 155 * 156 * @return 後続処理の指示 157 */ 158 @Override 159 public int doStartTag() { 160 // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 161 if( useTag() ) { 162 useQuotCheck( quotCheck ); 163 // 5.0.0.2 (2009/09/15) XSS対策 164 useXssCheck( xssCheck ); 165 166 localReq = multi; // 6.1.1.0 (2015/01/17) 内部の getRequestValue を呼び出すキー 167 value = getRequestParameter( value ); 168 169 if( value == null || value.isEmpty() ) { 170 return EVAL_BODY_BUFFERED ; // Body を評価する。( extends BodyTagSupport 時) 171 } 172 173 // if( value != null && value.length() > 0 ) { 174 // return( SKIP_BODY ); // Body を評価しない 175 // } 176 // else { 177 // return EVAL_BODY_BUFFERED ; // Body を評価する。( extends BodyTagSupport 時) 178 // } 179 } 180 return SKIP_BODY ; // Body を評価しない 181 } 182 183 /** 184 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 185 * 186 * @og.rev 4.0.0.0 (2006/12/05) BODY 部の値を value に使用する機能追加 187 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 188 * 189 * @return 後続処理の指示(SKIP_BODY) 190 */ 191 @Override 192 public int doAfterBody() { 193 localReq = multi; // 6.1.1.0 (2015/01/17) 内部の getRequestValue を呼び出すキー 194 value = getBodyString(); 195 return SKIP_BODY ; 196 } 197 198 /** 199 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 200 * 201 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 202 * @og.rev 3.8.8.1 (2007/01/06) makeInstrVals を加味する。 203 * @og.rev 5.0.0.2 (2009/09/15) multi時のallNull対応 204 * @og.rev 5.1.9.0 (2010/08/01) matchKey 、matchVal 対応 ⇒ 5.2.2.0 (2010/11/01) 廃止 205 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 206 * 207 * @return 後続処理の指示 208 */ 209 @Override 210 public int doEndTag() { 211 debugPrint(); // 4.0.0 (2005/02/28) 212 // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 213 if( useTag() ) { 214 final SqlWhereTag where = (SqlWhereTag)findAncestorWithClass( this,SqlWhereTag.class ); 215 if( where == null ) { 216 final String errMsg = "<b>" + getTagName() + "タグは、where タグの内部におく必要があります。</b>"; 217 throw new HybsSystemException( errMsg ); 218 } 219 220 // 5.1.9.0 (2010/08/01) matchKey 、matchVal 対応 ⇒ 5.2.2.0 (2010/11/01) 廃止 221 if( ! isNull() && ! allNull ) { // 5.2.2.0 (2010/11/01) 222 value = makeInstrVals( instrVals,instrType,value ); // 5.4.1.0 (2011/11/01) 223 if( value != null ) { 224 set( "keyWord", startKey ); 225 set( "value" , value ); 226 where.setAttributes( getAttributes() ); 227 } 228 } 229 } 230 return EVAL_PAGE ; 231 } 232 233 /** 234 * タグリブオブジェクトをリリースします。 235 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 236 * 237 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加 238 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 239 * @og.rev 3.8.8.1 (2007/01/06) instrVals 属性追加 240 * @og.rev 4.0.0.0 (2005/08/31) quotCheck 属性の追加 241 * @og.rev 5.0.0.2 (2009/09/15) XSS対応 242 * @og.rev 5.0.0.2 (2009/09/15) multi時のallNull対応 243 * @og.rev 5.1.9.0 (2010/08/01) matchKey、matchVal 属性の追加 244 * @og.rev 5.2.2.0 (2010/11/01) separator , isMatch 属性の追加 245 * @og.rev 5.2.2.0 (2010/11/01) matchKey、matchVal 属性廃止(caseKey,caseVal属性を使用してください。) 246 * @og.rev 5.4.1.0 (2011/11/01) instrType属性追加 247 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 248 * @og.rev 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 249 */ 250 @Override 251 protected void release2() { 252 super.release2(); 253 startKey = "and"; 254 value = ""; 255 instrVals = null; // 3.8.8.1 (2007/01/06) 256 instrType = "and"; // 5.4.1.0 (2011/11/01) 257 multi = false; 258 quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); // 4.0.0 (2005/08/31) 259 xssCheck = HybsSystem.sysBool( "USE_XSS_CHECK" ); // 5.0.0.2 (2009/09/15) 260 allNull = false; // 5.0.0.2 (2009/09/15) 261 separator = null; // 5.2.2.0 (2010/11/01) 項目区切り文字 262 localReq = false; // 6.1.1.0 (2015/01/17) このクラスの getRequestValue を呼び出すキー 263 range = false; // 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 264 } 265 266 /** 267 * リクエスト情報の文字列を取得します。 268 * 269 * これは、通常のgetRequestParameter 処理の中で呼ばれる getRequestValue を 270 * オーバーライトしています。 271 * 272 * @og.rev 5.0.0.2 (2009/09/15) valuesの全NULL/空文字をisNull扱いにする 273 * @og.rev 5.3.8.0 (2011/08/01) Attribute等からも値が取得できるようにする。の対応時の特殊処理 274 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 275 * @og.rev 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 276 * 277 * @param key キー 278 * 279 * @return リクエスト情報の文字列 280 */ 281 @Override 282 protected String getRequestValue( final String key ) { 283 String rtn = ""; 284 285 if( localReq ) { // 6.1.1.0 (2015/01/17) localReq変数を使う 286 // 5.3.8.0 (2011/08/01) getRequestValues の中で、getRequestValue を呼び出すためこのままでは 287 // 再帰呼び出しが永遠に続くので、2回目以降は、再帰しないように、強制的に multi の値を書き換えます。 288 localReq = false; // 6.1.1.0 (2015/01/17) 再帰しないように、localReq変数の値を書き換え 289 final String[] array = getRequestValues( key ); 290 allNull = true; // 5.0.0.2 (2009/09/15) arrayの内容が全てnull/空文字か 291 if( ! isNull() ) { 292 // 5.0.0.2 (2009/09/15) 全てnull/空文字の場合はnullと扱い 293 for( int i=0; i<array.length; i++ ) { 294 if( array[i] != null && !array[i].isEmpty() ) { 295 allNull = false; 296 break; 297 } 298 } 299 if( ! allNull ){ 300 rtn = makeCSVvalue( array ); 301 } 302 } 303 } 304 else { 305 rtn = super.getRequestValue( key ); 306 // 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 307 if( range ) { rtn = makeRangeCsv( rtn ); } 308 309 } 310 return rtn ; 311 } 312 313 /** 314 * 複数の値を 'xx1','xx2','xx3', ・・・ という形式に変換します。 315 * 316 * この処理は、in などで使用するためのリクエストを配列で受け取って処理 317 * する場合の文字列を加工します。 318 * 319 * @og.rev 5.2.2.0 (2010/11/01) separator 対応 320 * @og.rev 6.1.1.0 (2015/01/17) 引数が、null や空文字列の場合は、処理しません。 321 * @og.rev 6.4.3.2 (2016/02/19) separator で分割後の文字列を trim() しておきます。 322 * 323 * @param array 元の文字列配列(可変長引数) 324 * 325 * @return 連結後の文字列 326 * @og.rtnNotNull 327 */ 328 private String makeCSVvalue( final String... array ) { 329 if( array == null || array.length == 0 ) { 330 final String errMsg = "array 引数に、null や、サイズゼロの配列は使用できません。"; 331 throw new HybsSystemException( errMsg ); 332 } 333 334 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 335 336 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 337 if( separator == null ) { 338 for( final String val : array ) { 339 if( val != null && !val.isEmpty() && !val.trim().isEmpty() ) { 340 // 6.4.3.2 (2016/02/19) append の文字列を trim() しておきます。 341 buf.append( '\'' ).append( val.trim() ).append( "'," ); 342 } 343 } 344 } 345 else { 346 for( final String vals : array ) { 347 if( vals != null && !vals.isEmpty() && !vals.trim().isEmpty() ) { 348 // 6.4.3.2 (2016/02/19) separator で分割後の文字列を trim() しておきます。 349 for( final String val : vals.split( separator ) ) { 350 if( val != null && !val.isEmpty() && !val.trim().isEmpty() ) { 351 buf.append( '\'' ).append( val.trim() ).append( "'," ); 352 } 353 } 354 } 355 } 356 } 357 358 // 6.4.3.2 (2016/02/19) 最後の ピリオドを削除する。buf が append されている場合のみ削除する。 359 if( buf.length() > 0 ) { buf.deleteCharAt( buf.length()-1 ); } 360 return buf.toString(); 361 } 362 363 /** 364 * スペースで区切られた複数の値を and 接続で連結します。 365 * 366 * value="CLM" instrVals="ABC DEF GHI" と指定すると、 367 * value="CLM LIKE '%ABC%' AND CLM LIKE '%DEF%' AND CLM LIKE '%GHI%' " 368 * という文字列を作成します。 369 * 個別にLIKE検索項目を AND 連結する為、現れる場所に依存しません。 370 * 逆に、現れる順序を指定する場合は、ABC%DEF の様に指定可能です。 371 * ただし、columnMarker の instrVals で、複数文字のマーカーを行う場合、 372 * ABC%DEF という文字列は、オリジナルでないので、マークアップされません。 373 * 374 * @og.rev 5.4.1.0 (2011/11/01) instrType属性対応 375 * @og.rev 5.5.1.1 (2012/04/06) notin対応 376 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 377 * @og.rev 6.1.1.0 (2015/01/17) 分割キーをseparatorで指定可能とします。 378 * @og.rev 6.4.3.2 (2016/02/19) 区切り文字を、スペース、カンマ、タブ、改行にします。 379 * @og.rev 6.7.3.0 (2017/01/27) in とnot in のコーディングを、変更 380 * @og.rev 6.8.5.0 (2018/01/09) StringUtil.csv2Array のデフォルトメソッドを使用します。 381 * 382 * @param instrVals 繰返し処理を行う値 383 * @param instrType 連結方法 384 * @param value 繰返し処理を行うvalue 385 * 386 * @return 連結後の文字列 387 * @see #setInstrVals( String ) 388 * @see ColumnMarkerTag#setInstrVals( String ) 389 */ 390 private String makeInstrVals( final String instrVals, final String instrType , final String value ) { 391 // instrValsが、設定されていない場合は、通常通り、value を使用する。 392 if( instrVals == null || instrVals.isEmpty() ) { return value; } 393 394 localReq = multi; // 6.1.1.0 (2015/01/17) 内部の getRequestValue を呼び出すキー 395 // instrValsが、設定されているが、リクエスト変数処理の結果が、null の場合は、このタグを使用しないため、null を返す。 396 final String reqVals = nval( getRequestParameter( instrVals ),null ); 397 if( reqVals == null || reqVals.isEmpty() ) { return null; } 398 399 // 6.4.3.2 (2016/02/19) empty() のときは処理 400 final List<String> lst; 401 if( multi ) { 402 // multi のときは、makeCSVvalue で加工された値になっているので、前後の ' はずし が必要。 403 final String[] vals = StringUtil.csv2Array( reqVals ); // 6.8.5.0 (2018/01/09) デフォルトがカンマ 404 lst = Stream.of( vals ) 405 .filter( v -> v != null && v.length() > 2 ) 406 .map( v -> v.substring( 1,v.length()-1 ) ) 407 .collect( Collectors.toList() ); 408 409 // multi のときは、makeCSVvalue で加工された値になっている。 410 } 411 else { 412 // 6.4.3.2 (2016/02/19) 区切り文字を、スペース、カンマ、タブ、改行にします。 413 final String[] vals = reqVals.split( separator == null ? "[, \\t\\n]" : separator ); 414 415 if( vals == null || vals.length == 0 ) { return null; } // splitしているので、nullはありえない・・・はず。 416 else { 417 // 6.4.3.2 (2016/02/19) separator で分割後の文字列を trim() しておきます。 418 lst = Stream.of( vals ) 419 .filter( v -> v != null && !v.isEmpty() && !v.trim().isEmpty() ) 420 .map( v -> v.trim() ) 421 .collect( Collectors.toList() ); 422 } 423 } 424 425 // 6.4.3.2 (2016/02/19) 先のif文の else でしか、null はありえないので、上にもって行きます。 426 427 final char instType = instrType != null && instrType.length() > 0 ? instrType.charAt(0) : 'X' ; 428 429 // 6.4.3.2 (2016/02/19) 元より判りにくくなった感じがしますが、とりあえず。 430 final String st ; // 文字列連結の 先頭文字 431 final String sep ; // 連結文字 432 final String ed ; // 最後の連結文字 433 434 if( 'a' == instType || 'A' == instType ) { // 6.1.1.0 (2015/01/17) 大文字対応 435 st = " ( " + value + " LIKE '%" ; 436 sep = "%' and " + value + " LIKE '%" ; 437 ed = "%' ) " ; 438 } 439 // 条件:or ⇒ 各値をorのlike条件で結合(%あり) 440 else if( 'o' == instType || 'O' == instType ) { // 6.1.1.0 (2015/01/17) 大文字対応 441 st = " ( " + value + " LIKE '%" ; 442 sep = "%' or " + value + " LIKE '%" ; 443 ed = "%' ) " ; 444 } 445 // 条件:in ⇒ 各値をorのlike条件で結合(%なし) 446 else if( 'i' == instType || 'I' == instType ) { // 6.1.1.0 (2015/01/17) 大文字対応 447 st = value + " in ( '" ; 448 sep = "','" ; 449 ed = "' ) " ; 450 } 451 // 条件:notin ⇒ 各値をandのnot like条件で結合(%なし) 5.5.1.1(2012/04/05) 452 else if( 'n' == instType || 'N' == instType ) { // 6.1.1.0 (2015/01/17) 大文字対応 453 st = value + " not in ( '" ; 454 sep = "','" ; 455 ed = "' ) " ; 456 } 457 else { 458 final String errMsg = "instrTypeには、'and','or','in','notin'のいずれかを指定して下さい。" + CR + 459 " instrType=[" + instrType + "]"; 460 throw new HybsSystemException( errMsg ); 461 } 462 463 // null,isEmpty(),trim().isEmpty() 以外の List から、文字列連結して、SQL文を作成します。 464 return lst.stream().collect( Collectors.joining( sep , st , ed ) ) ; 465 466 } 467 468 /** 469 * 数値型カラムの範囲指定処理を行います。 470 * 471 * 引数に、"1,3,4-8" のような指定を行うと、"1,3,4,5,6,7,8" に変換します。 472 * これは、数値カラムの範囲指定で、対象は自然数なので、小数や、マイナス、増加ステップが1以外、 473 * マイナスステップは扱えません。 474 * 475 * 数値が前提なので、区切り記号は、スペース、カンマで区切ったあと、再度、カンマでつなげます。 476 * その際、"-" を含む場合は、前後の数値を、1づつ増加させてカンマでつなげます。 477 * 478 * @og.rev 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 479 * 480 * @param inVal 指定の数値型カラムの値 481 * @return 数値型カラムの範囲指定変換の値 482 */ 483 private String makeRangeCsv( final String inVal ) { 484 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 485 486 for( final String val : inVal.split( "[, ]" ) ) { // スペース、カンマで分割 487 if( val != null && !val.isEmpty() ) { 488 final int ad = val.indexOf( '-' ); 489 if( ad < 0 ) { 490 buf.append( val ).append( ',' ); 491 } 492 else { // 範囲処理 493 final int st = Integer.parseInt( val.substring( 0,ad ) ); 494 final int ed = Integer.parseInt( val.substring( ad+1 ) ); 495 for( int i=st; i<=ed; i++ ) { // 終了条件は含みます。 496 buf.append( Integer.toString( i ) ).append( ',' ); 497 } 498 } 499 } 500 } 501 502 final int len = buf.length(); // 長さを取得。 503 if( len > 1 ) { buf.setLength( len-1 ); } // 長さがある場合、最後のカンマを削除します。 504 505 return buf.toString(); 506 } 507 508 /** 509 * 【TAG】SQL条件句の最初の演算子を指定します(初期値:and)。 510 * 511 * @og.tag 512 * value を連結する場合の頭に置かれる文字列で、where句の最初には表示されず、 513 * それ以降について、表示されます。 514 * (つまり、where VALUE1 and VALUE2 and VALUE3 … です。) 515 * startKey の初期値は、"and" です。 516 * 517 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 518 * 519 * @param skey 条件句の最初の演算子 520 */ 521 public void setStartKey( final String skey ) { 522 startKey = nval( getRequestParameter( skey ),startKey ); // 6.1.1.0 (2015/01/17) 523 } 524 525 /** 526 * 【TAG】条件の値を セットします。 527 * 528 * @og.tag 529 * 条件値に、{@XXXX} 変数が含まれている場合、そのリクエスト値がない場合は、 530 * このタグそのものがなにも出力しません。(つまり条件から消えます。) 531 * BODY 部に記述することが可能です。その場合は、条件属性になにも設定できません。 532 * 533 * @param val 条件値 534 */ 535 public void setValue( final String val ) { 536 value = val; 537 } 538 539 /** 540 * 【TAG】特定の文字で区切られた複数の値すべてを含む条件を作成します。 541 * 542 * @og.tag 543 * value="CLM" instrVals="ABC DEF GHI" と指定すると、 544 * value="CLM LIKE '%ABC%' AND CLM LIKE '%DEF%' AND CLM LIKE '%GHI%' " 545 * という文字列を作成します。 546 * 通常の、value="CLM LIKE '%ABC%DEF%'" の指定方法では、ABCとDEFの 547 * 順番が固定化されますが、instrVals を用いた方法では、個別指定が可能です。 548 * 549 * ※ 6.4.3.2 (2016/02/19) 550 * これは、instrVals に指定した引数に対して、スペース、カンマ、タブ、改行の 551 * どれかで区切ります。個別に指定する場合は、separatorに設定します。 552 * これは、instrVals.split(separator) で分割するので、正規表現が使用可能です。 553 * 分割後に、前方の value に複数のAND検索(instrTypeで変更可)を同時に指定できる 554 * ため、現れる場所に依存しません。 555 * 556 * 逆に、現れる順序を指定する場合は、ABC%DEF の様に指定可能です。 557 * ただし、columnMarker の instrVals で、複数文字のマーカーを行う場合、 558 * ABC%DEF という文字列は、オリジナルでないので、マークアップされません。 559 * ※instrType属性の指定により条件の生成方法を変更することができます。 560 * 詳細については、instrType属性のドキュメントを参照下さい。 561 * 562 * @og.rev 6.4.3.2 (2016/02/19) 区切り文字を、スペース、カンマ、タブ、改行にします。 563 * 564 * @param val 複合条件作成のための設定値 565 * @see #setInstrType 566 * @see ColumnMarkerTag#setInstrVals( String ) 567 */ 568 public void setInstrVals( final String val ) { 569 instrVals = val; 570 } 571 572 /** 573 * 【TAG】instrValsで複数の値を条件にする際の方法を指定します(初期値:and)。 574 * 575 * @og.tag 576 * 通常、instrValsに指定された値は、スペース区切りで分割した各値を 577 * LIKE条件としてand結合します。 578 * しかし、instrType属性を変更することで、この条件式の生成方法を変更 579 * することができます。 580 * 具体的には、以下の通りです。 581 * ①instrTypeに"and"が指定されている場合(初期値) 582 * タグの記述 : value="CLM" instrVals="ABC DEF GHI" 583 * 生成文字列 : "( CLM LIKE '%ABC%' AND CLM LIKE '%DEF%' AND CLM LIKE '%GHI%' )" 584 * ②instrTypeに"or"が指定されている場合 585 * タグの記述 : value="CLM" instrVals="ABC DEF GHI" 586 * 生成文字列 : "( CLM LIKE '%ABC%' OR CLM LIKE '%DEF%' OR CLM LIKE '%GHI%' )" 587 * ③instrTypeに"in"が指定されている場合 588 * タグの記述 : value="CLM" instrVals="ABC DEF GHI" 589 * 生成文字列 : "CLM in ('ABC','DEF5','GHI')" 590 * ④instrTypeに"notin"が指定されている場合 591 * タグの記述 : value="CLM" instrVals="ABC DEF GHI" 592 * 生成文字列 : "CLM not in ('ABC','DEF5','GHI')" 593 * ※この属性を指定しない場合は、①のLIKE条件でのand結合となります。 594 * ※③④について、LIKE条件で%を自動付加しないことにより、画面からの入力値に応じて、 595 * 前方一致、後方一致、前後方一致の制御を行うことができます。 596 * 597 * @og.rev 5.5.1.1 (2012/04/06) notin対応(コメント修正) 598 * @og.rev 6.1.1.0 (2015/01/17) 初期値指定のコーディングミス修正 599 * @og.rev 6.7.3.0 (2017/01/27) in とnot in のコーディングを、変更 600 * 601 * @param tp 条件方法 [and/or/in/notin] 602 * @see #setInstrVals( String ) 603 */ 604 public void setInstrType( final String tp ) { 605 instrType = nval( getRequestParameter( tp ),instrType ); // 6.1.1.0 (2015/01/17) 606 } 607 608 /** 609 * 【TAG】複数の引数に対して処理するかどうか[true/false]を設定します(初期値:false)。 610 * 611 * @og.tag 612 * {@XXXX} 変数に、値が複数含まれている場合の処理を規定します。 613 * multi="true" に設定すると、複数の引数は、'xx1','xx2','xx3', ・・・ という 614 * 形式に変換します。 615 * where 条件で言うと、 "where PN in ( {@PN} )" という文字列に対して、 616 * "where PN in ( 'xx1','xx2','xx3' )" を作成することになります。 617 * 初期値は、 false (マルチ変換しない) です。 618 * 619 * @og.rev 6.1.1.0 (2015/01/17) localReq変数を使う事で、RequestParameter処理を制御します。 620 * 621 * @param flag マルチ変換 [true:する/それ以外:しない] 622 * @see #setSeparator( String ) 623 */ 624 public void setMulti( final String flag ) { 625 multi = nval( getRequestParameter( flag ),multi ); 626 } 627 628 /** 629 * 【TAG】数値型カラムに対して、ハイフンで範囲指定をカンマに分解するかどうか[true/false]を設定します(初期値:false)。 630 * 631 * @og.tag 632 * {@XXXX} 変数に、"1,3,4-8" のような指定を行うと、"1,3,4,5,6,7,8" に変換します。 633 * これは、数値型カラムの範囲指定を、ハイフンで行うことが出来る機能です。 634 * ハイフン以外は、カンマで区切って、普通の数値として指定できます。 635 * where 条件で言うと、 "where GOKI in ( {@GOKI} )" という文字列に対して、 636 * "where GOKI in ( 1,3,4,5,6,7,8 )" を作成することになります。 637 * 初期値は、 false (範囲変換しない) です。 638 * これは、数値カラムの範囲指定で、対象は自然数なので、小数や、マイナス、増加ステップが1以外、 639 * マイナスステップは扱えません。 640 * ちなみに、指定を数値タイプのカラムを使用すると、カンマを、自動削除してしまいますので、文字カラムを 641 * リクエスト変数に使用してください。 642 * 643 * @og.rev 6.5.0.0 (2016/09/30) 数値型カラムの範囲指定(range)の追加。 644 * 645 * @param flag 範囲変換 [true:する/それ以外:しない] 646 */ 647 public void setRange( final String flag ) { 648 range = nval( getRequestParameter( flag ),range ); 649 } 650 651 /** 652 * 【TAG】multi アクション/instrVals 時の文字列を分割する項目区切り文字をセットします。 653 * 654 * @og.tag 655 * multi="true" の場合、複数のリクエストを連結して、in 句で問合せを行う文字列を 656 * 作成しますが、separator を指定すると、さらに、separator で文字列を分割して、 657 * in 句の引数を構築します。 658 * これは、instrVals を指定した場合にも、同様に分解します。 659 * 具体的には、分割後の文字列が、複数の個々のリクエスト変数と同じ形式に加工されます。 660 * String#split( separator ) で、分解するため、正規表現が使用できます。 661 * 662 * 何も指定しない場合は、multi アクション時は、分割処理は行いません。 663 * instrVals 時は、スペースで分解処理します。 664 * 665 * @og.rev 5.2.2.0 (2010/11/01) 新規追加 666 * @og.rev 6.1.1.0 (2015/01/17) コメント修正。separatorは、正規表現が使用できます。 667 * 668 * @param sepa 項目区切り文字(正規表現) 669 * @see #setMulti( String ) 670 */ 671 public void setSeparator( final String sepa ) { 672 separator = nval( getRequestParameter( sepa ),separator ); 673 } 674 675 /** 676 * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します 677 * (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 678 * 679 * @og.tag 680 * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに 681 * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。 682 * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、 683 * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、 684 * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。 685 * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。 686 * (初期値:システム定数のUSE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 687 * 688 * @og.rev 4.0.0.0 (2005/08/31) 新規追加 689 * 690 * @param flag クォートチェック [true:する/それ以外:しない] 691 * @see org.opengion.hayabusa.common.SystemData#USE_SQL_INJECTION_CHECK 692 */ 693 public void setQuotCheck( final String flag ) { 694 quotCheck = nval( getRequestParameter( flag ),quotCheck ); 695 } 696 697 /** 698 * 【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します 699 * (初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。 700 * 701 * @og.tag 702 * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。 703 * (><) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。 704 * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。 705 * 706 * @og.rev 5.0.0.2 (2009/09/15) 新規追加 707 * 708 * @param flag XSSチェック [true:する/false:しない] 709 * @see org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK 710 */ 711 public void setXssCheck( final String flag ) { 712 xssCheck = nval( getRequestParameter( flag ),xssCheck ); 713 } 714 715 /** 716 * タグの名称を、返します。 717 * 自分自身のクラス名より、自動的に取り出せないため、このメソッドをオーバーライドします。 718 * 719 * @og.rev 4.0.0.0 (2005/01/31) 新規追加 720 * 721 * @return タグの名称 722 * @og.rtnNotNull 723 */ 724 @Override 725 protected String getTagName() { 726 return "and" ; 727 } 728 729 /** 730 * このオブジェクトの文字列表現を返します。 731 * 基本的にデバッグ目的に使用します。 732 * 733 * @return このクラスの文字列表現 734 * @og.rtnNotNull 735 */ 736 @Override 737 public String toString() { 738 return ToString.title( this.getClass().getName() ) 739 .println( "VERSION" ,VERSION ) 740 .println( "startKey" ,startKey ) 741 .println( "value" ,value ) 742 .println( "instrVals" ,instrVals ) 743 .println( "multi" ,multi ) 744 .println( "quotCheck" ,quotCheck ) 745 .println( "Other..." ,getAttributes().getAttribute() ) 746 .fixForm().toString() ; 747 } 748}