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.fukurou.db; 017 018import java.io.UnsupportedEncodingException; 019import java.util.Calendar; // 7.0.5.0 (2019/09/16) 020 021import org.opengion.fukurou.util.StringUtil; // 6.9.8.0 (2018/05/28) 022import org.opengion.fukurou.util.HybsDateUtil; // 7.0.5.0 (2019/09/16) 023 024import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 7.2.9.5 (2020/11/28) 025 026/** 027 * JavaDB(derby) や、hsqldb に対する、Javaの拡張組込み関数です。 028 * 029 * staticメソッドとして、関数を定義します。引数や返り値は、各データベースの 030 * 定義に準拠します。 031 * 032 * <pre> 033 * ① JavaDB の場合 034 * 【概要】 035 * 実行するデータベースから見えるところに、ファイルを配置する必要があります。 036 * java8 までなら、Javaのエクステンション(JAVA_HOME\)jre\lib\ext などですが、 037 * java9以降は、CLASSPATH に設定します。 038 * openGionでは、bin/const.bat で、OG_CLASSPATH 環境変数にパスを通して、 039 * 使用しています。 040 * 標準の Java staticメソッドを FUNCTION 定義することも出来ます。 041 * 【設定】 042 * JavaDBに FUNCTION を定義します。(サンプル) 043 * DROP FUNCTION TO_CHAR; 044 * 045 * CREATE FUNCTION TO_CHAR ( VAL DOUBLE ) 046 * RETURNS VARCHAR(20) 047 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 048 * PARAMETER STYLE JAVA -- 戻り値のタイプ 049 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 050 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ; 051 * 052 * ② HSQLDB の場合 053 * 【概要】 054 * 055 * </pre> 056 * 057 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更 058 * @og.group 拡張組込み関数 059 * 060 * @version 1.1.0 061 * @author Kazuhiko Hasegawa 062 * @since JDK8.0, 063 */ 064public final class Functions { 065 private static final String ENCODE = "UTF-8"; 066 067 /** 068 * デフォルトコンストラクターをprivateにして、 069 * オブジェクトの生成をさせないようにする。 070 * 071 * @og.rev 6.9.7.0 (2018/05/14) 新規作成 072 */ 073 private Functions() {} 074 075 /** 076 * 数値を文字列に変換します。 077 * 078 * この関数は、引数の double が、小数点を含まない場合は、 079 * 小数点以下を出しません。 080 * JavaDBの場合、数字と文字列の連結が出来ないため、文字列変換関数を用意します。 081 * 082 * DROP FUNCTION TO_CHAR; 083 * 084 * CREATE FUNCTION TO_CHAR ( VAL DOUBLE ) 085 * RETURNS VARCHAR(20) 086 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 087 * PARAMETER STYLE JAVA -- 戻り値のタイプ 088 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 089 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ; 090 * 091 * @og.rev 6.7.3.0 (2017/01/27) 新規作成 092 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更 093 * @og.rev 6.9.8.0 (2018/05/28) FindBugs:浮動小数点の等価性のためのテスト 094 * 095 * @param val 文字列に変換すべき数値 096 * @return 変換した文字列 097 */ 098 public static String toChar( final double val ) { 099 // 6.9.8.0 (2018/05/28) FindBugs の警告を避ける方法が、見つかりませんでした。 100 return val == (int)val ? String.valueOf( (int)val ) : String.valueOf( val ); 101 102// final int intVal = (int)val; 103// return ((double)intVal) == val ? String.valueOf( intVal ) : String.valueOf( val ); 104 } 105 106 /** 107 * 特殊な文字列の連結を行います。 108 * 109 * これは、第1引数の数字と、第2、第3、第4の文字列をスペースで連結した文字列を返します。 110 * 引数の個数が、可変に出来ないため、完全に決め打ちです。 111 * 112 * DROP FUNCTION JOIN2; 113 * 114 * CREATE FUNCTION JOIN2 ( INTEGER , VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) ) 115 * RETURNS VARCHAR(4000) 116 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 117 * PARAMETER STYLE JAVA -- 戻り値のタイプ 118 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 119 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.join2' ; 120 * 121 * @og.rev 6.7.3.0 (2017/01/27) 新規作成 122 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更 123 * 124 * @param no 第1引数の数字 125 * @param arg2 第2引数 126 * @param arg3 第3引数 127 * @param arg4 第4引数 128 * @return 連結したした文字列 129 */ 130 public static String join2( final int no , final String arg2 , final String arg3 , final String arg4 ) { 131 return new StringBuilder( BUFFER_MIDDLE ) 132 .append( no ).append( ' ' ) 133 .append( arg2 == null ? "" : arg2 ).append( ' ' ) 134 .append( arg3 == null ? "" : arg3 ).append( ' ' ) 135 .append( arg4 == null ? "" : arg4 ) 136 .toString() ; 137 } 138 139 /** 140 * 連結文字列を指定した、文字列の連結を行います。 141 * 142 * これは、第1引数の連結文字列を使用して、第2、第3、第4の文字列を連結します。 143 * 引数の個数が、可変に出来ないため、第2、第3、第4の文字列が、null か、ゼロ文字列の 144 * 場合は、連結せずにパスします。 145 * 146 * DROP FUNCTION JOIN3; 147 * 148 * CREATE FUNCTION JOIN3 ( VARCHAR(10) , VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) ) 149 * RETURNS VARCHAR(4000) 150 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 151 * PARAMETER STYLE JAVA -- 戻り値のタイプ 152 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 153 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.join3' ; 154 * 155 * @og.rev 8.0.2.0 (2021/11/30) 新規作成 156 * 157 * @param sep 連結時の文字列 158 * @param arg2 第2引数 159 * @param arg3 第3引数 160 * @param arg4 第4引数 161 * @return 連結したした文字列 162 */ 163 public static String join3( final String sep , final String arg2 , final String arg3 , final String arg4 ) { 164 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) ; 165 166 boolean useSep = false; // 連結文字列を使うかどうか。直前の文字列が null 等の場合は、連結しません。 167 if( arg2 != null && !arg2.isEmpty() ) { 168 buf.append( arg2 ); 169 useSep = true; 170 } 171 172 if( arg3 != null && !arg3.isEmpty() ) { 173 if( useSep ) { buf.append( sep ); } 174 buf.append( arg3 ); 175 useSep = true; 176 } 177 178 if( arg4 != null && !arg4.isEmpty() ) { 179 if( useSep ) { buf.append( sep ); } 180 buf.append( arg4 ); 181 } 182 183 return buf.toString() ; 184 } 185 186 /** 187 * 対象の文字列の部分文字列を置換します。 188 * 189 * ただし、source、target、replacement のどれかが、null(ゼロ文字列)の場合は、 190 * 処理を実行せず、source をそのまま返します。 191 * 192 * DROP FUNCTION REPLACE; 193 * 194 * CREATE FUNCTION REPLACE ( VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) ) 195 * RETURNS VARCHAR(4000) 196 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 197 * PARAMETER STYLE JAVA -- 戻り値のタイプ 198 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 199 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.replace' ; 200 * 201 * @og.rev 6.7.3.0 (2017/01/27) 新規作成 202 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更 203 * @og.rev 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。 204 * 205 * @param source 対象の文字列 206 * @param target 置換したい文字列 207 * @param replacement 置換する文字列 208 * @return 置換した文字列。 209 */ 210 public static String replace( final String source , final String target , final String replacement ) { 211 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 212 // 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。 213 return StringUtil.isEmpty( source , target , replacement ) // 一つでも、null の場合、true 214 ? source 215 : source.replace( target,replacement ); 216 217// // 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。 218// if( StringUtil.isEmpty( source , target , replacement ) ) { // 一つでも、null の場合、true 219// return source; 220// } 221// else { 222// return source.replace( target,replacement ); 223// } 224 225// if( source != null && target != null || !target.isEmpty() && replacement != null && !replacement.isEmpty() ) { 226// return source.replace( target,replacement ); 227// } 228// else { 229// return source; 230// } 231 } 232 233// /** 234// * この文字列内にあるすべてのoldCharをnewCharに置換した結果生成される文字列を返します。 235// * 236// * String#replace( char , char )を、繰り返します。 237// * 238// * @og.rev 6.7.9.0 (2017/04/28) 新規作成 239// * 240// * @param source 対象の文字列 241// * @param target 以前の文字の集合 242// * @param replacement 置換する文字の集合 243// * @return 置換した文字列。 244// */ 245// public static String translate( final String source , final String target , final String replacement ) { 246// String rtn = source ; 247// 248// if( source != null && target != null && replacement != null && target.length() == replacement.length() ) { 249// for( int i=0; i<target.length(); i++ ) { 250// rtn = rtn.replace( target.charAt(i) , replacement.charAt(i) ); 251// } 252// } 253// 254// return rtn; 255// } 256 257 /** 258 * substr関数のバイト数版 259 * 過去に、hsqldb 用に作成したJava関数です。 260 * 261 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更 262 * ※ 現在未使用 263 * 264 * @param value 変換する文字列 265 * @param start 変換開始アドレス 266 * @param length 変換バイト数 267 * @return 変換後文字列 268 * @throws UnsupportedEncodingException 文字のエンコーディングがサポートされていません。 269 */ 270 public static String substrb( final String value, final int start, final int length ) throws UnsupportedEncodingException { 271 String rtn = null; 272 final byte[] byteValue = makeByte( value ); 273 if( byteValue != null ) { 274 rtn = new String( byteValue,start-1,length,ENCODE ); 275 } 276 return rtn; 277 } 278 279 /** 280 * length関数のバイト数版 281 * 過去に、hsqldb 用に作成したJava関数です。 282 * 283 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更 284 * ※ 現在未使用 285 * 286 * @param value バイト数をカウントする文字列 287 * @return バイト数 288 * @throws UnsupportedEncodingException 文字のエンコーディングがサポートされていません。 289 */ 290 public static int lengthb( final String value ) throws UnsupportedEncodingException { 291 return makeByte( value ).length; 292 } 293 294 /** 295 * 指定の文字列をバイトコードに変換します。 296 * 引数の文字列が null の場合は、return は、byte[0] を返します。 297 * 298 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更 299 * 300 * @param value 変換するストリング値 301 * @return 変換後文字列 302 * @throws UnsupportedEncodingException エンコードが不正な場合 303 */ 304 private static byte[] makeByte( final String value ) throws UnsupportedEncodingException { 305 byte[] rtnByte = new byte[0]; 306 if( value != null ) { 307 rtnByte = value.getBytes( ENCODE ); 308 } 309 return rtnByte; 310 } 311 312 /** 313 * 日時文字列(yyyyMMddHHmmss)の引数1,2に対して、差分の範囲判定を行います。 314 * 315 * 範囲判定を行う引数 sec1、sec2、sec3 引数には、それぞれ(秒)レベルの値を指定します。 316 * 上記の引数の値が、0 の場合は、判定を回避します。 317 * 318 * sec1引数と比べて、小さければ 0 を、大きければ 1 を返します。 319 * sec2引数と比べて、小さければ sec1引数の結果を、大きければ 2 を返します。 320 * sec3引数と比べて、小さければ sec2引数の結果を、大きければ 3 を返します。 321 * 322 * 通常、sec1、sec2、sec3 と、順番に大きな値にしておきます。 323 * 小さいと判定された時点で、判定処理は終了します。 324 * 325 * date2 - date1 <= sec1 ⇒ 0 326 * > sec1 ⇒ 1 327 * > sec2 ⇒ 2 328 * > sec3 ⇒ 3 329 * 330 * date1,date2 のどちらかが、null,ゼロ文字列の場合は、判定しません。(return "0") 331 * 332 * @og.rev 7.0.5.0 (2019/09/16) 新規作成 333 * ※ 現在未使用 334 * 335 * @param date1 前比較日時文字列(yyyyMMddHHmmss) 336 * @param date2 後比較日時文字列(yyyyMMddHHmmss) 337 * @param sec1 比較する秒 338 * @param sec2 比較する秒 339 * @param sec3 比較する秒 340 * @return 判定結果 341 */ 342 public static String checkDelay( final String date1 , final String date2 , final double sec1 , final double sec2 , final double sec3 ) { 343 if( StringUtil.isNull( date1,date2 ) ) { return "0"; } // どちらかが、null,ゼロ文字列の場合は、判定しません。 344 345 final Calendar cal1 = HybsDateUtil.getCalendar( date1 ); 346 final Calendar cal2 = HybsDateUtil.getCalendar( date2 ); 347 348 final double diff = ( cal2.getTimeInMillis() - cal1.getTimeInMillis() )/1000.0; // 秒に変換しておきます。 349 350 String rtn = "0"; 351 if( sec1 > 0.0 ) { 352 if( diff <= sec1 ) { return rtn; } 353 else { rtn = "1"; } 354 } 355 356 if( sec2 > 0.0 ) { 357 if( diff <= sec2 ) { return rtn; } 358 else { rtn = "2"; } 359 } 360 361 if( sec3 > 0.0 ) { 362 if( diff <= sec3 ) { return rtn; } 363 else { rtn = "3"; } 364 } 365 366 return rtn; 367 } 368 369 /** 370 * 部分文字列の出現位置を検索します。 371 * 第1引数の検索元の文字列と、第2引数の部分文字列を指定します。 372 * 戻り値は、Javaの indexOf とは異なり、+1 された値が返ります。 373 * 374 * DROP FUNCTION INSTR; 375 * 376 * CREATE FUNCTION INSTR ( VARCHAR(1000) , VARCHAR(100) ) 377 * RETURNS INTEGER 378 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 379 * PARAMETER STYLE JAVA -- 戻り値のタイプ 380 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 381 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.instr' ; 382 * 383 * @og.rev 7.3.0.0 (2021/01/06) 新規追加 384 * 385 * @param value 検索元の文字列 386 * @param sub 部分文字列 387 * @return 文字列が見つかった位置(最初が1、見つからなければ 0 ) 388 */ 389 public static int instr( final String value,final String sub ) { 390 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 391 return ( value == null || value.isEmpty() ) 392 ? 0 393 : value.indexOf( sub ) + 1 ; 394 395// if( value == null || value.isEmpty() ) { 396// return 0 ; 397// } 398// else { 399// return value.indexOf( sub ) + 1 ; 400// } 401 } 402 403 /** 404 * 文字列を連結します。 405 * 406 * 文字列が、NULL の場合は、ゼロ文字列として扱います。 407 * 引数がどちらも NULL の場合は、ゼロ文字列が戻されます。 408 * これは、ORACLEとの互換性を考慮した関数です。 409 * ORACLE は、NULLとゼロ文字列を同等にNULLとして扱うため、 410 * COALESCE( VAL1,'' ) で、VAL1がNULLか、ゼロ文字列の場合は、NULLが返されます。 411 * NULLを含むWHERE条件で、WHERE COALESCE( VAL1,'' ) = COALESCE( VAL2,'' ) 412 * としても、一致しないため、WHERE COALESCE( VAL1,'x' ) = COALESCE( VAL2,'x' ) 413 * とする必要がありますが、VAL1が、実際の 'x' で、VAL2がNULLの場合もマッチします。 414 * 一方、Derbyでは、NULLとゼロ文字列を区別するため、COALESCE( VAL1,'x' ) の 415 * VAL1の値が、NULLとゼロ文字列で、結果が変わってしまいます。 416 * また、|| による文字列連結も、ORACLEとDerbyでは結果が変わるため、 417 * CONCAT関数を作成して、ORACLEと同じSQL文での比較ができるようにしておきます。 418 * 419 * DROP FUNCTION CONCAT; 420 * 421 * CREATE FUNCTION CONCAT ( VARCHAR(1000) , VARCHAR(1000) ) 422 * RETURNS VARCHAR(2000) 423 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 424 * PARAMETER STYLE JAVA -- 戻り値のタイプ 425 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 426 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.concat' ; 427 * 428 * @og.rev 7.3.0.0 (2021/01/06) 新規追加 429 * 430 * @param val1 文字列1 431 * @param val2 文字列2 432 * @return 文字列1と文字列2を連結した文字列 433 */ 434 public static String concat( final String val1,final String val2 ) { 435 return new StringBuilder( BUFFER_MIDDLE ) 436 .append( val1 == null ? "" : val1 ) 437 .append( val2 == null ? "" : val2 ) 438 .toString() ; 439 } 440 441 /** 442 * NULL文字列の判定変換を行います。 443 * 444 * 文字列が、NULL(または空文字列)は、文字列2を、NULLでない場合は、文字列1を返します。 445 * Derbyでは、NULLと空文字列が区別されますが、ORACLE等では、区別されないため、 446 * この関数でも区別しません。 447 * 空文字列の場合は、NULLと同じになります。 448 * 449 * DROP FUNCTION NVL2; 450 * 451 * CREATE FUNCTION NVL2 ( VARCHAR(1000) , VARCHAR(1000) , VARCHAR(1000) ) 452 * RETURNS VARCHAR(1000) 453 * DETERMINISTIC -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic) 454 * PARAMETER STYLE JAVA -- 戻り値のタイプ 455 * NO SQL LANGUAGE JAVA -- 関数の中でSQLは実行しないことを示す 456 * EXTERNAL NAME 'org.opengion.fukurou.db.Functions.nvl2' ; 457 * 458 * @og.rev 7.3.1.3 (2021/03/09) 新規追加 459 * 460 * @param inval 判定する文字列 461 * @param val1 invalがNULL(または空文字列)でない場合に返す文字列1 462 * @param val2 invalがNULL(または空文字列)の場合に返す文字列2 463 * @return 文字列がNULLの場合は、val2 を、そうでない場合は、val1 を返す。 464 */ 465 public static String nvl2( final String inval,final String val1,final String val2 ) { 466 return inval == null || inval.isEmpty() ? val2 : val1 ; 467 } 468}