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