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
024/**
025 * JavaDB(derby) や、hsqldb に対する、Javaの拡張組込み関数です。
026 *
027 * staticメソッドとして、関数を定義します。引数や返り値は、各データベースの
028 * 定義に準拠します。
029 *
030 * <pre>
031 * ① JavaDB の場合
032 * 【概要】
033 *     実行するデータベースから見えるところに、ファイルを配置する必要があります。
034 *     java8 までなら、Javaのエクステンション(JAVA_HOME\)jre\lib\ext などですが、
035 *     java9以降は、CLASSPATH に設定します。
036 *     openGionでは、bin/const.bat で、OG_CLASSPATH 環境変数にパスを通して、
037 *     使用しています。
038 *     標準の Java staticメソッドを FUNCTION 定義することも出来ます。
039 * 【設定】
040 *     JavaDBに FUNCTION を定義します。(サンプル)
041 *      DROP FUNCTION TO_CHAR;
042 *
043 *      CREATE FUNCTION TO_CHAR ( VAL DOUBLE )
044 *      RETURNS VARCHAR(20)
045 *      DETERMINISTIC           -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
046 *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
047 *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
048 *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ;
049 *
050 * ② HSQLDB の場合
051 * 【概要】
052 *
053 * </pre>
054 *
055 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
056 * @og.group 拡張組込み関数
057 *
058 * @version  1.1.0
059 * @author       Kazuhiko Hasegawa
060 * @since    JDK8.0,
061 */
062public final class Functions {
063    private static final String ENCODE = "UTF-8";
064
065        /**
066         *      デフォルトコンストラクターをprivateにして、
067         *      オブジェクトの生成をさせないようにする。
068         *
069         * @og.rev 6.9.7.0 (2018/05/14) 新規作成
070         */
071        private Functions() {}
072
073        /**
074         * 数値を文字列に変換します。
075         *
076         * この関数は、引数の double が、小数点を含まない場合は、
077         * 小数点以下を出しません。
078         * JavaDBの場合、数字と文字列の連結が出来ないため、文字列変換関数を用意します。
079         *
080         *      DROP FUNCTION TO_CHAR;
081         *
082         *      CREATE FUNCTION TO_CHAR ( VAL DOUBLE )
083         *      RETURNS VARCHAR(20)
084         *      DETERMINISTIC           -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
085         *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
086         *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
087         *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ;
088         *
089         * @og.rev 6.7.3.0 (2017/01/27) 新規作成
090         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
091         * @og.rev 6.9.8.0 (2018/05/28) FindBugs:浮動小数点の等価性のためのテスト
092         *
093         * @param       val     文字列に変換すべき数値
094         * @return      変換した文字列
095         */
096        public static String toChar( final double val ) {
097                //  6.9.8.0 (2018/05/28) FindBugs の警告を避ける方法が、見つかりませんでした。
098                return val == (int)val ? String.valueOf( (int)val ) : String.valueOf( val );
099
100//              final int intVal = (int)val;
101//              return ((double)intVal) == val ? String.valueOf( intVal ) : String.valueOf( val );
102        }
103
104        /**
105         * 特殊な文字列の連結を行います。
106         *
107         * これは、第1引数の数字と、第2、第3、第4の文字列をスペースで連結した文字列を返します。
108         * 引数の個数が、可変に出来ないため、完全に決め打ちです。
109         *
110         *      DROP FUNCTION JOIN2;
111         *
112         *      CREATE FUNCTION JOIN2 ( INTEGER , VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) )
113         *      RETURNS VARCHAR(4000)
114         *      DETERMINISTIC                   -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
115         *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
116         *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
117         *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.join2' ;
118         *
119         * @og.rev 6.7.3.0 (2017/01/27) 新規作成
120         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
121         *
122         * @param       no              第1引数の数字
123         * @param       arg2     第2引数
124         * @param       arg3    第3引数
125         * @param       arg4    第4引数
126         * @return      連結したした文字列
127         */
128        public static String join2( final int no , final String arg2 , final String arg3 , final String arg4 ) {
129                return new StringBuilder()
130                                .append( no ).append( ' ' )
131                                .append( arg2 == null ? "" : arg2 ).append( ' ' )
132                                .append( arg3 == null ? "" : arg3 ).append( ' ' )
133                                .append( arg4 == null ? "" : arg4 ).append( ' ' )
134                                .toString() ;
135        }
136
137        /**
138         * 対象の文字列の部分文字列を置換します。
139         *
140         * ただし、source、target、replacement のどれかが、null(ゼロ文字列)の場合は、
141         * 処理を実行せず、source をそのまま返します。
142         *
143         *      DROP FUNCTION REPLACE;
144         *
145         *      CREATE FUNCTION REPLACE ( VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) )
146         *      RETURNS VARCHAR(4000)
147         *      DETERMINISTIC                   -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
148         *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
149         *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
150         *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.replace' ;
151         *
152         * @og.rev 6.7.3.0 (2017/01/27) 新規作成
153         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
154         * @og.rev 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。
155         *
156         * @param       source 対象の文字列
157         * @param       target 置換したい文字列
158         * @param       replacement 置換する文字列
159         * @return      置換した文字列。
160         */
161        public static String replace( final String source , final String target , final String replacement ) {
162                // 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。
163                if( StringUtil.isEmpty( source , target , replacement ) ) {             // 一つでも、null の場合、true
164                        return source;
165                }
166                else {
167                        return source.replace( target,replacement );
168                }
169
170//              if( source != null && target != null || !target.isEmpty() && replacement != null && !replacement.isEmpty() ) {
171//                      return source.replace( target,replacement );
172//              }
173//              else {
174//                      return source;
175//              }
176        }
177
178//      /**
179//       * この文字列内にあるすべてのoldCharをnewCharに置換した結果生成される文字列を返します。
180//       *
181//       * String#replace( char , char )を、繰り返します。
182//       *
183//       * @og.rev 6.7.9.0 (2017/04/28) 新規作成
184//       *
185//       * @param       source 対象の文字列
186//       * @param       target 以前の文字の集合
187//       * @param       replacement 置換する文字の集合
188//       * @return      置換した文字列。
189//       */
190//      public static String translate( final String source , final String target , final String replacement ) {
191//              String rtn = source ;
192//
193//              if( source != null && target != null && replacement != null && target.length() == replacement.length() ) {
194//                      for( int i=0; i<target.length(); i++ ) {
195//                              rtn = rtn.replace( target.charAt(i) , replacement.charAt(i) );
196//                      }
197//              }
198//
199//              return rtn;
200//      }
201
202        /**
203         * substr関数のバイト数版
204         * 過去に、hsqldb 用に作成したJava関数です。
205         *
206         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
207         *
208         * @param       value            変換する文字列
209         * @param       start            変換開始アドレス
210         * @param       length           変換バイト数
211         * @return      変換後文字列
212         * @throws      UnsupportedEncodingException    文字のエンコーディングがサポートされていません。
213         */
214        public static String substrb( final String value, final int start, final int length ) throws UnsupportedEncodingException {
215                String rtn = null;
216                final byte[] byteValue = makeByte( value );
217                if( byteValue != null ) {
218                        rtn = new String( byteValue,start-1,length,ENCODE );
219                }
220                return rtn;
221        }
222
223        /**
224         * length関数のバイト数版
225         * 過去に、hsqldb 用に作成したJava関数です。
226         *
227         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
228         *
229         * @param       value           バイト数をカウントする文字列
230         * @return      バイト数
231         * @throws      UnsupportedEncodingException    文字のエンコーディングがサポートされていません。
232         */
233        public static int lengthb( final String value ) throws UnsupportedEncodingException {
234                return makeByte( value ).length;
235        }
236
237        /**
238         * 指定の文字列をバイトコードに変換します。
239         * 引数の文字列が null の場合は、return は、byte[0] を返します。
240         *
241         * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
242         *
243         * @param       value    変換するストリング値
244         * @param       encode   変換する文字エンコード
245         * @return      変換後文字列
246         */
247        private static byte[] makeByte( final String value ) throws UnsupportedEncodingException {
248                byte[] rtnByte = new byte[0];
249                if( value != null ) {
250                        rtnByte = value.getBytes( ENCODE );
251                }
252                return rtnByte;
253        }
254
255        /**
256         * 日時文字列(yyyyMMddHHmmss)の引数1,2に対して、差分の範囲判定を行います。
257         *
258         * 範囲判定を行う引数 sec1、sec2、sec3 引数には、それぞれ(秒)レベルの値を指定します。
259         * 上記の引数の値が、0 の場合は、判定を回避します。
260         *
261         * sec1引数と比べて、小さければ 0 を、大きければ 1 を返します。
262         * sec2引数と比べて、小さければ sec1引数の結果を、大きければ 2 を返します。
263         * sec3引数と比べて、小さければ sec2引数の結果を、大きければ 3 を返します。
264         * 
265         * 通常、sec1、sec2、sec3 と、順番に大きな値にしておきます。
266         * 小さいと判定された時点で、判定処理は終了します。
267         * 
268         * date2 - date1 <= sec1 ⇒ 0
269         *               >  sec1 ⇒ 1
270         *               >  sec2 ⇒ 2
271         *               >  sec3 ⇒ 3
272         *
273         * date1,date2 のどちらかが、null,ゼロ文字列の場合は、判定しません。(return "0")
274         *
275         * @og.rev 7.0.5.0 (2019/09/16) 新規作成
276         *
277         * @param       date1   前比較日時文字列(yyyyMMddHHmmss)
278         * @param       date2   後比較日時文字列(yyyyMMddHHmmss)
279         * @param       sec1    比較する秒
280         * @param       sec2    比較する秒
281         * @param       sec3    比較する秒
282         * @return      判定結果
283         */
284        public static String checkDelay( final String date1 , final String date2 , final double sec1 , final double sec2 , final double sec3 ) {
285                if( StringUtil.isNull( date1,date2 ) ) { return "0"; }          // どちらかが、null,ゼロ文字列の場合は、判定しません。
286
287                final Calendar cal1 = HybsDateUtil.getCalendar( date1 );
288                final Calendar cal2 = HybsDateUtil.getCalendar( date2 );
289
290                final double diff = ( cal2.getTimeInMillis() - cal1.getTimeInMillis() )/1000.0;                 // 秒に変換しておきます。
291
292                String rtn = "0";
293                if( sec1 > 0.0 ) {
294                        if( diff <= sec1 ) { return rtn; }
295                        else { rtn = "1"; }
296                }
297
298                if( sec2 > 0.0 ) {
299                        if( diff <= sec2 ) { return rtn; }
300                        else { rtn = "2"; }
301                }
302
303                if( sec3 > 0.0 ) {
304                        if( diff <= sec3 ) { return rtn; }
305                        else { rtn = "3"; }
306                }
307
308                return rtn;
309        }
310}