    001/*
    002 * Copyright (c) 2017 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.fukurou.fileexec;
    017
    018import java.util.Locale;
    019import java.util.Date;
    020import java.text.DateFormat;
    021import java.text.SimpleDateFormat;
    022
    023/**
    024 * StringUtilは、共通的に使用される文字列処理に関する、ユーティリティークラスです。
    025 *
    026 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
    027 *
    028 * @version  7.0
    029 * @author   Kazuhiko Hasegawa
    030 * @since    JDK1.8,
    031 */
    032public final class StringUtil {
    033
    034        /**
    035         * デフォルトコンストラクターをprivateにして、
    036         * オブジェクトの生成をさせないようにする。
    037         */
    038        private StringUtil() {}
    039
    040        /**
    041         * 指定の文字列が nullか、ゼロ文字列 の場合は、２番目以降の引数から、null でない最初の値を返します。
    042         * ２番目以降の引数には、ゼロ文字列の指定も可能です。
    043         *
    044         * @param       val 判定する文字列
    045         * @param       def 初期値の可変長引数
    046         * @return      指定の値で、最初にnullでない値。最後まで、無ければ、val を返します。
    047         */
    048        public static String nval( final String val , final String... def ) {
    049                if( val == null || val.trim().isEmpty() ) {
    050                        for( final String str : def ) {
    051                                if( str != null ) { return str.trim(); }                        // ゼロ文字列 の場合も、返します。
    052                        }
    053                }
    054
    055                return val;             // 最後まで null以外の値が見つからない場合なので、val が null の場合もありうる。
    056        }
    057
    058        /**
    059         * 指定の数値型文字列が nullか、ゼロ文字列 の場合は、２番目の初期値を返します。
    060         *
    061         * そうでない場合は、指定の文字列を、Integer.parseInt( String ) した値を返します。
    062         *
    063         * @param       val 判定する数値型文字列
    064         * @param       def 初期値の数値
    065         * @return      指定の文字列が nullか、ゼロ文字列でない場合は、数値に変換し、そうでなければ、初期値の数値を返します。
    066         * @throws      NumberFormatException   文字列が解析可能な整数型を含まない場合。
    067         */
    068        public static int nval( final String val , final int def ) {
    069                return val == null || val.trim().isEmpty() ? def : Integer.parseInt( val.trim() );
    070        }
    071
    072        /**
    073         * LOGファイルのフォーマットに対応した日付、時刻文字列を作成します。
    074         *
    075         * yyyyMMddHHmmss 形式のフォーマットで、返します。
    076         *
    077         * @return      現在の日付、時刻文字列
    078         */
    079        public static String getTimeFormat() {
    080                return getTimeFormat( "yyyyMMddHHmmss" );
    081        }
    082
    083        /**
    084         * 指定のフォーマットに対応した日付、時刻文字列を作成します。
    085         *
    086         * 例えば、LOGフォーマットの場合は、yyyyMMdd HH:mm:ss 形式です。
    087         *
    088         * @param format        日付、時刻文字列のフォーマット
    089         * @return      指定のフォーマットに対応した現在の日付、時刻文字列
    090         */
    091        public static String getTimeFormat( final String format ) {
    092                final DateFormat formatter = new SimpleDateFormat( format , Locale.getDefault() );
    093                return formatter.format( new Date() );
    094        }
    095
    096        /**
    097         * 指定のフォーマットに対応した日付、時刻文字列を作成します。
    098         *
    099         * 例えば、LOGフォーマットの場合は、yyyyMMdd HH:mm:ss 形式です。
    100         *
    101         * @param date          フォーマットする日付情報
    102         * @param format        日付、時刻文字列のフォーマット
    103         * @return      指定のフォーマットに対応した指定の日付、時刻文字列
    104         */
    105        public static String getTimeFormat( final long date , final String format ) {
    106                final DateFormat formatter = new SimpleDateFormat( format , Locale.getDefault() );
    107                return formatter.format( new Date( date ) );
    108        }
    109
    110        /**
    111         * EUROMAPの日付、時間文字列から、openGion系日付時刻文字列を作成します。
    112         *
    113         * 日付は、yyyyMMdd 形式で、時間は、HH:mm:ss 形式を標準としますが、
    114         * 数字以外の文字列を削除して、連結した文字列を作成します。
    115         * その場合、１４桁数字文字列 になります。
    116         *
    117         * その形式に合わない場合は、現在時刻 を返します。
    118         * ただし、日付の整合性チェックは、行っていません。
    119         *
    120         * @og.rev 7.0.5.1 (2019/09/27) 日付指定がない場合に、無条件に１秒待つ仕様を廃止。ロジックミス修正
    121         *
    122         * @param       ymd EUROMAPの日付(yyyyMMdd形式の８桁数字)
    123         * @param       hms EUROMAPの時間(HH:mm:ss形式の８桁数字)
    124         * @return      openGion系日付時刻文字列(yyyyMMddHHmmss形式の１４桁数字)
    125         */
    126        public static String getTime( final String ymd , final String hms ) {
    127                if( ymd != null && hms != null ) {
    128                        if( ymd.length() == 8 && hms.length() == 8 ) {
    129                                return ymd + hms.substring(0,2) + hms.substring(3,5) + hms.substring(6);
    130                        }
    131                        else {
    132                                final StringBuilder buf = new StringBuilder();
    133                                for( int i=0; i<ymd.length(); i++ ) {
    134                                        final char ch = ymd.charAt( i );
    135//                                      if( '0' <= ch && ch < '9' ) { buf.append( ch ); }       // 数字のみ許可
    136                                        if( '0' <= ch && ch <= '9' ) { buf.append( ch ); }      // 数字のみ許可               7.0.5.1 (2019/09/27)
    137                                }
    138
    139                                for( int i=0; i<hms.length(); i++ ) {
    140                                        final char ch = hms.charAt( i );
    141//                                      if( '0' <= ch && ch < '9' ) { buf.append( ch ); }       // 数字のみ許可
    142                                        if( '0' <= ch && ch <= '9' ) { buf.append( ch ); }      // 数字のみ許可               7.0.5.1 (2019/09/27)
    143                                }
    144
    145                                if( buf.length() == 14 ) {
    146                                        return buf.toString();
    147                                }
    148                        }
    149                }
    150
    151                // 引数が存在しない場合は、無条件に１秒待ちます。
    152                // これは、テスト用に日付無しのDATデータを用意して登録する場合に使用します。
    153//              try{ Thread.sleep( 1000 ); } catch( final InterruptedException ex ){}
    154
    155                return getTimeFormat() ;                // 現在時刻
    156        }
    157
    158        /**
    159         * すべての引数をｽﾍﾟｰｽで連結してtrim()した文字列を返します。
    160         *
    161         * 各引数は、それぞれ、trim()した後、ｽﾍﾟｰｽで連結します。
    162         * ただし、引数が、nullの場合は、ゼロ文字列に変換します。
    163         * すべてを連結して、trim() した結果が、ゼロ文字列の場合は、defVal を返します。
    164         *
    165         * @param       defVal 結果が、ゼロ文字列の場合に返す文字列
    166         * @param       keys 連結したいキーの可変長文字列配列
    167         * @return      合成した文字列。
    168         */
    169        public static String keyAppend( final String defVal , final String... keys ) {
    170                final StringBuilder buf = new StringBuilder();
    171                for( final String key : keys ) {
    172                        buf.append( nval( key , "" ).trim() ).append( ' ' );
    173                }
    174
    175                return nval( buf.toString().trim() , defVal );
    176        }
    177
    178        /**
    179         * 指定の文字列を、引数の文字で、前後に分割します。
    180         * キー=値 や、キー,値 などの文字列を分割して、キーと値の配列を返します。
    181         * １番目の配列[0] は、キー(chの左側)、２番目の配列[1] は、値(chの右側) です。
    182         * キーも値も、前後のスペースを削除:trim() します。
    183         * キー(１番目の配列[0])は、空文字列を許可しません。
    184         *
    185         * 分割文字を含まない場合や、キーが、空文字列の場合は、null を返します。
    186         * nullで無い場合は、配列は、必ず２個存在しそのどちらの値も、null を含みません。
    187         *
    188         * @param       line    １行分の文字列
    189         * @param       ch      分割する文字
    190         * @return      キーと値の配列。無ければ、null
    191         */
    192        public static String[] split( final String line , final char ch ) {
    193                final int ad = line.indexOf( ch );
    194                if( ad > 0 ) {          // ch が先頭は無視
    195                        final String key = line.substring( 0,ad ).trim();
    196
    197                        if( !key.isEmpty() ) {
    198                                final String val = line.substring( ad+1 ).trim();
    199                                return new String[] { key , val };
    200                        }
    201                }
    202                return null;
    203        }
    204
    205        /**
    206         * 数字型文字列の一致をチェックします。
    207         *
    208         * 処理としては、数字型文字列以外を判定条件に入れても、同一文字列の場合は、
    209         * true になります。
    210         * ここでは、どちらかの文字列の末尾が、".0" などの場合に、同じかどうかを
    211         * 数値に変換して、判定します。
    212         * どちらかが、null の場合は、必ず false になります。両方とも、null でも false です。
    213         *
    214         * @param       val1 判定用の引数１
    215         * @param       val2 判定用の引数２
    216         * @return      判定結果(一致する場合は、true)
    217         */
    218        public static boolean numEquals( final String val1 , final String val2 ) {
    219                if( val1 == null || val2 == null ) { return false; }
    220
    221                if( val1.equals( val2 ) ) {
    222                        return true;
    223                }
    224                else {
    225                        try {
    226//                              return Double.parseDouble( val1 ) == Double.parseDouble( val2 );
    227
    228                                // 6.9.8.0 (2018/05/28) FindBugs:浮動小数点の等価性のためのテスト
    229                                // FindBugs では、等価性のための比較は、if (Math.abs(x - y) < .0000001) が推奨されています。
    230                                // return Math.abs( Double.parseDouble( val1 ) - Double.parseDouble( val2 ) ) < .0000001 ;
    231                                return Double.compare( Double.parseDouble( val1 ) , Double.parseDouble( val2 ) ) == 0 ;
    232                        }
    233                        catch( final NumberFormatException ex ) {
    234                                // 数値変換できなかった場合は、無視します。
    235                        }
    236                }
    237
    238                return false;
    239        }
    240}