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         *
110         * @param       value 元の文字列
111         * @param       sizeX    整数部分の文字列の長さ
112         * @param       sizeY    小数部分の文字列の長さ
113         *
114         * @return      エラー文字列長さ(正常時は、null)
115         */
116        public static String sizeYCheck( final String value ,final int sizeX ,final int sizeY ) {
117                if( sizeY == 0 ) {
118                        return null;
119                }
120                int valuesizeY;
121                final int pos = value.indexOf( '.' );
122                if( pos >= 0 ) {
123                        valuesizeY = value.length() - pos - 1;
124                }
125                else {
126                        valuesizeY = 0;
127                }
128
129                if( valuesizeY > sizeY ) {
130                        // 小数部の長さが指定の長さよりも長いです。
131                        return String.valueOf(valuesizeY);
132                } else {
133                        return null;
134                }
135        }
136
137        /**
138         * 文字列の小数点の位置をチェックします。
139         * 小数点(.)が、2箇所以上存在する(存在する位置が異なる)場合エラー
140         *
141         * @param       value 元の文字列
142         *
143         * @return      エラー文字列(正常時は、null)
144         */
145        public static String decimalPointCheck( final String value ) {
146                String rtn = null;
147                if( value.indexOf( '.' ) != value.lastIndexOf( '.' ) ) {
148                        rtn = changeErrorPath( value, '.' );
149                }
150                return rtn ;
151        }
152
153        /**
154         * 文字列の符号の位置をチェックします。
155         * マイナス(-)が、存在しないか、先頭以外の場合は、エラー
156         *
157         * @param       value 元の文字列
158         *
159         * @return      エラー文字列(正常時は、null)
160         */
161        public static String decimalCodeCheck( final String value ) {
162                String rtn = null;
163                if( value.lastIndexOf( '-' ) > 0 ) {
164                        rtn = changeErrorPath( value, '-' );
165                }
166                return rtn ;
167        }
168
169        /**
170         * 文字列の整合性(整数)をチェックします。
171         * 0~9およびマイナス(-)を許可します。
172         *
173         * @param       value 元の文字列
174         *
175         * @return      エラー文字列(正常時は、null)
176         */
177        public static String numberFormatCheck( final String value ) {
178                boolean isError = false;
179                int i = 0;
180                char ch;
181                while( i<value.length() ) {
182                        ch = value.charAt( i );
183                        if( ( '0'>ch || '9'<ch ) && '-'!=ch ) {
184                                isError = true;
185                                break;
186                        }
187                        i++;
188                }
189                if( isError ) {
190                        final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
191                        for( i=0; i<value.length(); i++ ) {
192                                ch = value.charAt( i );
193                                if( ( '0'>ch || '9'<ch ) &&  '-' != ch ) {
194                                        val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
195                                }
196                                else {
197                                        val.append( ch );
198                                }
199                        }
200                        return val.toString();
201                } else {
202                        return null;
203                }
204        }
205
206        /**
207         * 文字列の整合性(小数)をチェックします。
208         * 0~9、マイナス(-)および小数点(.)を許可します。
209         *
210         * og.rev 4.2.4.0 (2008/06/26) '.' or '-' のみはエラー
211         *
212         * @param       value 元の文字列
213         *
214         * @return      エラー文字列(正常時は、null)
215         */
216        public static String decimalFormatCheck( final String value ) {
217                boolean isError = false;
218                int i = 0;
219                char ch;
220                while( i<value.length() ) {
221                        ch = value.charAt( i );
222                        if( ( '0'>ch || '9'<ch ) && '.'!=ch && '-'!=ch ) {
223                                isError = true;
224                                break;
225                        }
226                        i++;
227                }
228
229                // 4.2.4.0 (2008/06/26) '.' or '-' のみはエラー
230                if( value.length() ==1 && ( value.charAt(0) == '.' || value.charAt(0) == '-' ) ) {
231                        isError = true;
232                }
233
234                if( isError ) {
235                        final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
236                        for( i=0; i<value.length(); i++ ) {
237                                ch = value.charAt( i );
238                                if( ( '0'>ch || '9'<ch ) && '.'!=ch && '-'!=ch ) {
239                                        val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
240                                }
241                                else {
242                                        val.append( ch );
243                                }
244                        }
245                        return val.toString();
246                } else {
247                        return null;
248                }
249        }
250
251        /**
252         * 日付文字列の整合性をチェックします。
253         *
254         * 整合性といっても、DBType_DATE のような厳密なチェックは、行いません。
255         * ここでは、yyyyMM(6桁)、yyyyMMdd(8桁)、yyyyMMddHHmmss(14桁) の3種類のみ
256         * 対象にします。
257         * "0000XXXX" , "9999XXXX" は、常に許可されます。
258         * 月と日の関係も、ありません。(20130231 は OK)
259         * あくまで、月は、1~12 の範囲、日は、1~31の範囲チェックです。
260         *
261         * 厳密な日付チェックを行いたい場合は、DBType_DATE を使用してください。
262         * 
263         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
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                final int len = value.length() ;
273                if( len >= 6 ) {        // 月のチェック
274                        final String val = ymdhmsCheck( value,4,6,1,12 );
275                        if( val != null ) { return val; }
276                }
277
278                if( len >= 8 ) {        // 日のチェック
279                        final String val = ymdhmsCheck( value,6,8,1,31 );
280                        if( val != null ) { return val; }
281                }
282
283                if( len >= 10 ) {       // 時のチェック
284                        final String val = ymdhmsCheck( value,8,10,0,24 );              // 240000 は許可します。
285                        if( val != null ) { return val; }
286                }
287
288                if( len >= 12 ) {       // 分のチェック
289                        final String val = ymdhmsCheck( value,10,12,0,60 );             // 60分は許可します。
290                        if( val != null ) { return val; }
291                }
292
293                if( len == 14 ) {       // 秒のチェック
294                        final String val = ymdhmsCheck( value,12,14,0,60 );             // うるう秒とは言いませんが、60秒は許可します。
295                        if( val != null ) { return val; }
296                }
297
298                return null;
299        }
300
301        /**
302         * 時刻文字列の整合性をチェックします。
303         *
304         * 整合性といっても、DBType_DATE のような厳密なチェックは、行いません。
305         * ここでは、HHmmss(6桁) のみ対象にします。
306         * 
307         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
308         *
309         * @param       value 元の文字列(nullは不可)
310         *
311         * @return      エラー文字列(正常時は、null)
312         */
313        public static String hmsFormatCheck( final String value ) {
314                final int len = value.length() ;
315
316                if( len >= 2 ) {        // 時のチェック
317                        final String val = ymdhmsCheck( value,0,2,0,24 );               // 240000 は許可します。
318                        if( val != null ) { return val; }
319                }
320
321                if( len >= 4 ) {        // 分のチェック
322                        final String val = ymdhmsCheck( value,2,4,0,60 );               // 60分は許可します。
323                        if( val != null ) { return val; }
324                }
325
326                if( len == 6 ) {        // 秒のチェック
327                        final String val = ymdhmsCheck( value,4,6,0,60 );               // うるう秒とは言いませんが、60秒は許可します。
328                        if( val != null ) { return val; }
329                }
330
331                return null;
332        }
333
334        /**
335         * 月、日、時、分、秒 のチェック用メソッド
336         *
337         * 同じようなパターンでチェックする為、共通メソッド化しておきます。
338         *
339         * @param       value   元の文字列
340         * @param       st              チェック開始桁数
341         * @param       ed              チェック終了桁数 (val.length() を超えない事)
342         * @param       minSu   許可範囲の最小値
343         * @param       maxSu   許可範囲の最大値
344         *
345         * @return      エラー文字列(正常な場合は、null)
346         */
347        public static String ymdhmsCheck( final String value, final int st , final int ed , final int minSu , final int maxSu ) {
348                String rtn = null;
349
350                final int dt = Integer.parseInt( value.substring( st,ed ) );    
351                if( dt < minSu || maxSu < dt ) {
352                        rtn = value.substring( 0,st ) + "<span class=\"NG\">" + value.substring( st,ed ) + "</span>" + value.substring( ed ) ;
353                }
354                return rtn;
355        }
356
357        /**
358         * 文字列のエラー文字列を返します。
359         *
360         * @param       val     元の文字列
361         * @param       inChar  エラー対象文字
362         *
363         * @return      エラー文字列
364         * @og.rtnNotNull
365         */
366        private static String changeErrorPath( final String val, final char inChar ) {
367                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
368                for( int i=0; i<val.length(); i++ ) {
369                        final char ch = val.charAt( i );
370                        if( inChar==ch ) {
371                                buf.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
372                        } else {
373                                buf.append( ch );
374                        }
375                }
376                return buf.toString();
377        }
378
379        /**
380         * 文字列の長さをチェックします。
381         * バイト数に換算して比較チェックします。
382         *
383         * @og.rev 3.0.1.3 (2003/03/11) メソッド新規追加
384         * @og.rev 3.5.5.3 (2004/04/09) StringUtil の CODE を使用したメソッドを削除する。
385         * @og.rev 5.3.9.0 (2011/09/01) DB_USE_TEXT_LENGTH を考慮した、「文字数」、「バイト数」チェック
386         *
387         * @param       value 元の文字列
388         * @param       len      文字列の長さ
389         *
390         * @return      エラー文字列(正常時は、null)
391         */
392        public static String byteLengthCheck( final String value ,final int len ) {
393                String rtn = null;
394
395                // 5.3.9.0 (2011/09/01) 「文字数」、「バイト数」チェック
396                final int valLen ;
397                if( USE_TEXT_LEN ) {    // true:「文字数」チェック方式
398                        valLen = value.length();
399                }
400                else {                                  // false:「バイト数」チェック方式
401                        final byte[] byteValue = StringUtil.makeByte( value,CODE );     // 3.5.5.3 (2004/04/09)
402                        valLen = byteValue.length;
403                }
404
405                if( valLen > len ) {
406                        rtn = String.valueOf( valLen );
407                }
408
409                return rtn ;
410        }
411
412        /**
413         * 文字列の整合性を、dbType パラメータを利用してチェックします。
414         * regex が、null または、長さゼロの文字列の場合は、なにもしません。
415         *
416         * @og.rev 3.6.0.0 (2004/09/22) 新規作成
417         *
418         * @param       value 元の文字列
419         * @param       regex チェックする正規表現文字列
420         *
421         * @return      エラー文字列(正常時は、null)
422         */
423        public static String matcheCheck( final String value,final String regex ) {
424                // 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
425                // 条件変更注意
426                return regex == null || regex.isEmpty() || value.matches( regex )
427                                        ? null
428                                        : "<span class=\"NG\">" + value + "</span> regex=" + regex ;
429
430        }
431}