    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         *
    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         * 小数点(.)が、２箇所以上存在する(存在する位置が異なる)場合エラー
    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         * ０～９およびマイナス(-)を許可します。
    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         * ０～９、マイナス(-)および小数点(.)を許可します。
    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(６桁)、yyyyMMdd(８桁)、yyyyMMddHHmmss(１４桁) の３種類のみ
    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(６桁) のみ対象にします。
    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}