    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         *
    264         * @param       value 元の文字列(nullは不可)
    265         *
    266         * @return      エラー文字列(正常時は、null)
    267         */
    268        public static String ymdFormatCheck( final String value ) {
    269                if( value.startsWith( "0000" ) || value.startsWith( "9999" ) ) { return null; }         // 無条件 OK
    270
    271                final int len = value.length() ;
    272                if( len >= 6 ) {        // 月のチェック
    273                        final String val = ymdhmsCheck( value,4,6,1,12 );
    274                        if( val != null ) { return val; }
    275                }
    276
    277                if( len >= 8 ) {        // 日のチェック
    278                        final String val = ymdhmsCheck( value,6,8,1,31 );
    279                        if( val != null ) { return val; }
    280                }
    281
    282                if( len >= 10 ) {       // 時のチェック
    283                        final String val = ymdhmsCheck( value,8,10,0,24 );              // 240000 は許可します。
    284                        if( val != null ) { return val; }
    285                }
    286
    287                if( len >= 12 ) {       // 分のチェック
    288                        final String val = ymdhmsCheck( value,10,12,0,60 );             // 60分は許可します。
    289                        if( val != null ) { return val; }
    290                }
    291
    292                if( len == 14 ) {       // 秒のチェック
    293                        final String val = ymdhmsCheck( value,12,14,0,60 );             // うるう秒とは言いませんが、60秒は許可します。
    294                        if( val != null ) { return val; }
    295                }
    296
    297                return null;
    298        }
    299
    300        /**
    301         * 時刻文字列の整合性をチェックします。
    302         *
    303         * 整合性といっても、DBType_DATE のような厳密なチェックは、行いません。
    304         * ここでは、HHmmss(６桁) のみ対象にします。
    305         *
    306         * @og.rev 5.6.0.3 (2012/01/24) 新規追加
    307         *
    308         * @param       value 元の文字列(nullは不可)
    309         *
    310         * @return      エラー文字列(正常時は、null)
    311         */
    312        public static String hmsFormatCheck( final String value ) {
    313                final int len = value.length() ;
    314
    315                if( len >= 2 ) {        // 時のチェック
    316                        final String val = ymdhmsCheck( value,0,2,0,24 );               // 240000 は許可します。
    317                        if( val != null ) { return val; }
    318                }
    319
    320                if( len >= 4 ) {        // 分のチェック
    321                        final String val = ymdhmsCheck( value,2,4,0,60 );               // 60分は許可します。
    322                        if( val != null ) { return val; }
    323                }
    324
    325                if( len == 6 ) {        // 秒のチェック
    326                        final String val = ymdhmsCheck( value,4,6,0,60 );               // うるう秒とは言いませんが、60秒は許可します。
    327                        if( val != null ) { return val; }
    328                }
    329
    330                return null;
    331        }
    332
    333        /**
    334         * 月、日、時、分、秒 のチェック用メソッド
    335         *
    336         * 同じようなパターンでチェックする為、共通メソッド化しておきます。
    337         *
    338         * @param       value   元の文字列
    339         * @param       st              チェック開始桁数
    340         * @param       ed              チェック終了桁数 (val.length() を超えない事)
    341         * @param       minSu   許可範囲の最小値
    342         * @param       maxSu   許可範囲の最大値
    343         *
    344         * @return      エラー文字列（正常な場合は、null）
    345         */
    346        public static String ymdhmsCheck( final String value, final int st , final int ed , final int minSu , final int maxSu ) {
    347                String rtn = null;
    348
    349                final int dt = Integer.parseInt( value.substring( st,ed ) );
    350                if( dt < minSu || maxSu < dt ) {
    351                        rtn = value.substring( 0,st ) + "<span class=\"NG\">" + value.substring( st,ed ) + "</span>" + value.substring( ed ) ;
    352                }
    353                return rtn;
    354        }
    355
    356        /**
    357         * 文字列のエラー文字列を返します。
    358         *
    359         * @param       val     元の文字列
    360         * @param       inChar  エラー対象文字
    361         *
    362         * @return      エラー文字列
    363         * @og.rtnNotNull
    364         */
    365        private static String changeErrorPath( final String val, final char inChar ) {
    366                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    367                for( int i=0; i<val.length(); i++ ) {
    368                        final char ch = val.charAt( i );
    369                        if( inChar==ch ) {
    370                                buf.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
    371                        } else {
    372                                buf.append( ch );
    373                        }
    374                }
    375                return buf.toString();
    376        }
    377
    378        /**
    379         * 文字列の長さをチェックします。
    380         * バイト数に換算して比較チェックします。
    381         *
    382         * @og.rev 3.0.1.3 (2003/03/11) メソッド新規追加
    383         * @og.rev 3.5.5.3 (2004/04/09) StringUtil の CODE を使用したメソッドを削除する。
    384         * @og.rev 5.3.9.0 (2011/09/01) DB_USE_TEXT_LENGTH を考慮した、｢文字数｣、｢バイト数｣チェック
    385         *
    386         * @param       value 元の文字列
    387         * @param       len      文字列の長さ
    388         *
    389         * @return      エラー文字列(正常時は、null)
    390         */
    391        public static String byteLengthCheck( final String value ,final int len ) {
    392                String rtn = null;
    393
    394                // 5.3.9.0 (2011/09/01) ｢文字数｣、｢バイト数｣チェック
    395                final int valLen ;
    396                if( USE_TEXT_LEN ) {    // true:｢文字数｣チェック方式
    397                        valLen = value.length();
    398                }
    399                else {                                  // false:｢バイト数｣チェック方式
    400                        final byte[] byteValue = StringUtil.makeByte( value,CODE );     // 3.5.5.3 (2004/04/09)
    401                        valLen = byteValue.length;
    402                }
    403
    404                if( valLen > len ) {
    405                        rtn = String.valueOf( valLen );
    406                }
    407
    408                return rtn ;
    409        }
    410
    411        /**
    412         * 文字列の整合性を、dbType パラメータを利用してチェックします。
    413         * regex が、null または、長さゼロの文字列の場合は、なにもしません。
    414         *
    415         * @og.rev 3.6.0.0 (2004/09/22) 新規作成
    416         *
    417         * @param       value 元の文字列
    418         * @param       regex チェックする正規表現文字列
    419         *
    420         * @return      エラー文字列(正常時は、null)
    421         */
    422        public static String matcheCheck( final String value,final String regex ) {
    423                // 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
    424                // 条件変更注意
    425                return regex == null || regex.isEmpty() || value.matches( regex )
    426                                        ? null
    427                                        : "<span class=\"NG\">" + value + "</span> regex=" + regex ;
    428        }
    429}