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 は、3つのタイプに分かれます。
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 の整合性チェックは、3つのパターンがあります。
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                // 第1の判定。 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                        // 第2の判定。 mustProparty に存在すれば、即抜けする。
375                        if( mustProparty.containsKey( key ) ) { return; }
376
377                        // 第3の判定。複数キー(先頭一致キー)の場合もありうるため、先頭からの比較を行う。
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                        // 第4の判定。 usableProparty に存在すれば、即抜けする。
390                        if( usableProparty.containsKey( key ) ) { return ; }
391
392                        // 第5の判定。複数キー(先頭一致キー)の場合もありうるため、先頭からの比較を行う。
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         * 取得して返します。通常は1行であるが、時には複数行のデータを指定
542         * したい場合に、2つのパラメータを使いますが、設定値は、同じ引数として
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         * 取得して返します。通常は1行であるが、時には複数行のデータを指定
567         * したい場合に、2つのパラメータを使いますが、設定値は、同じ引数として
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         * 3つのキーが選定された場合、キーは、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}