    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.fukurou.util;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    020import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    021
    022import java.util.Map;
    023import java.util.LinkedHashMap;
    024import java.util.Collections;                                                                           // 6.4.3.1 (2016/02/12) refactoring
    025import java.util.List;
    026import java.util.ArrayList;
    027import java.util.Iterator;
    028import java.util.Arrays;
    029import java.util.Date;
    030import java.util.Locale;
    031
    032import java.text.DateFormat;
    033import java.text.SimpleDateFormat;
    034
    035/**
    036 * Argument は、バッチ処理の main メソッドの引数を解析するクラスです。
    037 * Argument は、３つのタイプに分かれます。
    038 *
    039 * ［コメント］  ： # で始まる引数で、使用されません。(登録もされません。)
    040 * ［引数］      ： #,-,= 以外で始まる通常の文字列。登録の順番が指定されます。
    041 * ［プロパティ］： - で始まり、キーと値を=で区切っているパラメータです。順序は無関係。
    042 *
    043 * これらのタイプを混在させても構いません。[引数]は、[コメント] や[プロパティ]を
    044 * 無視した、入力の順番が重要視されます。取り出す場合も、番号で取り出します。
    045 * 最初の［引数］が、0 で、以降 引数個数-1 までの番号で取り出します。
    046 * ［プロパティ］は、順番は無視し、キー部を指定することで取り出せます。
    047 * ただし、キー部を重複して登録することは出来ません。なお、キー部の頭の文字列のみで
    048 * 取り出すメソッドがあるため、key1,key2,key3 などと指定して、key で取り出せば、
    049 * 複数プロパティを同一キーで取り出すことが可能です。
    050 * ［プロパティ］の指定では、キーと値を=で区切りますが、その前後にスペースを
    051 * 入れないで下さい。引数の前後に = が付く文字列は指定できません。
    052 *
    053 * java Program AAA BBB #CCC -DD=XX -EE=YY -FF=ZZ GGG
    054 *              ~~~ ~~~ ~~~~ ~~~~~~ ~~~~~~ ~~~~~~ ~~~
    055 * ［コメント］  ： #CCC
    056 * ［引数］      ： [0]=AAA , [1]=BBB , [2]=GGG
    057 * ［プロパティ］： key=DD,val=XX  key=EE,val=YY  key=FF,val=ZZ
    058 *
    059 * Argument の整合性チェックは、３つのパターンがあります。
    060 *
    061 * ［引数］個数指定 ：引数自身の最小個数、最大個数を登録しておくことで、プロパティのハイフン忘れ等を防止します。
    062 * ［プロパティ］必須チェック ：必須キーが登録されたかどうかのチェックを行います。
    063 * ［プロパティ］整合性チェック ： 指定されているキーのみ登録可能です。
    064 *
    065 * これらのチェックで、整合性チェックのみ、Argument の登録時に行います。
    066 * それ以外は、取り出し時まで、判断できません。
    067 * (取り出しは、登録がすべて終了したのちに行われると仮定しています)
    068 *
    069 * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    070 * ［プロパティ］設定可能なプロパティの値を指定することで、誤記入を防止します。
    071 *
    072 * @version  4.0
    073 * @author   Kazuhiko Hasegawa
    074 * @since    JDK5.0,
    075 */
    076public final class Argument  {
    077        /** Argument引数のタイプ ［コメント］は、無視されます。 {@value} */
    078        public static final int CMNT = 0;       // ［コメント］
    079
    080        /** Argument引数のタイプ ［引数］は、入力順にアクセスできます。 {@value} */
    081        public static final int ARGS = 1;       // ［引数］
    082
    083        /** Argument引数のタイプ ［プロパティ］は、-KEY=VALUE 形式でキーでアクセスできます。 {@value} */
    084        public static final int PROP = 2;       // ［プロパティ］
    085
    086        private boolean argOkFlag       ;
    087        private final List<String> argments = new ArrayList<>();
    088        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。 */
    089        private final Map<String,String> propMap = Collections.synchronizedMap( new LinkedHashMap<>() );
    090
    091        private int argRangeMin ;                       // 初期値:0
    092        private int argRangeMax = 200 ;         // 本当は、Windows の引数の上限値を設定
    093
    094        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。 */
    095        private final Map<String,String> mustProparty   = Collections.synchronizedMap( new LinkedHashMap<>() );
    096        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。 */
    097        private final Map<String,String> usableProparty = Collections.synchronizedMap( new LinkedHashMap<>() );
    098
    099        private final String programID  ;
    100
    101        /**
    102         * この Argument を使用している プログラムID(Javaクラス名)を指定して
    103         * インスタンスを作成します。
    104         * toString() する際に、表示します。
    105         *
    106         * @param   pgid プログラムID
    107         */
    108        public Argument( final String pgid ) {
    109                programID = pgid;
    110        }
    111
    112        /**
    113         * Argument の配列文字列から、引数やプロパティをセットします。
    114         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    115         * これは、main メソッド等で単独起動する場合に、引数そのままを
    116         * セットする場合に使用します。
    117         *
    118         * @param   args 引数配列(可変長引数)
    119         * @see  #putArgument( String )
    120         */
    121        public void setArgument( final String... args ) {
    122                for( int i=0; i<args.length; i++ ) {
    123                        putArgument( args[i] );
    124                }
    125        }
    126
    127        /**
    128         * Argument の文字列から、引数かプロパティをセットします。
    129         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    130         * Argument を設定する時に、タイプ判断として、getArgumentType( String ) を
    131         * 使用します。よって、不正な Argument を設定した場合は、強制終了されます。
    132         *
    133         * @og.rev 6.4.8.3 (2016/07/15) key,val 分解後は、#putArgument(String,String)
    134         *
    135         * @param   arg 引数
    136         * @see  #putArgument( String,String )
    137         */
    138        public void putArgument( final String arg ) {
    139                final int type = getArgumentType( arg );
    140
    141                switch( type ) {
    142                        case CMNT : break;
    143                        case ARGS : argments.add( arg ); break;
    144                        case PROP :
    145                                final int sep = arg.indexOf( '=' );     // sep は、 0 以上保証済み
    146                                final String key = arg.substring(1,sep);
    147                                final String val = arg.substring(sep+1);
    148                                putArgument( key,val );         // 6.4.8.3 (2016/07/15)
    149                                break;
    150                        default: break;
    151                }
    152        }
    153
    154        /**
    155         * Argument の文字列から、プロパティをセットします。
    156         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    157         * このメソッドは、引数 や コメントの判断を行いません。プロパティ のみ
    158         * 設定されるものとして、処理します。
    159         * プロパティの key=val が初めから分割されている場合の簡易メソッドです。
    160         *
    161         * @og.rev 6.4.8.3 (2016/07/15) val で、｢\t｣と、｢\n｣の文字列を、タブと改行に変換します。
    162         * @og.rev 6.4.8.4 (2016/07/22) 元に戻します。タブと改行は、ここで変換できません。
    163         *
    164         * @param   key プロパティのキー
    165         * @param   val プロパティの値
    166         * @see  #putArgument( String )
    167         */
    168        public void putArgument( final String key,final String val ) {
    169                checkProparty( key );           // 3.8.0.1 (2005/06/17)
    170                propMap.put( key,val );
    171        }
    172
    173        /**
    174         * ［引数］個数指定を設定します。
    175         * 最大値、最小値を登録しておくことで、個数が、規定から外れていないか
    176         * どうかを確認します。
    177         * エラー判定は、実際に、［引数］を取り出すときに行われます。
    178         * このチェックの登録は、putArgument( String ) の前でも後でもよく、
    179         * getArgument の実行前であれば、いつでも構いません。
    180         * 設定しない場合の初期値は、0～200 です。
    181         *
    182         * @param   min ［引数］の最小個数(初期値:0)
    183         * @param   max ［引数］の最大個数(初期値:200)
    184         */
    185        public void setArgRange( final int min, final int max ) {
    186                argRangeMin = min ;
    187                argRangeMax = max ;
    188        }
    189
    190        /**
    191         * ［プロパティ］必須チェック  Map 登録
    192         * 必須キーが登録されたかどうかのチェックを行います。
    193         * マスト判定は、実際に、［プロパティ］を取り出すときに行われます。
    194         * すべてのプロパティーがセットし終わったかどうかの判断が出来ないためです。
    195         * それ以外のチェックは、putArgument( String ) 時に行われるので、それまでに
    196         * mustProparty のMapを登録しておく必要があります。
    197         * ただし、引数文字列の記述チェック(使用してもよい値の配列チェック)は、
    198         * #getProparty( String , String , String[] ) で行われるので、取得時になります。
    199         *
    200         * 設定しない場合の初期値は、制限なしです。
    201         * 指定のMapのValue値には、エラー時のコメントを記述しておきます。
    202         *
    203         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。
    204         *
    205         * @param   mustProp 必須キーのMap
    206         * @see #getProparty( String , String , String[] )
    207         */
    208        public void setMustProparty( final Map<String,String> mustProp ) {
    209                mustProparty.putAll( mustProp ) ;
    210        }
    211
    212        /**
    213         * ［プロパティ］整合性チェック Map 登録
    214         * 指定されているキーのみ登録可能です。
    215         * エラー判定は、実際に、［プロパティ］を取り出すときに行われます。
    216         * このチェックの登録は、putArgument( String ) 時に行われるので、それまでに
    217         * usableProparty のMapを登録しておく必要があります。
    218         * ただし、引数文字列の記述チェック(使用してもよい値の配列チェック)は、
    219         * #getProparty( String , String , String[] ) で行われるので、取得時になります。
    220         *
    221         * 設定しない場合の初期値は、制限なしです。
    222         * 指定のMapのValue値には、このキーに対する解説を登録しておきます。
    223         *
    224         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。
    225         *
    226         * @param   useProp 使用可能キーのMap
    227         */
    228        public void setUsableProparty( final Map<String,String> useProp ) {
    229                usableProparty.putAll( useProp ) ;
    230        }
    231
    232        /**
    233         * Argument の文字列から、そのタイプを判断します。
    234         * 引数の形式が不正な場合(例えば、キーと値の分離の = の前後にスペースが入った場合)
    235         * RuntimeException で強制終了します。
    236         *
    237         * ［コメント］  ： # で始まる引数で、使用されません。(登録もされません。)
    238         * ［引数］      ： #,-,= 以外で始まる通常の文字列。登録の順番が指定されます。
    239         * ［プロパティ］： - で始まり、キーと値を=で区切っているパラメータです。順序は無関係。
    240         *
    241         * ※ 引数の設定方法が間違っている場合、RuntimeException が throw されます。
    242         *
    243         * @og.rev 5.3.4.0 (2011/04/01) 空文字列など無関係なパラメータは処理しないように変更
    244         * @og.rev 6.4.8.3 (2016/07/15) KEY=VALUE の VALUE が、ゼロ文字列でも許可します。
    245         *
    246         * @param   arg 引数
    247         *
    248         * @return  引数タイプ(CMNT,ARGS,PROP)
    249         * @see Argument#CMNT ［コメント］
    250         * @see Argument#ARGS ［引数］
    251         * @see Argument#PROP ［プロパティ］
    252         */
    253        public int getArgumentType( final String arg ) {
    254                if( arg == null || arg.trim().isEmpty() || '#' == arg.charAt(0) ) {
    255                        return CMNT;
    256                }
    257                else if( '=' == arg.charAt(0)  ) {      // 不正引数
    258                        final String errMsg = "引数の = の前後には、スペースを入れないで下さい。"
    259                                        +       " BAD Argument=[" + arg + "]"  ;
    260                        throw new OgRuntimeException( errMsg );
    261                }
    262                else if( '-' == arg.charAt(0) ) {
    263                        final int sep = arg.indexOf( '=' );
    264        //              if( sep > 0 && sep < arg.length()-1 ) {
    265                        if( sep > 0 && sep < arg.length() ) {                                   // 6.4.8.3 (2016/07/15)
    266                                return PROP;
    267                        }
    268                        else {
    269                                final String errMsg = "-KEY を指定する場合は、= を続けて、VALUEを指定して下さい。"
    270                                                +       "  -KEY=VALUE 形式 BAD Argument=[" + arg + "]"  ;
    271                                throw new OgRuntimeException( errMsg );
    272                        }
    273                }
    274                else {
    275                        return ARGS ;
    276                }
    277        }
    278
    279        /**
    280         * 指定の番号に対する［引数］を返します。
    281         * ［引数］は、#,-,= 以外で始まる通常の文字列として登録されています。
    282         * 登録された順番で取得します。
    283         *
    284         * ※ 引数の設定方法が間違っている場合、RuntimeException が throw されます。
    285         *
    286         * @param   adrs 番号
    287         *
    288         * @return  ［引数］
    289         */
    290        public String getArgument( final int adrs ) {
    291                // 以下のチェックは、getArgument が呼ばれて一度のみの実行でよい。
    292                if( ! argOkFlag ) {
    293                        if( argRangeMin < argments.size() || argments.size() < argRangeMax ) {
    294                                final String errMsg = "［引数］個数が最小/最大個数を満たしていません。"
    295                                                +       "  Min:" + argRangeMin + " <= " + argments.size() + " < Max:" + argRangeMax  ;
    296                                throw new OgRuntimeException( errMsg );
    297                        }
    298                        argOkFlag = true;
    299                }
    300
    301                if( argments.size() <= adrs ) {
    302                        final String errMsg = "指定のアドレスは、［引数］設定個数外です。"
    303                                        +       "  Size:" + argments.size() + " <= " + adrs ;
    304                        throw new OgRuntimeException( errMsg );
    305                }
    306
    307                return argments.get( adrs );
    308        }
    309
    310        /**
    311         * 指定の番号に対する［引数］を返します。
    312         * def には、文字列の初期値を指定しておきます。adrs に対応する値が、null の場合、
    313         * この def をそのまま返します。
    314         *
    315         * 処理は、getArgument( int ) の結果を、使用しています。
    316         *
    317         * @param   adrs 番号
    318         * @param   def 値が null の場合の初期値
    319         *
    320         * @return  ［引数］
    321         * @see #getArgument( int )
    322         */
    323        public String getArgument( final int adrs, final String def ) {
    324                final String value = getArgument( adrs );
    325                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    326                return value == null ? def : value ;
    327        }
    328
    329        /**
    330         * 指定の番号に対する［引数］を返します。
    331         * def には、数字の初期値を指定しておきます。adrs に対応する値が、null の場合、
    332         * この def をそのまま返します。
    333         *
    334         * 処理は、getArgument( int ) の結果を、使用しています。
    335         *
    336         * @param   adrs 番号
    337         * @param   def 値が null の場合の初期値
    338         *
    339         * @return  ［引数］
    340         * @see #getArgument( int )
    341         */
    342        public int getArgument( final int adrs, final int def ) {
    343                final String value = getArgument( adrs );
    344                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    345                return value == null ? def : Integer.parseInt( value ) ;
    346        }
    347
    348        /**
    349         * 指定の番号に対する［引数］を返します。
    350         * def には、boolean の初期値を指定しておきます。adrs に対応する値が、null の場合、
    351         * この def をそのまま返します。
    352         *
    353         * 処理は、getArgument( int ) の結果を、使用しています。
    354         *
    355         * @param   adrs 番号
    356         * @param   def 値が null の場合の初期値
    357         *
    358         * @return  ［引数］
    359         * @see #getArgument( int )
    360         */
    361        public boolean getArgument( final int adrs, final boolean def ) {
    362                final String value = getArgument( adrs );
    363                return ( value == null ) ? def : Boolean.parseBoolean( value ) ;                // 6.1.0.0 (2014/12/26) refactoring
    364        }
    365
    366        /**
    367         * ［プロパティ］整合性チェック 実行
    368         * 設定された整合性チェックを実行します。
    369         * 複数キーに対応する為に、先頭からの判定も行います。
    370         * チェックするキーの大文字･小文字は、厳格に判定しています。
    371         *
    372         * ※ 引数の設定方法が間違っている場合、RuntimeException が throw されます。
    373         *
    374         * @og.rev 5.1.5.0 (2010/04/01) 判定の条件が、重複していたので修正。
    375         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。
    376         * @og.rev 6.4.3.3 (2016/03/04) Iterator 処理を、拡張for文に変更。
    377         *
    378         * @param   key チェックする入力キー
    379         */
    380        private void checkProparty( final String key ) {
    381
    382                // 第１の判定。 propMap にすでに存在していれば、エラーになる。
    383                if( propMap.get( key ) != null ) {
    384                        final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE )
    385                                .append( "キー[" ).append( key ).append( "]は、すでに指定済みです。" ).append( CR )
    386                                .append( "  登録済み：-" )
    387                                .append( key ).append( '=' ).append( propMap.get( key ) )               // 6.0.2.5 (2014/10/31) char を append する。
    388                                .append( CR );
    389                        throw new OgRuntimeException( errMsg.toString() );
    390                }
    391
    392                // 6.4.3.1 (2016/02/12) インスタンスで初期化しているため、null はない。
    393                if( !mustProparty.isEmpty() ) {                                                         // 6.4.3.1 (2016/02/12)
    394                        // 第２の判定。 mustProparty に存在すれば、即抜けする。
    395                        if( mustProparty.containsKey( key ) ) { return; }
    396
    397                        // 第３の判定。複数キー(先頭一致キー)の場合もありうるため、先頭からの比較を行う。
    398                        // 6.4.3.3 (2016/03/04) Iterator 処理を、拡張for分に変更。判定は、keyのみでよい
    399                        for( final String propKey : mustProparty.keySet() ) {
    400                                if( key.startsWith( propKey ) ) { return ; }            // マッチすれば、即抜ける。
    401                        }
    402                }
    403
    404                // 5.1.5.0 (2010/04/01) 判定の条件が、重複していたので修正。
    405                // 6.4.3.1 (2016/02/12) インスタンスで初期化しているため、null はない。
    406                if( !usableProparty.isEmpty() ) {                                                       // 6.4.3.1 (2016/02/12)
    407                        // 第４の判定。 usableProparty に存在すれば、即抜けする。
    408                        if( usableProparty.containsKey( key ) ) { return ; }
    409
    410                        // 第５の判定。複数キー(先頭一致キー)の場合もありうるため、先頭からの比較を行う。
    411                        // 6.4.3.3 (2016/03/04) Iterator 処理を、拡張for分に変更。判定は、keyのみでよい
    412                        for( final String propKey : usableProparty.keySet() ) {
    413                                if( key.startsWith( propKey ) ) { return ; }            // マッチすれば、即抜ける。
    414                        }
    415
    416                        // そこまで探して見つからない場合は、定義外引数エラー
    417                        final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE )
    418                                .append( "-KEY が、指定の整合性リストに含まれていません。" )
    419                                .append( CR )
    420                                .append( "  -KEY=VALUE 形式 BAD Key=[" ).append( key ).append( ']' )              // 6.0.2.5 (2014/10/31) char を append する。
    421                                .append( CR )
    422                                .append( toString() );
    423                        throw new OgRuntimeException( errMsg.toString() );
    424                }
    425        }
    426
    427        /**
    428         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    429         * 値が設定されていない場合は、 null を返します。
    430         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    431         *
    432         * ※ 引数の設定方法が間違っている場合、RuntimeException が throw されます。
    433         *
    434         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。
    435         *
    436         * @param   key 引数のキー
    437         *
    438         * @return  引数に対する値
    439         */
    440        public String getProparty( final String key ) {
    441
    442                final String value = propMap.get( key );
    443
    444                // 値が null で must 設定があり、かつマストキーが指定している場合。
    445                if( value == null &&
    446                        // 6.4.3.1 (2016/02/12) インスタンスで初期化しているため、null はない。
    447                        !mustProparty.isEmpty() &&                                      // 6.4.3.1 (2016/02/12)
    448                        mustProparty.containsKey( key ) ) {
    449                                final String errMsg = "指定の［プロパティ］は、必須キーですが、値が null です。"
    450                                                +       "  Key:" + key + "  説明:" + mustProparty.get( key )
    451                                                + CR + toString() ;
    452                                throw new OgRuntimeException( errMsg );
    453                }
    454
    455                return value ;
    456        }
    457
    458        /**
    459         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    460         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    461         * def には、文字列の初期値を指定しておきます。key に対応する値が、null の場合、
    462         * この def をそのまま返します。
    463         *
    464         * 処理は、getProparty( String ) の結果を、使用しています。
    465         *
    466         * @param   key キー
    467         * @param   def 値が null の場合の初期値
    468         *
    469         * @return  ［プロパティ］
    470         * @see #getProparty( String )
    471         */
    472        public String getProparty( final String key, final String def ) {
    473                final String value = getProparty( key );
    474                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    475                return value == null ? def : value ;
    476        }
    477
    478        /**
    479         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    480         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    481         * def には、文字列の初期値を指定しておきます。key に対応する値が、null の場合、
    482         * この def をそのまま返します。
    483         * list 配列には、登録できる文字列配列を指定します。この文字列に含まれない
    484         * 値が設定されていた場合は、エラーになります。
    485         *
    486         * 処理は、getProparty( String ) の結果を、使用しています。
    487         *
    488         * ※ 引数の設定方法が間違っている場合、RuntimeException が throw されます。
    489         *
    490         * @param   key キー
    491         * @param   def  値が null の場合の初期値
    492         * @param   list 値として存在できる文字列配列(可変長引数)
    493         *
    494         * @return  ［プロパティ］
    495         * @see #getProparty( String )
    496         */
    497        public String getProparty( final String key, final String def, final String... list ) {
    498                final String value = getProparty( key,def );
    499                if( value != null ) {
    500                        boolean isOK = false;
    501                        for( int i=0; i<list.length; i++ ) {
    502                                if( value.equalsIgnoreCase( list[i] ) ) {
    503                                        isOK = true; break;
    504                                }
    505                        }
    506                        if( !isOK ) {
    507                                final String errMsg = key + " は、" + Arrays.toString( list )
    508                                                                        + " から指定してください。" + CR
    509                                                                        + "-" + key + "=[" + value + "]" ;
    510                                throw new OgRuntimeException( errMsg );
    511                        }
    512                }
    513
    514                return value ;
    515        }
    516
    517        /**
    518         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    519         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    520         * def には、数字の初期値を指定しておきます。key に対応する値が、null の場合、
    521         * この def をそのまま返します。
    522         *
    523         * 処理は、getProparty( String ) の結果を、使用しています。
    524         *
    525         * @param   key キー
    526         * @param   def 値が null の場合の初期値
    527         *
    528         * @return  ［プロパティ］
    529         * @see #getProparty( String )
    530         */
    531        public int getProparty( final String key, final int def ) {
    532                final String value = getProparty( key );
    533                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    534                return value == null ? def : Integer.parseInt( value ) ;
    535        }
    536
    537        /**
    538         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    539         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    540         * def には、boolean の初期値を指定しておきます。key に対応する値が、null の場合、
    541         * この def をそのまま返します。
    542         *
    543         * 処理は、getProparty( String ) の結果を、使用しています。
    544         *
    545         * @param   key キー
    546         * @param   def 値が null の場合の初期値
    547         *
    548         * @return  ［プロパティ］
    549         * @see #getProparty( String )
    550         */
    551        public boolean getProparty( final String key, final boolean def ) {
    552                final String value = getProparty( key );
    553                return ( value == null ) ? def : Boolean.parseBoolean( value ) ;                // 6.1.0.0 (2014/12/26) refactoring
    554        }
    555
    556        /**
    557         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    558         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    559         * key プロパティは、通常の引数として指定できる簡易的な値を設定します。
    560         * keyFile プロパティ は、ファイル名を指定し、そのファイルの中身を
    561         * 取得して返します。通常は１行であるが、時には複数行のデータを指定
    562         * したい場合に、２つのパラメータを使いますが、設定値は、同じ引数として
    563         * 使用したいケースに便利です。
    564         * key プロパティと、keyFile プロパティ は、同時指定できません。
    565         * これは、指定方法の間違い等を避ける為です。
    566         * どちらも、null である可能性はあります。
    567         *
    568         * ※ 同時指定時、または、must 必須時に null の場合、RuntimeException が throw されます。
    569         *
    570         * @param   key キー
    571         * @param   keyFile  設定ファイル名
    572         * @param   must 必須条件[true/false]
    573         *
    574         * @return  ［プロパティ］
    575         * @see #getProparty( String )
    576         */
    577        public String getFileProparty( final String key, final String keyFile, final boolean must ) {
    578                return getFileProparty( key,keyFile,null,must );
    579        }
    580
    581        /**
    582         * 内部で使用する［プロパティ］を、キーを指定して取得します。
    583         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    584         * key プロパティは、通常の引数として指定できる簡易的な値を設定します。
    585         * keyFile プロパティ は、ファイル名を指定し、そのファイルの中身を
    586         * 取得して返します。通常は１行であるが、時には複数行のデータを指定
    587         * したい場合に、２つのパラメータを使いますが、設定値は、同じ引数として
    588         * 使用したいケースに便利です。
    589         * key プロパティと、keyFile プロパティ は、同時指定できません。
    590         * これは、指定方法の間違い等を避ける為です。
    591         * どちらも、null である可能性はあります。
    592         *
    593         * ※ 同時指定時、または、must 必須時に null の場合、RuntimeException が throw されます。
    594         *
    595         * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
    596         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
    597         *
    598         * @param   key キー
    599         * @param   keyFile 設定ファイル名
    600         * @param   encode keyFile読取エンコード(null はデフォルトエンコード)
    601         * @param   must 必須条件[true/false]
    602         *
    603         * @return  ［プロパティ］
    604         * @see #getProparty( String )
    605         */
    606        public String getFileProparty( final String key, final String keyFile,
    607                                                                         final String encode,final boolean must ) {
    608                String val     = getProparty( key );
    609                final String valFile = getProparty( keyFile );
    610
    611                if( val != null && valFile != null ) {
    612                        final String errMsg = key + "か、" + keyFile + " は、両方同時に指定できません。[" + val + "],[" + valFile + "]";
    613                        throw new OgRuntimeException( errMsg );
    614                }
    615
    616                if( valFile != null ) {
    617                        // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
    618                        val = FileUtil.getValue( valFile , encode );            // 6.4.5.2 (2016/05/06)
    619                }
    620
    621                if( must && val == null ) {
    622                        final String errMsg = key + "か、" + keyFile + " は、片方必須です。";
    623                        throw new OgRuntimeException( errMsg );
    624                }
    625
    626                return val;
    627        }
    628
    629        /**
    630         * 内部で使用する［プロパティ］を、キーを先頭に含む値を取得します。
    631         * ［プロパティ］のキー部の大文字・小文字は、厳格に判定しています。
    632         * 値が設定されていない場合は、String[0] を返します。
    633         * HybsEntry のキーに設定される値は、引数の先頭キーを除いた文字列です。
    634         * 例えば、"const_" のような値を与えて、const_AA, const_BB, const_CC の
    635         * ３つのキーが選定された場合、キーは、AA, BB, CC のみ返します。
    636         *
    637         * @param   startsKey 引数の先頭のキー
    638         *
    639         * @return  引数に対する［プロパティ］のHybsEntry
    640         * @og.rtnNotNull
    641         */
    642        public HybsEntry[] getEntrys( final String startsKey ) {
    643                final ArrayList<HybsEntry> list = new ArrayList<>();
    644                final int len = startsKey.length();
    645
    646                final Iterator<Map.Entry<String,String>> ite = propMap.entrySet().iterator();   // 4.3.3.6 (2008/11/15) Generics警告対応
    647                while( ite.hasNext() ) {
    648                        final Map.Entry<String,String> entry = ite.next();      // 4.3.3.6 (2008/11/15) Generics警告対応
    649                        final String key = entry.getKey();                                      // 4.3.3.6 (2008/11/15) Generics警告対応
    650                        if( key.startsWith( startsKey ) ) {
    651                                list.add( new HybsEntry( key.substring( len ), entry.getValue() ) );    // 4.3.3.6 (2008/11/15) Generics警告対応
    652                        }
    653                }
    654
    655                return list.toArray( new HybsEntry[list.size()] ) ;
    656        }
    657
    658        /**
    659         * 入力文字列に、{&#064;XXXX}関係の文字列変換を行います。
    660         * 引数に含まれる {&#064;XXXX}=YYYY という入力に対して、inMsg に
    661         * 含まれる{&#064;XXXX} 文字列を、YYYY という文字列に変換します。
    662         * それ以外に、予約文字変換として、
    663         *   {&#064;ARG.XXX}  引数に使用された値を再利用(割り当て)します。
    664         *   {&#064;DATE.XXX} SimpleDateFormat 形式の文字を変換します。(日付、時刻等)
    665         *   {&#064;ENV.XXX}  システムプロパティーの文字を変換します。(java -Dkey=value オプション)
    666         *
    667         * @param  inMsg 入力文字列
    668         *
    669         * @return  変換後文字列
    670         */
    671        public String changeParam( final String inMsg ) {
    672                if( inMsg == null ) { return inMsg; }
    673
    674                String message = inMsg;
    675
    676                // {@ARG.XXXX} 変数の置換処理
    677                int adrs = message.indexOf( "{@ARG." ) ;
    678                while( adrs >= 0 ) {
    679                        final int end = message.indexOf( '}',adrs ) ;
    680                        final String key = message.substring( adrs+6,end );
    681                        final String oldData = "{@ARG." + key + "}" ;
    682                        // 注意：{@XXX}と異なり、{@ARG.XXX} では、XXX で propMap を検索する。
    683                        final String newData = StringUtil.nval( getProparty( key ),"" );
    684                        message = StringUtil.replace( message,oldData,newData );
    685                        adrs = message.indexOf( "{@ARG.",adrs ) ;
    686                }
    687                // {@DATE.XXXX} 変数の置換処理
    688                adrs = message.indexOf( "{@DATE." ) ;
    689                if( adrs >= 0 ) {
    690                        final Date dt = new Date();
    691                        while( adrs >= 0 ) {
    692                                final int end = message.indexOf( '}',adrs ) ;
    693                                final String key = message.substring( adrs+7,end );
    694                                final String oldData = "{@DATE." + key + "}" ;
    695                                final DateFormat formatter = new SimpleDateFormat( key, Locale.JAPAN );
    696                                final String newData = StringUtil.nval( formatter.format(dt),"" );
    697                                message = StringUtil.replace( message,oldData,newData );
    698                                adrs = message.indexOf( "{@DATE.",adrs ) ;
    699                        }
    700                }
    701                // {@ENV.XXXX} 変数の置換処理
    702                adrs = message.indexOf( "{@ENV." ) ;
    703                while( adrs >= 0 ) {
    704                        final int end = message.indexOf( '}',adrs ) ;
    705                        final String key = message.substring( adrs+6,end );
    706                        final String oldData = "{@ENV." + key + "}" ;
    707                        final String newData = System.getProperty( key,"" );
    708                        message = StringUtil.replace( message,oldData,newData );
    709                        adrs = message.indexOf( "{@ENV.",adrs ) ;
    710                }
    711
    712                // 残りのメッセージ本文中の置換文字列を処理します。
    713                adrs = message.indexOf( "{@" ) ;
    714                while( adrs >= 0 ) {
    715                        final int end = message.indexOf( '}',adrs ) ;
    716                        final String key = message.substring( adrs,end+1 );     // +1 注意
    717                        final String oldData = key ;
    718                        // 注意：{@ARG.XXX} と異なり、{@XXX} そのもので propMap を検索する。
    719                        final String newData = StringUtil.nval( getProparty( key ),"" );
    720                        message = StringUtil.replace( message,oldData,newData );
    721                        adrs = message.indexOf( "{@",adrs ) ;
    722                }
    723
    724                return message;
    725        }
    726
    727        /**
    728         * このオブジェクトの内部表現を、文字列にして返します。
    729         * クラス名 ＋ 起動時の引数リストを表示します。
    730         *
    731         * @return  引数に対する値
    732         * @og.rtnNotNull
    733         */
    734        @Override
    735        public String toString() {
    736                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    737
    738                buf.append( "java " ).append( programID ).append( CR );
    739
    740                if( ! argments.isEmpty() ) {
    741                        for( int i=0; i<argments.size(); i++ ) {
    742                                buf.append( ' ' ).append( argments.get(i) );            // 6.0.2.5 (2014/10/31) char を append する。
    743                        }
    744                        buf.append( CR );
    745                }
    746
    747                final Iterator<Map.Entry<String,String>> propIte = propMap.entrySet().iterator();       // 4.3.3.6 (2008/11/15) Generics警告対応
    748                while( propIte.hasNext() ) {
    749                        final Map.Entry<String,String> entry = propIte.next();  // 4.3.3.6 (2008/11/15) Generics警告対応
    750                        final String key = entry.getKey();                                              // 4.3.3.6 (2008/11/15) Generics警告対応
    751                        Object val = entry.getValue();
    752                        if( key.startsWith( "passwd" ) ) {
    753                                val = "*****" ;
    754                        }
    755
    756                        buf.append( "    -" ).append( key ).append( '=' ).append( val );                // 6.0.2.5 (2014/10/31) char を append する。
    757                        buf.append( CR );
    758                }
    759
    760                return buf.toString();
    761        }
    762
    763        /**
    764         * このクラスの使用方法を返します。
    765         *
    766         * @return      このクラスの使用方法
    767         * @og.rtnNotNull
    768         */
    769        public String usage() {
    770                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    771                        .append( toString() )
    772                        .append( CR )
    773                        .append( propMapToString( "[ Must Proparty List ]"  , mustProparty   ) )
    774                        .append( propMapToString( "[ Usable Proparty List ]", usableProparty ) );
    775
    776                return buf.toString();
    777        }
    778
    779        /**
    780         * プロパティーを文字列に変換します。
    781         *
    782         * propMap の キーの最大長さを求め、位置あわせのためのスペースを追加します。
    783         *
    784         * @param       title タイトル
    785         * @param       propMap プロパティー(Mapオブジェクト)
    786         *
    787         * @return      プロパティー文字列
    788         * @og.rtnNotNull
    789         */
    790        private String propMapToString( final String title,final Map<String,String> propMap ) {
    791                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    792
    793                if( propMap != null ) {
    794                        buf.append( title ).append( CR );
    795
    796                        // キーの長さをそろえるための処理
    797                        int maxLen = 0;
    798                        final Iterator<String> keyIte = propMap.keySet().iterator();    // 4.3.3.6 (2008/11/15) Generics警告対応
    799                        while( keyIte.hasNext() ) {
    800                                final int len = keyIte.next().length(); // 4.3.3.6 (2008/11/15) Generics警告対応
    801                                if( len > maxLen ) { maxLen = len; }
    802                        }
    803
    804                        final char[] ch = new char[maxLen];
    805                        Arrays.fill( ch,' ' );          // スペースで埋めます。
    806                        final String SPACE = new String( ch );
    807                        final String VAL_SPACE = CR + SPACE + "        " ;
    808
    809                        final Iterator<Map.Entry<String,String>> propIte = propMap.entrySet().iterator();       // 4.3.3.6 (2008/11/15) Generics警告対応
    810                        while( propIte.hasNext() ) {
    811                                final Map.Entry<String,String> entry = propIte.next();  // 4.3.3.6 (2008/11/15) Generics警告対応
    812                                final String key = entry.getKey();                                              // 4.3.3.6 (2008/11/15) Generics警告対応
    813                                String val = entry.getValue();                                                  // 4.3.3.6 (2008/11/15) Generics警告対応
    814                                if( val != null ) { val = val.replaceAll( CR,VAL_SPACE ); }
    815                                buf.append( "    -" ).append( key );
    816                                buf.append( SPACE.substring( key.length() ) );  // 使用されるキー
    817                                buf.append( " : " ).append( val ) ;     // その説明
    818                                buf.append( CR );
    819                        }
    820                }
    821
    822                return buf.toString();
    823        }
    824}