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.db;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.fukurou.util.StringUtil;
020import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
021
022/**
023 * DBカラムの属性チェックに使用されるメソッドを集約した、クラスです。
024 *
025 * 全変数は、public static final 宣言されており、全メソッドは、public static synchronized 宣言されています。
026 *
027 * @og.group データ属性
028 *
029 * @version  4.0
030 * @author       Kazuhiko Hasegawa
031 * @since    JDK5.0,
032 */
033public final class DBTypeCheckUtil {
034        /** String を Byte[]に変換するコード
035         * 例:) "MS932" , "JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
036         */
037        private static final String CODE = HybsSystem.sys( "DB_ENCODE" );
038
039        // 5.3.9.0 (2011/09/01) 文字数チェック方式の指定
040        // 6.1.0.0 (2014/12/26) refactoring
041        private static final boolean USE_TEXT_LEN = HybsSystem.sysBool( "DB_USE_TEXT_LENGTH" );
042
043        /**
044         * オブジェクトを作らせない為の、private コンストラクタ
045         */
046        private DBTypeCheckUtil() {}
047
048        /**
049         * 文字列に使われている文字の範囲チェックを行います。
050         *
051         * 最小文字から最大文字、および、許可される文字を指定します。
052         * それ以外は、エラーと判定されます。
053         * ここで判定される以外に細かい制限をかけたい場合は、別のチェックと併用してください。
054         *
055         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
056         *
057         * @param       value   元の文字列
058         * @param       minCh   許可される文字の最小値(含む)
059         * @param       maxCh   許可される文字の最大値(含む)
060         *
061         * @return      範囲チェックエラー文字列(正常時は、null)
062         */
063        public static String rangeCheck( final String value ,final char minCh ,final char maxCh ) {
064                final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
065                boolean isError = false;
066                for( int i=0; i<value.length(); i++ ) {
067                        final char ch = value.charAt( i );
068                        if( minCh <= ch && ch <= maxCh ) {
069                                val.append( ch );
070                        }
071                        else {
072                                val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
073                                isError = true;
074                        }
075                }
076
077                return isError ? val.toString() : null ;
078        }
079
080        /**
081         * 文字列の長さ(整数部)をチェックします。
082         *
083         * @param       value 元の文字列
084         * @param       sizeX    整数部分の文字列の長さ
085         * @param       sizeY    小数部分の文字列の長さ
086         *
087         * @return      エラー文字列長さ(正常時は、null)
088         */
089        public static String sizeXCheck( final String value ,final int sizeX ,final int sizeY ) {
090                int valuesizeX;
091                final int pos = value.indexOf( '.' );
092                if( pos >= 0 ) {
093                        valuesizeX = pos;
094                }
095                else {
096                        valuesizeX = value.length();
097                }
098                if( value.charAt(0) == '-' ) { valuesizeX--; }
099
100                // 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
101                return valuesizeX > sizeX
102                                        ? String.valueOf(valuesizeX)            // 整数部の長さが指定の長さよりも長いです。
103                                        : null;
104        }
105
106        /**
107         * 文字列の長さ(小数部)をチェックします。
108         *
109         * @param       value 元の文字列
110         * @param       sizeX    整数部分の文字列の長さ
111         * @param       sizeY    小数部分の文字列の長さ
112         *
113         * @return      エラー文字列長さ(正常時は、null)
114         */
115        public static String sizeYCheck( final String value ,final int sizeX ,final int sizeY ) {
116                if( sizeY == 0 ) {
117                        return null;
118                }
119                int valuesizeY;
120                final int pos = value.indexOf( '.' );
121                if( pos >= 0 ) {
122                        valuesizeY = value.length() - pos - 1;
123                }
124                else {
125                        valuesizeY = 0;
126                }
127
128                if( valuesizeY > sizeY ) {
129                        // 小数部の長さが指定の長さよりも長いです。
130                        return String.valueOf(valuesizeY);
131                } else {
132                        return null;
133                }
134        }
135
136        /**
137         * 文字列の小数点の位置をチェックします。
138         * 小数点(.)が、2箇所以上存在する(存在する位置が異なる)場合エラー
139         *
140         * @param       value 元の文字列
141         *
142         * @return      エラー文字列(正常時は、null)
143         */
144        public static String decimalPointCheck( final String value ) {
145                String rtn = null;
146                if( value.indexOf( '.' ) != value.lastIndexOf( '.' ) ) {
147                        rtn = changeErrorPath( value, '.' );
148                }
149                return rtn ;
150        }
151
152        /**
153         * 文字列の符号の位置をチェックします。
154         * マイナス(-)が、存在しないか、先頭以外の場合は、エラー
155         *
156         * @param       value 元の文字列
157         *
158         * @return      エラー文字列(正常時は、null)
159         */
160        public static String decimalCodeCheck( final String value ) {
161                String rtn = null;
162                if( value.lastIndexOf( '-' ) > 0 ) {
163                        rtn = changeErrorPath( value, '-' );
164                }
165                return rtn ;
166        }
167
168        /**
169         * 文字列の整合性(整数)をチェックします。
170         * 0~9およびマイナス(-)を許可します。
171         *
172         * @param       value 元の文字列
173         *
174         * @return      エラー文字列(正常時は、null)
175         */
176        public static String numberFormatCheck( final String value ) {
177                boolean isError = false;
178                int i = 0;
179                char ch;
180                while( i<value.length() ) {
181                        ch = value.charAt( i );
182                        if( ( '0'>ch || '9'<ch ) && '-'!=ch ) {
183                                isError = true;
184                                break;
185                        }
186                        i++;
187                }
188                if( isError ) {
189                        final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
190                        for( i=0; i<value.length(); i++ ) {
191                                ch = value.charAt( i );
192                                if( ( '0'>ch || '9'<ch ) &&  '-' != ch ) {
193                                        val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
194                                }
195                                else {
196                                        val.append( ch );
197                                }
198                        }
199                        return val.toString();
200                } else {
201                        return null;
202                }
203        }
204
205        /**
206         * 文字列の整合性(小数)をチェックします。
207         * 0~9、マイナス(-)および小数点(.)を許可します。
208         *
209         * og.rev 4.2.4.0 (2008/06/26) '.' or '-' のみはエラー
210         *
211         * @param       value 元の文字列
212         *
213         * @return      エラー文字列(正常時は、null)
214         */
215        public static String decimalFormatCheck( final String value ) {
216                boolean isError = false;
217                int i = 0;
218                char ch;
219                while( i<value.length() ) {
220                        ch = value.charAt( i );
221                        if( ( '0'>ch || '9'<ch ) && '.'!=ch && '-'!=ch ) {
222                                isError = true;
223                                break;
224                        }
225                        i++;
226                }
227
228                // 4.2.4.0 (2008/06/26) '.' or '-' のみはエラー
229                if( value.length() ==1 && ( value.charAt(0) == '.' || value.charAt(0) == '-' ) ) {
230                        isError = true;
231                }
232
233                if( isError ) {
234                        final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
235                        for( i=0; i<value.length(); i++ ) {
236                                ch = value.charAt( i );
237                                if( ( '0'>ch || '9'<ch ) && '.'!=ch && '-'!=ch ) {
238                                        val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
239                                }
240                                else {
241                                        val.append( ch );
242                                }
243                        }
244                        return val.toString();
245                } else {
246                        return null;
247                }
248        }
249
250        /**
251         * 日付文字列の整合性をチェックします。
252         *
253         * 整合性といっても、DBType_DATE のような厳密なチェックは、行いません。
254         * ここでは、yyyyMM(6桁)、yyyyMMdd(8桁)、yyyyMMddHHmmss(14桁) の3種類のみ
255         * 対象にします。
256         * "0000XXXX" , "9999XXXX" は、常に許可されます。
257         * 月と日の関係も、ありません。(20130231 は OK)
258         * あくまで、月は、1~12 の範囲、日は、1~31の範囲チェックです。
259         *
260         * 厳密な日付チェックを行いたい場合は、DBType_DATE を使用してください。
261         *
262         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
263         * @og.rev 7.2.5.1 (2020/06/05) 数値変換エラーの前にチェックを入れます。
264         *
265         * @param       value 元の文字列(nullは不可)
266         *
267         * @return      エラー文字列(正常時は、null)
268         */
269        public static String ymdFormatCheck( final String value ) {
270                if( value.startsWith( "0000" ) || value.startsWith( "9999" ) ) { return null; }         // 無条件 OK
271
272                // 7.2.5.1 (2020/06/05) 数値変換エラーの前にチェックを入れます。
273                final String str = rangeCheck( value, '0', '9' );
274                if( str != null ) { return str; }
275
276                final int len = value.length() ;
277                if( len >= 6 ) {        // 月のチェック
278                        final String val = ymdhmsCheck( value,4,6,1,12 );
279                        if( val != null ) { return val; }
280                }
281
282                if( len >= 8 ) {        // 日のチェック
283                        final String val = ymdhmsCheck( value,6,8,1,31 );
284                        if( val != null ) { return val; }
285                }
286
287                if( len >= 10 ) {       // 時のチェック
288                        final String val = ymdhmsCheck( value,8,10,0,24 );              // 240000 は許可します。
289                        if( val != null ) { return val; }
290                }
291
292                if( len >= 12 ) {       // 分のチェック
293                        final String val = ymdhmsCheck( value,10,12,0,60 );             // 60分は許可します。
294                        if( val != null ) { return val; }
295                }
296
297                if( len == 14 ) {       // 秒のチェック
298                        final String val = ymdhmsCheck( value,12,14,0,60 );             // うるう秒とは言いませんが、60秒は許可します。
299                        if( val != null ) { return val; }
300                }
301
302                return null;
303        }
304
305        /**
306         * 時刻文字列の整合性をチェックします。
307         *
308         * 整合性といっても、DBType_DATE のような厳密なチェックは、行いません。
309         * ここでは、HHmmss(6桁) のみ対象にします。
310         *
311         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
312         * @og.rev 7.2.5.1 (2020/06/05) 数値変換エラーの前にチェックを入れます。
313         *
314         * @param       value 元の文字列(nullは不可)
315         *
316         * @return      エラー文字列(正常時は、null)
317         */
318        public static String hmsFormatCheck( final String value ) {
319                // 7.2.5.1 (2020/06/05) 数値変換エラーの前にチェックを入れます。
320                final String str = rangeCheck( value, '0', '9' );
321                if( str != null ) { return str; }
322
323                final int len = value.length() ;
324
325                if( len >= 2 ) {        // 時のチェック
326                        final String val = ymdhmsCheck( value,0,2,0,24 );               // 240000 は許可します。
327                        if( val != null ) { return val; }
328                }
329
330                if( len >= 4 ) {        // 分のチェック
331                        final String val = ymdhmsCheck( value,2,4,0,60 );               // 60分は許可します。
332                        if( val != null ) { return val; }
333                }
334
335                if( len == 6 ) {        // 秒のチェック
336                        final String val = ymdhmsCheck( value,4,6,0,60 );               // うるう秒とは言いませんが、60秒は許可します。
337                        if( val != null ) { return val; }
338                }
339
340                return null;
341        }
342
343        /**
344         * 月、日、時、分、秒 のチェック用メソッド
345         *
346         * 同じようなパターンでチェックする為、共通メソッド化しておきます。
347         *
348         * @og.rev 7.2.5.1 (2020/06/05) 数値変換エラーの前にチェックを入れます。
349         *
350         * @param       value   元の文字列
351         * @param       st              チェック開始桁数
352         * @param       ed              チェック終了桁数 (val.length() を超えない事)
353         * @param       minSu   許可範囲の最小値
354         * @param       maxSu   許可範囲の最大値
355         *
356         * @return      エラー文字列(正常な場合は、null)
357         */
358//      public static String ymdhmsCheck( final String value, final int st , final int ed , final int minSu , final int maxSu ) {
359        private static String ymdhmsCheck( final String value, final int st , final int ed , final int minSu , final int maxSu ) {
360                String rtn = null;
361
362                final int dt = Integer.parseInt( value.substring( st,ed ) );
363                if( dt < minSu || maxSu < dt ) {
364                        rtn = value.substring( 0,st ) + "<span class=\"NG\">" + value.substring( st,ed ) + "</span>" + value.substring( ed ) ;
365                }
366                return rtn;
367        }
368
369        /**
370         * 文字列のエラー文字列を返します。
371         *
372         * @param       val     元の文字列
373         * @param       inChar  エラー対象文字
374         *
375         * @return      エラー文字列
376         * @og.rtnNotNull
377         */
378        private static String changeErrorPath( final String val, final char inChar ) {
379                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
380                for( int i=0; i<val.length(); i++ ) {
381                        final char ch = val.charAt( i );
382                        if( inChar==ch ) {
383                                buf.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
384                        } else {
385                                buf.append( ch );
386                        }
387                }
388                return buf.toString();
389        }
390
391        /**
392         * 文字列の長さをチェックします。
393         * バイト数に換算して比較チェックします。
394         *
395         * @og.rev 3.0.1.3 (2003/03/11) メソッド新規追加
396         * @og.rev 3.5.5.3 (2004/04/09) StringUtil の CODE を使用したメソッドを削除する。
397         * @og.rev 5.3.9.0 (2011/09/01) DB_USE_TEXT_LENGTH を考慮した、「文字数」、「バイト数」チェック
398         *
399         * @param       value 元の文字列
400         * @param       len      文字列の長さ
401         *
402         * @return      エラー文字列(正常時は、null)
403         */
404        public static String byteLengthCheck( final String value ,final int len ) {
405                String rtn = null;
406
407                // 5.3.9.0 (2011/09/01) 「文字数」、「バイト数」チェック
408                final int valLen ;
409                if( USE_TEXT_LEN ) {    // true:「文字数」チェック方式
410                        valLen = value.length();
411                }
412                else {                                  // false:「バイト数」チェック方式
413                        final byte[] byteValue = StringUtil.makeByte( value,CODE );     // 3.5.5.3 (2004/04/09)
414                        valLen = byteValue.length;
415                }
416
417                if( valLen > len ) {
418                        rtn = String.valueOf( valLen );
419                }
420
421                return rtn ;
422        }
423
424        /**
425         * 文字列の整合性を、dbType パラメータを利用してチェックします。
426         * regex が、null または、長さゼロの文字列の場合は、なにもしません。
427         *
428         * @og.rev 3.6.0.0 (2004/09/22) 新規作成
429         *
430         * @param       value 元の文字列
431         * @param       regex チェックする正規表現文字列
432         *
433         * @return      エラー文字列(正常時は、null)
434         */
435        public static String matcheCheck( final String value,final String regex ) {
436                // 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
437                // 条件変更注意
438                return regex == null || regex.isEmpty() || value.matches( regex )
439                                        ? null
440                                        : "<span class=\"NG\">" + value + "</span> regex=" + regex ;
441        }
442}