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