    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 * ＤＢカラムの属性チェックに使用されるメソッドを集約した、クラスです。
    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         * 小数点(.)が、２箇所以上存在する(存在する位置が異なる)場合エラー
    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         * ０～９およびマイナス(-)を許可します。
    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         * ０～９、マイナス(-)および小数点(.)を許可します。
    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(６桁)、yyyyMMdd(８桁)、yyyyMMddHHmmss(１４桁) の３種類のみ
    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(６桁) のみ対象にします。
    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}