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.awt.Color;
019import java.io.PrintWriter;
020import java.io.StringWriter;
021import java.io.UnsupportedEncodingException;
022import java.net.URLEncoder;
023import java.net.URLDecoder;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Enumeration;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.Map;
030import java.util.StringTokenizer;
031import java.util.Locale ;                               // 5.7.2.3 (2014/01/31)
032import java.nio.charset.Charset;                // 5.5.2.6 (2012/05/25)
033
034
035/**
036 * StringUtil.java は、共通的に使用される String関連メソッドを集約した、クラスです。
037 *
038 * @og.group ユーティリティ
039 *
040 * @version  4.0
041 * @author       Kazuhiko Hasegawa
042 * @since    JDK5.0,
043 */
044public final class StringUtil {
045
046        /** バッファの初期容量を通常より多い目に設定します。(200)  */
047        private static final int BUFFER_MIDDLE = 200;
048
049        /** システム依存の改行記号をセットします。 */
050        private static final String CR = System.getProperty("line.separator");
051
052        /**
053         * プラットフォーム依存のデフォルトの Charset です。
054         * プラットフォーム依存性を考慮する場合、エンコード指定で作成しておく事をお勧めします。
055         *
056         * @og.rev 5.5.2.6 (2012/05/25) findbugs対応
057         */
058        public static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ;
059
060        /**
061         * code39 のチェックデジット計算に使用する モジュラス43 の変換表です。
062         *
063         */
064        private static final String MODULUS_43 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" ;
065
066        /**
067         * getUnicodeEscape で使用する桁合わせ用文字列配列です。
068         * Unicodeの HexString 変換後の桁に応じて、埋め合わせします。
069         *
070         */
071        private static final String[] UTF_STR = { "&#x0000", "&#x000", "&#x00", "&#x0", "&#x" };
072
073        // 4.0.3.0 (2007/12/26) 色コードにPURPLE を追加
074        // 5.7.8.0 (2014/07/04) 透明追加
075        private static final Map<String,Color> CLR_MAP;
076        static {
077                CLR_MAP = new HashMap<String,Color>();
078                CLR_MAP.put( "BLACK"            ,Color.BLACK            );
079                CLR_MAP.put( "BLUE"                     ,Color.BLUE                     );
080                CLR_MAP.put( "CYAN"                     ,Color.CYAN                     );
081                CLR_MAP.put( "DARK_GRAY"        ,Color.DARK_GRAY        );
082                CLR_MAP.put( "GRAY"                     ,Color.GRAY                     );
083                CLR_MAP.put( "GREEN"            ,Color.GREEN            );
084                CLR_MAP.put( "LIGHT_GRAY"       ,Color.LIGHT_GRAY       );
085                CLR_MAP.put( "MAGENTA"          ,Color.MAGENTA          );
086                CLR_MAP.put( "ORANGE"           ,Color.ORANGE           );
087                CLR_MAP.put( "PINK"                     ,Color.PINK                     );
088                CLR_MAP.put( "RED"                      ,Color.RED                      );
089                CLR_MAP.put( "WHITE"            ,Color.WHITE            );
090                CLR_MAP.put( "YELLOW"           ,Color.YELLOW           );
091                CLR_MAP.put( "PURPLE"           ,new Color( 8388736 )   );              // #800080
092                CLR_MAP.put( "TRANSPARENT"      ,new Color( 255,255,255,0 )     );      // 5.7.8.0 (2014/07/04) 透明追加
093        }
094
095        /**
096         *      デフォルトコンストラクターをprivateにして、
097         *      オブジェクトの生成をさせないようにする。
098         *
099         */
100        private StringUtil() {}
101
102        /**
103         * UTF-8 で、URLエンコードを行います。
104         * このメソッドは、JDK1.4 以上でないと使用できません。
105         *
106         * @param       value エンコードする文字列
107         *
108         * @return       指定の文字コードでURLエンコードされた文字列
109         */
110        public static String urlEncode( final String value ) {
111                if( value == null ) { return ""; }
112
113                try {
114                        return URLEncoder.encode( value,"UTF-8" );
115                }
116                catch( UnsupportedEncodingException ex ) {
117                        String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
118                                                + ex.getMessage() ;
119                        throw new RuntimeException( errMsg,ex );
120                }
121                catch( RuntimeException ex2 ) {         // 3.6.0.0 (2004/09/17)
122                        String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
123                                                + ex2.getMessage();
124                        throw new RuntimeException( errMsg,ex2 );
125                }
126        }
127
128        /**
129         * UTF-8 でURLエンコードされた文字列をデコードします。
130         * このメソッドは、JDK1.4 以上でないと使用できません。
131         *
132         * @og.rev 5.4.5.0 追加
133         * @param       value デコードする文字列
134         *
135         * @return       デコードされた文字列
136         */
137        public static String urlDecode( final String value ) {
138                try {
139                        return URLDecoder.decode( value,"UTF-8" );
140                }
141                catch( UnsupportedEncodingException ex ) {
142                        String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
143                                                + ex.getMessage() ;
144                        throw new RuntimeException( errMsg,ex );
145                }
146                catch( RuntimeException ex2 ) {         // 3.6.0.0 (2004/09/17)
147                        String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
148                                                + ex2.getMessage();
149                        throw new RuntimeException( errMsg,ex2 );
150                }
151        }
152
153        /**
154         * 文字列の後ろのスペースを削除します。
155         * String クラスの trim()メソッドは、文字列の両方のスペースを削除しますが、
156         * この rTrim( String ) は、後ろの半角スペースのみ、詰めます。
157         * 注意:'\u0020' (スペース文字) より小さい文字を切り取ります。
158         *
159         * @param       str 元の文字列
160         *
161         * @return      後ろの半角スペースを詰めた、新しい文字列
162         */
163        public static String rTrim( final String str ) {
164                if( str == null )  { return null; }
165                int count = str.length();
166
167                int len = count;
168
169                while ( 0 < len && str.charAt(len-1) <= ' ' ) {
170                        len--;
171                }
172                return (len < count) ? str.substring(0, len) : str;
173        }
174
175        /**
176         * 文字列の後ろから、" .0" の文字を削除した数字型文字列を返します。
177         * 数字型文字列は、入力文字列の後ろの スペース、小数点、ゼロを削除します。
178         * また、先頭が、"." で始まる場合は、"0" を追加します。
179         * 例: "123.00" ⇒ "123" , ".123" ⇒ "0.123"
180         *
181         * @og.rev 3.8.8.1 (2007/01/10) 新規作成
182         *
183         * @param       str 元の文字列
184         *
185         * @return      数字文字列化された、新しい文字列
186         */
187        public static String toNumber( final String str ) {
188                if( str == null )  { return null; }
189
190                String rtn = str.trim() ;
191
192                int adrs = rtn.indexOf( '.' );
193                int count = rtn.length();
194                int len = count;
195
196                if( adrs >= 0 ) {
197                        while ( adrs < len && ".0".indexOf( rtn.charAt(len-1) ) >= 0 ) {
198                                len--;
199                        }
200                }
201
202                if( len < count ) { rtn = rtn.substring(0, len); }
203                if( adrs == 0 ) { rtn = "0" + rtn; }
204
205                return rtn ;
206        }
207
208        /**
209         * 文字列の前方のゼロ(0)を削除します。
210         * 先頭の0を削除するまえに、trim して、スペースを削除しておきます。
211         *
212         * @og.rev 3.5.4.5 (2004/01/23) 新規追加
213         *
214         * @param       in 元の文字列
215         *
216         * @return      前方のゼロ(0)を削除した、新しい文字列
217         */
218        public static String lTrim0( final String in ) {
219                if( in == null )  { return null; }
220                String str = in.trim();
221                int count = str.length();
222
223                int len = 0;
224
225                while ( count > len && str.charAt(len) == '0' ) {
226                        len++;
227                }
228
229                if( len == 0 ) { return str; }                          // 先頭がゼロでない。
230                else if( len == count ) { return "0"; }         // すべてがゼロ
231                else if( str.charAt(len) == '.' ) { return "0" + str.substring(len); }
232                else { return str.substring(len); }
233        }
234
235        /**
236         * 文字列配列の各要素の後ろのスペースを削除します。
237         * 個々の配列要素に対して、rTrim( String str ) を適用します。
238         * 元の文字列配列に直接作用するのではなく、新しい文字列配列に
239         * 結果をコピーして返します。
240         * ただし、元の文字列配列が、null か、length == 0 の場合は、
241         * 元の文字列配列(アドレス)を返します。
242         * 注意:'\u0020' (スペース文字) より小さい文字を切り取ります。
243         *
244         * @param       str 元の文字列
245         *
246         * @return      後ろの半角スペースを詰めた、新しい文字列
247         */
248        public static String[] rTrims( final String[] str ) {
249                if( str == null || str.length == 0 ) { return str; }
250
251                String[] rtn = new String[ str.length ];
252                for( int i=0; i<str.length; i++ ) {
253                        rtn[i] = rTrim( str[i] );
254                }
255                return rtn ;
256        }
257
258        /**
259         * 文字列の前後のダブルクオートを取り外します。
260         * 前後にダブルクオートが入っていなければ、そのままの文字列を返します。
261         * 前後に入っていない(片方のみなど)場合も、そのままの文字列を返します。
262         *
263         * @param       str 元の文字列
264         *
265         * @return      ダブルクオートを取り外した新しい文字列
266         */
267        public static String csvOutQuote( final String str ) {
268                if( str == null )  { return null; }
269                int end = str.length();
270
271                if( end < 2 || str.charAt(0) != '"' || str.charAt( end-1 ) != '"' ) {
272                        return str;
273                }
274
275                return str.substring( 1,end-1 ) ;
276        }
277
278        /**
279         * 内部で使われる byte[] から String 生成 メソッド
280         *
281         * @param       byteValue        変換するバイト列
282         * @param       start            変換開始アドレス
283         * @param       length           変換バイト数
284         * @param       encode           変換する文字エンコード
285         *
286         * @return      変換後文字列
287         */
288        public static String makeString( final byte[] byteValue, final int start, final int length,final String encode ) {
289
290                if( encode.startsWith( "Unicode" ) ) {
291                        String errMsg = "Unicode文字列は、変換できません。[" + encode + "]"  + CR;
292                        throw new RuntimeException( errMsg );
293                }
294
295                String rtn = null;
296                if( byteValue != null ) {
297                        try {
298                                // encode コードで変換されている byte[] を、String に変換。
299                                rtn = new String( byteValue,start,length,encode );
300                        } catch( UnsupportedEncodingException ex ) {      // 変換コードが存在しないエラー
301                                String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
302                                                        + ex.getMessage() ;
303                                throw new RuntimeException( errMsg,ex );
304                        }
305                }
306                return rtn;
307        }
308
309        /**
310         * 指定の文字列をバイトコードに変換します。
311         * 引数の文字列が null の場合は、return は、byte[0] を返します。
312         *
313         * @param       value    変換するストリング値
314         * @param       encode   変換する文字エンコード
315         *
316         * @return      変換後文字列
317         */
318        public static byte[] makeByte( final String value,final String encode ) {
319                byte[] rtnByte = new byte[0];
320                if( value != null ) {
321                        try {
322                                rtnByte = value.getBytes( encode );             // byte[] に encode コードで変換。
323                        } catch( UnsupportedEncodingException ex ) {      // 変換コードが存在しないエラー
324                                String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
325                                                        + ex.getMessage();
326                                throw new RuntimeException( errMsg,ex );
327                        }
328                }
329                return rtnByte;
330        }
331
332        /**
333         * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
334         * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
335         * 内部にセットした文字列は、変化しません。
336         *
337         * @param       str      Fill埋めする文字列
338         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
339         *
340         * @return      Fill埋めした新しいStringを返す。
341         */
342        public static String stringXFill( final String str,final int su_fill ) {
343                char[] charValue ;
344
345                if( str == null ) { charValue = new char[0]; }
346                else              { charValue = str.toCharArray(); }
347                int len = charValue.length;
348
349                if( su_fill < len ) {
350                        String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。)"
351                                        + "su_fill[" + su_fill + "], len[" + len + "]" + CR
352                                        + "input=[" + str + "]" + CR;
353                        throw new RuntimeException( errMsg );
354                }
355
356                char[] charbuf = new char[ su_fill ];                   // 移す char 配列を新規作成
357                Arrays.fill( charbuf,' ' );
358                System.arraycopy( charValue,0,charbuf,0,len );
359
360                return new String( charbuf );            // コピーした配列全てを文字列に変換
361        }
362
363        /**
364         * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
365         * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
366         * 内部にセットした文字列は、変化しません。
367         *
368         * @param       str      Fill埋めする文字列
369         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
370         * @param       encode   Fill埋めする文字列の文字エンコード
371         *
372         * @return      Fill埋めした新しいStringを返す。
373         */
374        public static String stringFill( final String str,final int su_fill,final String encode ) {
375                if( su_fill < 0 ) {
376                        String errMsg = "指定文字数が負です。[" + su_fill + "]";
377                        throw new RuntimeException( errMsg );
378                }
379
380                byte[] byteValue = makeByte( str,encode );
381                int len = byteValue.length;
382
383                // 内部文字列が指定長より長い場合
384                if( len >= su_fill ) {
385                        return makeString( byteValue,0,su_fill,encode );
386                }
387                else {
388                        byte[] space = makeByte( " ",encode );
389                        int spaceLen = space.length ;
390                        if( spaceLen == 4 ) {   // encode が、UnicodeLittle の場合の特殊処理
391                                space[0] = space[2];
392                                space[1] = space[3];
393                                spaceLen = 2;
394                        }
395                        byte[] bytebuf = new byte[ su_fill ];
396                        for( int i=0; i<len; i++ ) { bytebuf[i] = byteValue[i]; }
397
398                        int k = 0;
399                        for( int j=len; j<su_fill; j++ ) {              // 余った部分は、スペース埋め
400                                if( k >= spaceLen ) { k = 0; }
401                                bytebuf[j] = space[k++];
402                        }
403                        return makeString( bytebuf,0,su_fill,encode );  // 新たに、すべての長さの部分文字列を作成する。
404                }
405        }
406
407        /**
408         * 整数のフォーム( 12 で、整数部 12桁を表す)に合った新しい文字列を作り、それを返します。
409         * 実行できるのは、整数の String に対してのみです。
410         * 内部にセットした文字列は、変化しません。
411         *
412         *              String str = StringUtil.intFill( "123",10 );
413         *
414         *              実行結果:"0000000123"
415         *
416         * @param       str     整数の String
417         * @param       su_fill フォームを表す数字 ( 12 で、整数部 12桁を表す)
418         *
419         * @return      整数のフォームに合った文字列
420         */
421        public static String intFill( final String str,final int su_fill ) {
422                if( su_fill < 0 ) {
423                        String errMsg = "指定文字数が負です。[" + su_fill + "]";
424                        throw new RuntimeException( errMsg );
425                }
426
427                char[] charbuf = new char[ su_fill ];                   // 移す char 配列を新規作成
428                Arrays.fill( charbuf,'0' );
429
430                if( str == null ) { return new String( charbuf ); }
431
432                char[] charValue = str.toCharArray();
433                int len = charValue.length;
434
435                if( su_fill < len ) {
436                        String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。) su_fill[" + su_fill + "], len[" + len + "]";
437                        throw new RuntimeException( errMsg );
438                }
439
440                System.arraycopy( charValue,0,charbuf,su_fill-len,len );
441
442                return new String( charbuf );            // コピーした配列全てを文字列に変換
443        }
444
445        /**
446         * 全角スペースで固定長(半角換算の数)に変換した文字列を返します。
447         *
448         * @param       str      Fill埋めする文字列
449         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
450         * @param       encode   Fill埋めする文字列の文字エンコード
451         *
452         * @return      全角スペースでFill埋めした新しいStringを返す。
453         */
454        public static String stringKFill( final String str,final int su_fill,final String encode ) {
455                if( su_fill < 0 ) {
456                        String errMsg = "指定文字数が負です。[" + su_fill + "]";
457                        throw new RuntimeException( errMsg );
458                }
459
460                byte[] byteValue = makeByte( str,encode );
461                int len = byteValue.length;
462
463                // 内部文字列が指定長より長い場合
464                if( len >= su_fill ) {
465                        return makeString( byteValue,0,su_fill,encode );
466                }
467                else {
468                        byte[] space = makeByte( " ",encode );
469                        int spaceLen = space.length ;
470                        byte[] bytebuf = new byte[ su_fill ];
471                        for( int i=0; i<len; i++ ) { bytebuf[i] = byteValue[i]; }
472                        int k = 0;
473                        for( int j=len; j<su_fill; j++ ) {              // 余った部分は、スペース埋め
474                                if( k >= spaceLen ) { k = 0; }
475                                bytebuf[j] = space[k++];
476                        }
477                        return makeString( bytebuf,0,su_fill,encode );  // 新たに、すべての長さの部分文字列を作成する。
478                }
479        }
480
481        /**
482         * 小数点のフォームに合った新しい文字列を作り、文字列を返します。
483         * 現在は、小数点が頭に付いたり、最後に付く場合の対応はしていません。
484         * フォームは、12.4 で、 000000000010.1000 という形で、ピリオドを含みます。
485         *
486         *  // 半角 整数部 10 桁 小数部 5桁で固定長の文字を得る。
487         *  String str = StringUtil.realFill( "123.45" ,10.5 ) ;
488         *
489         *  実行結果:0000000123.45000
490         *
491         * @param       str             整数の String
492         * @param       su_fill フォームを表す実数       ( 12.4 で、整数部 12桁、小数部 4桁 計17桁 )
493         *
494         * @return      value   小数点のフォーム文字列
495         */
496        public static String realFill( final String str,final double su_fill ) {
497                if( su_fill < 0 ) {
498                        String errMsg = "指定文字数が負です。[" + su_fill + "]";
499                        throw new RuntimeException( errMsg );
500                }
501
502                int su_seisu = (int)(su_fill);                                             // 指定のフォームの整数部を取り出す。
503                int su_shosu = (int)(su_fill*10 - su_seisu*10);            // 小数部を取り出しす。
504                char[] charbuf = new char[ su_seisu + su_shosu + 1 ];  // 移す char 配列
505                Arrays.fill( charbuf,'0' );
506
507                if( str == null ) {
508                        charbuf[su_seisu] = '.' ;
509                        return new String( charbuf );
510                }
511
512                char[] charValue = str.toCharArray();
513                int len = charValue.length;
514
515                // 検査する文字列の加工(検査文字列は、インデックスの値とバイト数で文字数を求める。)
516                // 小数点の位置を求める。 本当は、String クラスの indexOf で求めず、byte[] で検索すべきである。
517                int valueindex = str.indexOf( '.' );
518                if( valueindex < 0 ) {                                                                  // valueform 自体が、合っていない。
519                        String errMsg = "元の文字列に小数点が、含まれません。";
520                        throw new RuntimeException( errMsg );
521                }
522                int su_valueseisu = valueindex;                                  // 整数部の文字数は、小数点の位置と同じ
523                int su_valueshosu = len - valueindex - 1 ;              // 小数部の文字数は、全文字数-整数文字数-1
524
525                // フォームの整数文字数 ー 加工文字の整数文字部 = 転送先配列位置
526                int to_index = su_seisu - su_valueseisu;
527                if( to_index < 0 ) {
528                        String errMsg = "元の数字が、フォームより長いです。(数字が壊れます。) form[" + su_fill + "]";
529                        throw new RuntimeException( errMsg );
530                }
531                int end_index;
532                // 転送先配列終了位置は、お互いの小数部の文字数により、短い方を選ぶ。
533                if( su_shosu < su_valueshosu ) { end_index = su_seisu + su_shosu + 1; }
534                else                                               { end_index = su_seisu + su_valueshosu + 1; }
535
536                int from_index = 0;
537                while( to_index < end_index ) {
538                        charbuf[to_index++] = charValue[from_index++];     // 転送(移し替え)
539                }
540                return new String( charbuf );            // コピーした配列全てを文字列に変換
541        }
542
543        /**
544         * ストリングの部分文字列を,別の文字列に置換えたストリングを返します。
545         * 例えば,リターンコードを&lt; br /&gt;に置換えて,画面上に改行表示させるが可能です。
546         *
547         * @og.rev 5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
548         *
549         * @param       target 元の文字列
550         * @param       from   置換元部分文字列
551         * @param       to         置換先部分文字列
552         *
553         * @return      置換えた文字列
554         */
555        public static String replace( final String target,final String from,final String to ) {
556                if( target == null || from == null || to == null || target.indexOf( from ) < 0 ) { return target; }
557
558                StringBuilder strBuf = new StringBuilder( target.length() );
559
560                int start = 0;
561                int end   = target.indexOf( from,start );
562                while( end >= 0 ) {
563                        strBuf.append( target.substring( start,end ) );
564                        strBuf.append( to );
565                        start = end + from.length();
566                        end   = target.indexOf( from,start );
567                }
568
569                if( start > 0 ) {
570                        strBuf.append( target.substring( start ) );
571                        return strBuf.toString();
572                }
573                else {
574                        return target;                  // 3.4.0.2 (2003/09/05)
575                }
576        }
577
578        /**
579         * 引数の AA:01 BB:02 CC:03 … 形式の、元値:新値のスペース区切り文字列を元に、
580         * 元値を新値に置き換えます。
581         * これは、部分置換ではなく、完全一致で処理します。
582         * caseStr が null や、マッチしなかった場合は、元の値を返します。
583         * その場合、ignoreCase=true としている場合は、元の文字列 も大文字に変換されて返されます。
584         *
585         * ゼロ文字列を元値や新値で使用することは可能ですが、スペースを使用することはできません。
586         *
587         * @og.rev 5.7.2.3 (2014/01/31) 新規追加
588         *
589         * @param       target          元の文字列
590         * @param       caseStr         置換リスト(AA:01 BB:02 CC:03 … 形式)。null の場合は、比較しない。
591         * @param       ignoreCase      true:大文字として比較 / false:そのまま比較
592         *
593         * @return      元の文字列を置き換えた結果。置換リストに存在しなければ、元の文字列を返す。
594         */
595        public static String caseReplace( final String target,final String caseStr,final boolean ignoreCase ) {
596                if( target == null ) { return target; }
597
598                String rtn = ignoreCase ? target.toUpperCase(Locale.JAPAN) : target ;
599
600                if( caseStr != null ) {
601                        String caseTmp = " " + caseStr.trim() + " " ;           // CASE文字列の形式をそろえる。
602
603                        int adrs = caseTmp.indexOf( " " + rtn + ":" );          // 前スペースと後ろコロンで、単語を確定する。
604                        if( adrs >= 0 ) {
605                                int st = caseTmp.indexOf( ':' , adrs+1 );               // 最初のコロンの位置。元値:新値 の 新値 の取出
606                                int ed = caseTmp.indexOf( ' ' , st+1 );                 // コロンの次から、最初のスペースの位置
607                                if( st >= 0 && ed >= 0 ) {
608                                        rtn = caseTmp.substring( st+1,ed );                     // コロンの次から、スペースの前までを切り出す。
609                                }
610                        }
611                }
612
613                return rtn ;
614        }
615
616        /**
617         * String型の配列から、カンマ(,)で連結されたString を作成します。
618         * これは,配列を表示用に変換する為のものです。
619         * array2line( array, ",", 0 ); と同等です。
620         *
621         * @param       array           元の文字列配列
622         *
623         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
624         */
625        public static String array2csv( final String[] array ) {
626                return array2line( array, ",", 0 );
627        }
628
629        /**
630         * String型の配列から、セパレーターで連結されたString を作成します。
631         * これは,配列を表示用に変換する為のものです。
632         *
633         * @param       array           元の文字列配列
634         * @param       separator       区切り記号
635         *
636         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
637         */
638        public static String array2line( final String[] array,final String separator ) {
639                return array2line( array, separator,0 );
640        }
641
642        /**
643         * String型の配列から、セパレーターで連結されたString を作成します。
644         * これは,配列を表示用に変換する為のものです。
645         *
646         * @param       array           元の文字列配列
647         * @param       separator       区切り記号
648         * @param       start           配列の連結開始アドレス
649         *
650         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
651         */
652        public static String array2line( final String[] array,final String separator,final int start ) {
653                if( array == null || array.length <= start ) { return ""; }
654
655                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
656
657                rtn.append( valueOf( array[start] ) );
658                for(int i=start+1; i < array.length; i++) {
659                        rtn.append( separator );
660                        rtn.append( valueOf( array[i] ) );
661                }
662                return rtn.toString();
663        }
664
665        /**
666         * Enumerationから、オブジェクト配列データを返します。
667         * これは,Enumerationを表示用に変換する為のものです。
668         *
669         * @param       enume   元のEnumeration
670         *
671         * @return      オブジェクト配列
672         */
673        public static Object[] enume2Array( final Enumeration<?> enume ) {              // 4.3.3.6 (2008/11/15) Generics警告対応
674                if( enume == null || ! enume.hasMoreElements() ) { return new Object[0]; }
675
676                ArrayList<Object> obj = new ArrayList<Object>();
677
678                while( enume.hasMoreElements() ) {
679                        obj.add( enume.nextElement() );
680                }
681                return obj.toArray();
682        }
683
684        /**
685         * Enumerationから、オブジェクト配列データを返します。
686         * これは,Enumerationを表示用に変換する為のものです。
687         *
688         * @param       enume   元のEnumeration
689         * @param       objs - 配列が十分な大きさを持つ場合は、Vector の要素が格納される配列。
690         *                      そうでない場合は、要素を格納するために同じ実行時の型の新しい配列が割り当てられる
691         * @return      オブジェクト配列
692         */
693        public static Object[] enume2Array( final Enumeration<?> enume,final Object[] objs ) {  // 4.3.3.6 (2008/11/15) Generics警告対応
694                if( enume == null || ! enume.hasMoreElements() ) { return objs ; }
695
696                ArrayList<Object> list = new ArrayList<Object>();
697
698                while( enume.hasMoreElements() ) {
699                        list.add( enume.nextElement() );
700                }
701                return list.toArray( objs );
702        }
703
704        /**
705         * Iteratorから、セパレーターで連結されたString を作成します。
706         * これは,Enumerationを表示用に変換する為のものです。
707         *
708         * @param       ite             元のIterator
709         * @param       separator       区切り記号
710         *
711         * @return      一列に変換した文字列
712         */
713        public static String iterator2line( final Iterator<?> ite,final String separator ) {
714                if( ite == null || ! ite.hasNext() ) { return ""; }
715
716                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
717
718                rtn.append( valueOf( ite.next() ) );
719                while( ite.hasNext() ) {
720                        rtn.append( separator );
721                        rtn.append( valueOf( ite.next() ) );
722                }
723                return rtn.toString();
724        }
725
726        /**
727         * カンマ(,)で連結された String を、配列に分解して、その値を返します。
728         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
729         * メニューなりリストを作成するのに便利です。
730         * 要素が空の場合は、必ずカンマの間にスペースを入れて記述してください。
731         * 分割後の文字列の前後のスペースは、削除されます。
732         *
733         * @param       csvData         元のデータ
734         *
735         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
736         */
737        public static String[] csv2Array( final String csvData ) {
738                return csv2Array( csvData, ',', 0 );
739        }
740
741        /**
742         * 区切り文字で連結された String を、配列に分解して、その値を返します。
743         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
744         * メニューなりリストを作成するのに便利です。
745         * 連続した区切り文字は、1文字に分割します。
746         * 分割後の文字列の前後のスペースは、削除されます。
747         *
748         * @param       csvData         元のデータ
749         * @param       separator       区切り文字
750         *
751         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
752         */
753        public static String[] csv2Array( final String csvData,final char separator ) {
754                return csv2Array( csvData,separator,0 );
755        }
756
757        /**
758         * 区切り文字で連結された String を、配列に分解して、その値を返します。
759         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
760         * メニューなりリストを作成するのに便利です。
761         * 連続した区切り文字は、1文字に分割します。
762         * 分割後の文字列の前後のスペースは、削除されます。
763         * 第3の引数は、リターンする配列の個数を指定します。ただし、第一引数がNULLや、ゼロ文字列
764         * などの不正な情報の場合は、通常と同じく 長さゼロの配列を返します。
765         * len=0 を指定すると分解したデータの個数分の配列を作成します。指定の長さが短い場合は、
766         * そこまで分のみ取り込みます。指定の長さが長い場合は、余分に配列を作成します。
767         * セットされる値は、"" です。
768         *
769         * @og.rev 3.8.5.1 (2006/05/08) 設定配列の数を指定できるように変更
770         * @og.rev 3.8.8.2 (2007/01/26) 分割後の値の前後のスペースは削除します。
771         *
772         * @param       csvData         元のデータ
773         * @param       separator       区切り文字
774         * @param       len                     指定の長さの配列で返します。
775         *
776         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
777         */
778        public static String[] csv2Array( final String csvData,final char separator, final int len ) {
779                if( csvData == null || csvData.length() == 0 ) { return new String[0] ; }
780
781                CSVTokenizer token = new CSVTokenizer( csvData,separator );
782
783                int count = (len > 0 ) ? len : token.countTokens() ;
784                String[] rtn = new String[ count ];
785                int i = 0;
786                for( ; i<count && token.hasMoreTokens() ; i++ ) {
787                        rtn[i] = token.nextToken().trim();      // 3.8.8.2 (2007/01/26)
788                }
789                for( ; i<count; i++ ) {
790                        rtn[i] = "" ;
791                }
792
793                return rtn;
794        }
795
796        /**
797         * 区切り文字で連結された String を、配列に分解して、その値を返します。
798         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
799         * メニューなりリストを作成するのに便利です。
800         * csv2Array と異なり、連続した区切り文字は、分割せずにトークンのみ切り出します。
801         * トークンは、カンマ(,)のみで区切り、その後 trim() により
802         * 前後のスペースを削除します。
803         *
804         * @param       csvData         元のデータ
805         *
806         * @return      文字列配列
807         */
808        public static String[] csv2ArrayOnly( final String csvData ) {
809                if( csvData == null || csvData.length() == 0 ) { return new String[0] ; }
810
811                StringTokenizer token = new StringTokenizer( csvData,"," );
812
813                ArrayList<String> list = new ArrayList<String>();
814                while( token.hasMoreTokens() ) {
815                        String temp = token.nextToken().trim();
816                        if( temp.length() > 0 ) { list.add( temp ); }
817                }
818
819                return list.toArray( new String[list.size()] );
820        }
821
822        /**
823         * カンマ(,)、ハイフン(-)で連結された String を、配列に分解して、その値を返す処理のスペシャル版です。
824         * 0,1,3,5-8,10-* などの数字文字列から、必要な数字をピックアップした文字配列を返します。
825         * 引数の maxNo は、"*" が指定された場合の、最大の数値です。
826         * よって、"*" は、単独(1文字)では、0-maxNo を表し、N-* では、N-maxNo を意味します。
827         * カンマ区切りで指定される値は、基本的に数字で、重複(1,1,2,2)、逆転(3,2,1)で指定できます。
828         * 5-3 と指定した場合は、5,4,3 に分解されます。逆順に登録されます。
829         * また、一文字だけの場合は、アルファベット(a-z,A-Zなど)も指定する事が可能です。
830         * アルファベットの場合は、"*" は指定できません。
831         * 重複削除、昇順並べ替え等が、必要な場合は、取得後の配列を操作してください。
832         *
833         * @og.rev 5.5.7.2 (2012/10/09) 新規追加
834         *
835         * @param       csvData 0,1,3,5-8,10-* などのCSV-ハイフン文字列
836         * @param       maxNo "*" が指定された場合の、最大数
837         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
838         */
839        public static String[] csv2ArrayExt( final String csvData , final int maxNo )  {
840                if( csvData == null || csvData.length() == 0 ) { return new String[0] ; }
841
842                String strData = csvData.replace( "-*" , "-" + maxNo );         // まず、N-* 形式を、N-maxNo に変換します。
843                strData        = strData.replace( "*"  , "0-" + maxNo );        // その後、"*" 単独(1文字)を、0-maxNo に変換します。
844
845                ArrayList<String> noList = new ArrayList<String>();
846
847                String[] nos = strData.split( "," );            // カンマで分解。N , N-M , N-* のどれか
848                for( int i=0; i<nos.length; i++ ) {
849                        String sno = nos[i] ;
850                        int hai = sno.indexOf( '-' );
851                        // ハイフンが含まれているときは前後に分解して、間を埋める
852                        if( hai > 0 ) {
853                                String st1 = sno.substring( 0,hai );    // 先頭からハイフンまで
854                                String st2 = sno.substring( hai+1 );    // ハイフンから最後まで
855                                if( st1.length() == 1 &&  st2.length() == 1 ) {         // ともに1文字の場合は、char化して処理。(英数字処理)
856                                        char ch1 = st1.charAt(0);
857                                        char ch2 = st2.charAt(0);
858                                        if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++) ); } }
859                                        else                    { while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
860                                }
861                                else {
862                                        int ch1 = Integer.parseInt( st1 );
863                                        int ch2 = Integer.parseInt( st2 );
864                                        if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++) ); } }
865                                        else                    { while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
866                                }
867                        }
868                        else {
869                                noList.add( String.valueOf(sno) );
870                        }
871                }
872                return noList.toArray( new String[noList.size()] ) ;
873        }
874
875        /**
876         * Object 引数の文字列表現を返します。
877         * これは,String.valueOf とほぼ同じ動作をしますが、引数が null の場合に、
878         * "null" という文字列を返すのではなく、なにもない文字列 "" を返します。
879         *
880         * @param       obj    文字列表現すべき元のオブジェクト
881         *
882         * @return      引数が null の場合は、"" に等しい文字列。そうでない場合は、obj.toString() の値
883         */
884        public static String valueOf( final Object obj ) {
885                if( obj == null ) { return "";                     }
886                else                      { return obj.toString(); }
887        }
888
889        /**
890         * HTML上のエスケープ文字を変換します。
891         *
892         * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
893         * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
894         * セキュリティーホールになる可能性があるので、注意してください。
895         *
896         * @param       input HTMLエスケープ前の文字列
897         *
898         * @return      エスケープ文字に変換後の文字列
899         */
900        public static String htmlFilter( final String input ) {
901                if( input == null || input.length() == 0 ) { return ""; }
902                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
903                char ch;
904                for(int i=0; i<input.length(); i++) {
905                        ch = input.charAt(i);
906                        switch( ch ) {
907                                case '<'  : rtn.append("&lt;");   break;
908                                case '>'  : rtn.append("&gt;");   break;
909                                case '"'  : rtn.append("&quot;"); break;
910                                case '\'' : rtn.append("&#39;");  break;
911                                case '&'  : rtn.append("&amp;");  break;
912                                default   : rtn.append(ch);
913                        }
914                }
915                return rtn.toString() ;
916        }
917
918        /**
919         * JavaScript 等の引数でのクオート文字をASCII変換します。
920         *
921         * JavaScript の引数の値に、ダブルクオート(")、シングルクオート(')が
922         * 含まれると、文字列を表す為に前後に指定しているクオートと混乱し、
923         * データを表現できないケースがあります。その場合には、クオート文字を
924         * ASCII文字に置き換える事で、指定の文字を渡すことが可能になります。
925         * ここでは、引数文字列に、ダブルクオート(")、シングルクオート(')が、
926         * 含まれると、それぞれ、ASCII コード(¥x22、¥x27)に置き換えます。
927         * なお、null は、ゼロ文字列に変換して返します。
928         *
929         * @param       input 入力文字列
930         *
931         * @return      クオート文字をASCII文字に置き換えた文字列
932         */
933        public static String quoteFilter( final String input ) {
934                if( input == null || input.length() == 0 ) { return ""; }
935                if( input.indexOf( '\'' ) < 0 && input.indexOf( '"' ) < 0 ) { return input; }
936
937                StringBuilder rtn = new StringBuilder();
938                char ch;
939                for(int i=0; i<input.length(); i++) {
940                        ch = input.charAt(i);
941                        switch( ch ) {
942                                case '"'  : rtn.append( "\\x22" ); break;
943                                case '\'' : rtn.append( "\\x27" ); break;
944                                default   : rtn.append( ch );
945                        }
946                }
947                return rtn.toString() ;
948        }
949
950        /**
951         * 所定のキャラクタコードを取り除いた文字列を作成します。
952         *
953         * 実現したい機能は、String#replace( 'x','' ) 的な表現です。
954         * つまり、指定のキャラクタを取り除きたいのですが、上記コマンドでは、
955         * コンパイル時にエラーが発生します。
956         * 取り除きたいキャラクタコードが存在しない場合は、指定の文字列を
957         * そのまま返します。
958         *
959         * @param       value 処理対象の文字列
960         * @param       ch 取り除きたいキャラクタ
961         *
962         * @return      処理後の文字列
963         */
964        public static String deleteChar( final String value,final char ch ) {
965                if( value == null || value.indexOf( ch ) < 0 ) { return value; }
966                char[] chs = value.toCharArray() ;
967                int j=0;
968                for( int i=0;i<chs.length; i++ ) {
969                        if( chs[i] == ch ) { continue; }
970                        chs[j] = chs[i];
971                        j++;
972                }
973                return String.valueOf( chs,0,j );
974        }
975
976        /**
977         * 文字列に含まれる、特定の文字の個数をカウントして返します。
978         *
979         * @og.rev 5.2.0.0 (2010/09/01)
980         *
981         * @param       value 処理対象の文字列
982         * @param       ch カウントする文字
983         *
984         * @return      カウント数
985         */
986        public static int countChar( final String value,final char ch ) {
987                if( value == null || value.indexOf( ch ) < 0 ) { return 0; }
988                char[] chs = value.toCharArray() ;
989                int cnt=0;
990                for( int i=0;i<chs.length; i++ ) {
991                        if( chs[i] == ch ) { cnt++; }
992                }
993                return cnt;
994        }
995
996        /**
997         * CODE39 の 文字列を作成します。
998         *
999         * CODE39 は、『0~9, A~Z,-,・, ,$,/,+,%』のコードが使用できる
1000         * バーコードの体系です。通常 * で始まり * で終了します。
1001         * また、チェックデジット に、モジュラス43 が使われます。
1002         * ここでは、指定の文字列の前後に、* を付与し、必要であれば
1003         * チェックデジットも付与します。
1004         * 指定の入力文字列には、* を付けないでください。
1005         *
1006         * @param       value 処理対象の文字列
1007         * @param       checkDigit チェックデジットの付与(true:付ける/false:付けない)
1008         *
1009         * @return      処理後の文字列
1010         */
1011        public static String code39( final String value,final boolean checkDigit ) {
1012                String rtn = ( value == null ) ? "" : value ;
1013                if( ! checkDigit ) { return "*" + rtn + "*"; }
1014
1015                int kei = 0;
1016                int cd;
1017                for( int i=0; i<rtn.length(); i++ ) {
1018                        cd = MODULUS_43.indexOf( rtn.charAt(i) );
1019                        if( cd < 0 ) {
1020                                String errMsg = "指定の文字中に、CODE39 規定外文字が使用されています。[" + rtn.charAt(i) + "]" ;
1021                                throw new RuntimeException( errMsg );
1022                        }
1023                        kei += cd ;
1024                }
1025                char digit = MODULUS_43.charAt( kei % 43 );
1026
1027                return "*" + rtn + digit + "*" ;
1028        }
1029
1030        /**
1031         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1032         * もちろん、in も def も null の場合は、null を返します。
1033         *
1034         * @param    in 基準となる文字列
1035         * @param    def デフォルト文字列
1036         *
1037         * @return   ( in != null ) ? in : def ;
1038         */
1039        public static String nval( final String in,final String def ) {
1040                return ( in == null || in.length() == 0 ) ? def : in ;
1041        }
1042
1043        /**
1044         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1045         *
1046         * @param    in 基準となる文字列
1047         * @param    def デフォルト数字
1048         *
1049         * @return   引数 in を変換した数字。変換できない場合は デフォルト値 def
1050         */
1051        public static int nval( final String in,final int def ) {
1052                return ( in == null || in.length() == 0 ) ? def : Integer.parseInt( in ) ;
1053        }
1054
1055        /**
1056         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1057         *
1058         * @param    in 基準となる文字列
1059         * @param    def デフォルト数字
1060         *
1061         * @return   引数 in を変換した数字。変換できない場合は デフォルト値 def
1062         */
1063        public static long nval( final String in,final long def ) {
1064                return ( in == null || in.length() == 0 ) ? def : Long.parseLong( in ) ;
1065        }
1066
1067        /**
1068         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1069         * 通常は、"true" または、 "TRUE" 文字列を、論理値の true に変換します。
1070         * ただし、文字列長が 1文字の場合のみ、"0" 以外を true に変換します。
1071         *
1072         * @param    in 基準となる文字列
1073         * @param    def デフォルト論理値
1074         *
1075         * @return   引数 in を変換した論理値。変換できない場合は デフォルト値 def
1076         */
1077        public static boolean nval( final String in,final boolean def ) {
1078                boolean rtn = def;
1079                if( in != null && in.length() != 0 ) {
1080                        rtn = "true".equalsIgnoreCase( in )  ;
1081                        if( in.length() == 1 ) { rtn = ! "0".equals( in ); }
1082                }
1083                return rtn ;
1084        }
1085
1086        /**
1087         * 引数 in が、null、"_"、ゼロ文字列の場合は、デフォルト値 def を返します。
1088         *
1089         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1090         *
1091         * @og.rev  5.2.2.0 (2010/11/01) "_" の取り扱い変更
1092         *
1093         * @param    in 基準となる文字列
1094         * @param    def デフォルト文字列
1095         *
1096         * @return  null、"_"、ゼロ文字列の場合は、デフォルト文字列を、そうでなければ、入力文字を返す。
1097         */
1098        public static String nval2( final String in,final String def ) {
1099                return ( in == null || in.length() == 0 || "_".equals( in ) ) ? def : in.intern() ;
1100        }
1101
1102        /**
1103         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1104         * ただし、NULL代替文字(_)は デフォルト値 def2 に置き換えます。
1105         *
1106         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1107         *
1108         * @og.rev  5.2.2.0 (2010/11/01) "_" の取り扱い変更
1109         *
1110         * @param    in 基準となる文字列
1111         * @param    def デフォルト文字列
1112         * @param    def2 NULL代替文字(_)の場合のデフォルト文字列
1113         *
1114         * @return  NULL文字列関係の場合は、ゼロ文字列を、そうでなければ、入力文字を返す。
1115         */
1116        public static String nval2( final String in,final String def,final String def2 ) {
1117                return ( in == null || in.length() == 0 ) ? def : ( "_".equals( in ) ? def2 : in.intern() ) ;
1118        }
1119
1120        /**
1121         * 引数 in が、null または、ゼロ文字列、またはすべて空白文字の場合は、true を返します。
1122         * それ以外は false を返します。
1123         *
1124         * 注意は、オールスペースやタブ文字、改行文字も true になります。
1125         *
1126         * @param    in 基準となる文字列
1127         *
1128         * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
1129         */
1130        public static boolean isNull( final String in ) {
1131                if( in == null || in.length() == 0 ) { return true; }
1132
1133                // String.trim().length()==0 の高速版
1134                for( int i=0; i<in.length(); i++ ) {
1135                        if( !Character.isWhitespace( in.charAt(i) ) ) {
1136                                return false;
1137                        }
1138                }
1139                return true;
1140        }
1141
1142        /**
1143         * Throwable の printStackTrace() 結果を文字列に変換して返します。
1144         *
1145         * @param    th   printStackTraceすべき元のThrowableオブジェクト
1146         *
1147         * @return   Throwableの詳細メッセージ( th.printStackTrace() )
1148         */
1149        public static String stringStackTrace( final Throwable th ) {
1150                if( th == null ) { return null; }
1151
1152                StringWriter sw = new StringWriter();
1153                th.printStackTrace( new PrintWriter( sw ) );
1154
1155                return String.valueOf( sw );
1156        }
1157
1158        /**
1159         * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。
1160         *
1161         * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" を
1162         * 含む箇所だけを、抜粋します。
1163         *
1164         * @og.rev 5.7.2.0 (2014/01/10) 新規作成
1165         *
1166         * @param    th 元のThrowableオブジェクト
1167         *
1168         * @return   Throwableの詳細メッセージ( StackTraceElement の抜粋 )
1169         */
1170        public static String ogStackTrace( final Throwable th ) {
1171                if( th == null ) { return null; }
1172
1173                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1174
1175                StackTraceElement[] eles = th.getStackTrace();
1176                if( eles.length > 0 ) {
1177                        rtn.append( "  " ).append( eles[0].toString() ).append( CR );
1178                }
1179
1180                for( int i=1; i<eles.length; i++ ) {
1181                        String cls = eles[i].getClassName();
1182                        if( cls.indexOf( "org.opengion" ) >= 0 ) {
1183                                rtn.append( "    at " ).append( eles[i].toString() ).append( CR );
1184                        }
1185                }
1186
1187                return rtn.toString();
1188        }
1189
1190        /**
1191         * 大きな浮動小数点数について、カンマ編集を行います。
1192         *
1193         * このメソッドでは、1.23 E12 などの数字は扱いません。通常の
1194         * 数字とピリオドで構成された文字列のみ、変換対象になります。
1195         * (ただし、不正な文字列を与えてもエラーチェックはしていません。)
1196         * minFraction には、少数点部に与える固定値を指定します。入力文字列が
1197         * その桁数より少ない場合は、0埋めします。
1198         * 多い場合でもカットしません。
1199         * minFraction が 0 の場合は、少数点は付きません。
1200         * ".12" などの少数点は、必ず先頭に 0 が付きます。
1201         * 入力文字列が null か、ゼロ文字列時は、そのまま入力データを返します。
1202         *
1203         * <pre>
1204         *      DecimalFormat format = new DecimalFormat( "#,##0.00########" );
1205         *      double dd = Double.parseDouble( val );
1206         *      return format.format( dd );
1207         * </pre>
1208         * に対して、minFraction分の少数以下のゼロの指定と、inに ',' が
1209         * 含まれた処理を追加した感じになります。
1210         *
1211         * @og.rev  4.0.0.0 (2007/10/26) 空白のトリム処理を追加
1212         *
1213         * @param       in              変換元の文字列
1214         * @param       minFraction     変換時の少数点以下の固定桁数
1215         *
1216         * @return      カンマ編集後の数字型文字列
1217         */
1218        public static String numberFormat( final String in, final int minFraction ) {
1219                if( in == null || in.length() == 0 ) { return in ; }
1220
1221                // 4.0.0.0 (2007/10/26)
1222                String tmp = in.trim();
1223
1224                if( tmp.length() == 0 ) { return tmp ; }
1225
1226                char[] chs = tmp.toCharArray();
1227                int pos = 0;
1228
1229                // 整数部の設定
1230                boolean firstZero = true;
1231                StringBuilder buf1 = new StringBuilder();
1232                while( pos < chs.length ) {
1233                        char ch = chs[pos++];
1234                        if( ch == '.' ) { break; }
1235                        else if( ch != '-' && ch != ',' && ( ch != '0' || !firstZero )) {
1236                                buf1.append( ch );
1237                                firstZero = false;
1238                        }
1239                }
1240                if( buf1.length() == 0 ) {
1241                        buf1.append( '0' );
1242                }
1243
1244                for( int i=buf1.length()-3; i>0; i-=3 ) {
1245                        buf1.insert( i,',' );
1246                }
1247                if( chs[0] == '-' ) { buf1.insert( 0,'-' ); }
1248
1249                // 少数部の設定
1250                // 3.6.0.3 (2004/10/05) 桁数が多い場合でもカットしない
1251                StringBuilder buf2 = new StringBuilder();
1252                while( pos < chs.length ) {
1253                        buf2.append( chs[pos++] );
1254                }
1255
1256                while( buf2.length() < minFraction ) {
1257                        buf2.append( '0' );
1258                }
1259
1260                if( buf2.length() > 0 ) {
1261                        buf1.append( '.' ).append( buf2 );
1262                }
1263
1264                return buf1.toString();
1265        }
1266
1267        /**
1268         * 識別id に応じた オブジェクトを作成します。
1269         * 作成するには、デフォルトコンストラクターが必要です。
1270         *
1271         * @param       cls 作成するクラスのフルネーム
1272         *
1273         * @return      オブジェクト
1274         * @throws RuntimeException 何らかのエラーが発生した場合
1275         */
1276        public static Object newInstance( final String cls ) {
1277                return newInstance( cls,Thread.currentThread().getContextClassLoader() );
1278        }
1279
1280        /**
1281         * 指定されたクラスローダを使って、識別id に応じた オブジェクトを作成します。
1282         * 作成するには、デフォルトコンストラクターが必要です。
1283         * initialize パラメータは true 相当(それまでに初期化されていない場合だけ初期化)です。
1284         *
1285         * @param       cls             作成するクラスのフルネーム
1286         * @param       loader  作成するクラスのクラスローダ
1287         *
1288         * @return      オブジェクト
1289         * @throws RuntimeException 何らかのエラーが発生した場合
1290         */
1291        public static Object newInstance( final String cls,final ClassLoader loader ) {
1292                try {
1293                        return Class.forName( cls,true,loader ).newInstance();
1294                }
1295                catch( ClassNotFoundException ex1 ) {
1296                        String errMsg = "クラスが見つかりません。class=[" + cls + "]" + CR
1297                                                + ex1.getMessage() ;
1298                        throw new RuntimeException( errMsg,ex1 );
1299                }
1300                catch( LinkageError ex2 ) {
1301                        String errMsg = "リンケージが失敗しました。class=[" + cls + "]" + CR
1302                                                + ex2.getMessage();
1303                        throw new RuntimeException( errMsg,ex2 );
1304                }
1305                catch( InstantiationException ex3 ) {
1306                        String errMsg = "インスタンスの生成が失敗しました。class=[" + cls + "]" + CR
1307                                                + ex3.getMessage() ;
1308                        throw new RuntimeException( errMsg,ex3 );
1309                }
1310                catch( IllegalAccessException ex4 ) {
1311                        String errMsg = "クラスまたは初期化子にアクセスできません。class=[" + cls + "]" + CR
1312                                                + ex4.getMessage();
1313                        throw new RuntimeException( errMsg,ex4 );
1314                }
1315                catch( RuntimeException ex5 ) {         // 3.6.0.0 (2004/09/17)
1316                        String errMsg = "予期せぬエラー class=[" + cls + "]" + CR
1317                                                + ex5.getMessage() ;
1318                        throw new RuntimeException( errMsg,ex5 );
1319                }
1320        }
1321
1322        /**
1323         * 指定のURL文字列同士を連結させます。
1324         * そのとき、後方URLが、絶対パスの場合は、連結せず 後方URLを返します。
1325         * 第2引数以降は、絶対パス判定をせず直前のURLの末尾判定のみで連結します。
1326         *
1327         * 絶対パスかどうかは、通常のファイル属性と同様に、先頭が、'/' (UNIX)または、
1328         * 2文字目が、":" (Windows)の場合、または、先頭が "\" (ネットワークパス)で
1329         * 始まる場合で判断します。
1330         * 連結時に、前方URLの末尾に "/" を付加します。
1331         *
1332         * 処理の互換性確保のため、第3引数の可変長引数を追加しています。
1333         *
1334         * @og.rev  5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
1335         * @og.rev  5.6.5.2 (2013/06/21) 第3引数を可変長引数に変更
1336         *
1337         * @param       url1 先頭URL文字列
1338         * @param       url2 後方URL文字列(絶対パスの場合は、返り値)
1339         * @param       urls 後方URL文字列
1340         *
1341         * @return      URL文字列同士の連結結果 url1 + url2(url2が絶対パスの場合は、url2から連結開始)
1342         */
1343        public static String urlAppend( final String url1,final String url2,final String... urls ) {
1344                StringBuilder rtnUrl = new StringBuilder( 200 );
1345
1346                if(        url2 == null || url2.length() == 0 ) { rtnUrl.append( url1 ) ; }
1347                else if( ( url1 == null || url1.length() == 0 ) ||
1348                                 ( url2.charAt(0) == '/'  ) ||                                                  // 実ディレクトリが UNIX
1349                                 ( url2.length() > 1 && url2.charAt(1) == ':' ) ||              // 実ディレクトリが Windows
1350                                 ( url2.charAt(0) == '\\' )     ) {                                                     // 実ディレクトリが ネットワークパス
1351                                        rtnUrl.append( url2 ) ;
1352                }
1353                else {
1354                        char ch = url1.charAt( url1.length()-1 ) ;
1355                        if( ch == '/' || ch == '\\' ) {
1356                                rtnUrl.append( url1 ).append( url2 ) ;
1357                        }
1358                        else {
1359                                rtnUrl.append( url1 ).append( "/" ).append( url2 ) ;
1360                        }
1361                }
1362
1363                // ここからが、追加分
1364                for( String url : urls ) {
1365                        if( url != null && url.length() > 0 ) {
1366                                char ch = rtnUrl.charAt( rtnUrl.length()-1 ) ;
1367                                if( ch == '/' || ch == '\\' ) {
1368                                        rtnUrl.append( url ) ;
1369                                }
1370                                else {
1371                                        rtnUrl.append( "/" ).append( url ) ;
1372                                }
1373                        }
1374                }
1375
1376                return rtnUrl.toString() ;
1377        }
1378
1379        /**
1380         * Unicode文字列の値を HTML のエスケープ記号(&amp;#xZZZZ;)に変換します。
1381         *
1382         * SJIS(JA16SJIS) で作成されたデータベースに、(NVARCHAR2)を使用して中国語等を登録するのは
1383         * 非常に複雑でかつ、リスクが大きい処理になります。
1384         * ORACLE殿でも、自信を持っては勧められない機能とのコメントを頂いています。
1385         * そこで、HTMLでのエスケープ文字を使用して、Unicodeを文字列化して登録する為の
1386         * DBType として、新規に作成します。
1387         * ここでは、入力文字を、キャラクタ(char)型に分解し、(&amp;#xZZZZ;)に変換していきます。
1388         * よって、通常に1文字(Shift-JISで2Byte,UTF-8で3Byte)が、8Byteになります。
1389         * この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為、
1390         * 通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。
1391         * ここでは、2バイト文字のみ、変換しています。
1392         *
1393         * @param       value 変換前の文字列
1394         *
1395         * @return      HTMLのエスケープ記号(&amp;#xZZZZ;)
1396         */
1397        public static String getUnicodeEscape( final String value ) {
1398                if( value == null || value.length() == 0 ) { return ""; }
1399
1400                StringBuilder rtn = new StringBuilder( value.length() * 4 );
1401
1402                for( int i=0; i<value.length(); i++ ) {
1403                        char ch = value.charAt(i);
1404
1405                        if( ch > 0xff ) {
1406                                String hex = Integer.toHexString( (int)ch ) ;
1407                                rtn.append( UTF_STR[hex.length()] ).append( hex ).append( ";" );
1408                        }
1409                        else {
1410                                rtn.append( ch );
1411                        }
1412                }
1413
1414                return rtn.toString();
1415        }
1416
1417        /**
1418         * HTML のエスケープ記号(&amp;#xZZZZ;)をUnicode文字列に戻します。
1419         *
1420         * HTMLでのエスケープ文字を使用して登録された文字を、Unicodeに戻します。
1421         * (&amp;#xZZZZ;)の8Byteを、もとのキャラクタコードに戻し、合成します。
1422         * ここでは、通常の文字列に混在したエスケープ文字も戻せるようにします。
1423         *
1424         * @param       value   HTMLのエスケープ記号(&amp;#xZZZZ;)を含む文字列
1425         *
1426         * @return      通常のUnicode文字列
1427         */
1428        public static String getReplaceEscape( final String value ) {
1429                if( value == null || value.length() == 0 ) { return ""; }
1430
1431                StringBuilder rtn = new StringBuilder( value );
1432
1433                int st = rtn.indexOf( "&#" );
1434                while( st >= 0 ) {
1435                        if( st+7 < rtn.length() && rtn.charAt( st+7 ) == ';' ) {
1436                                int ch = Integer.parseInt( rtn.substring( st+3,st+7 ),16 );
1437                                rtn.replace( st,st+8, Character.toString( (char)ch ) );
1438                        }
1439                        st = rtn.indexOf( "&#",st );
1440                }
1441
1442                return rtn.toString();
1443        }
1444
1445        /**
1446         * 文字列をdoubleに変換します。
1447         *
1448         * これは、Double.parseDouble( value ) と、ほぼ同じ動作を行います。
1449         * 内部的には、引数の カンマ(,) を削除した文字列を、Double.parseDouble( value )
1450         * に渡します。
1451         * また、引数が、null,ゼロ文字列,'_' の時には、0.0 を返します。
1452         *
1453         * @param       value   doubleに変換する元の文字列
1454         *
1455         * @return      変換後のdouble数値
1456         */
1457        public static double parseDouble( final String value ) {
1458                double rtn ;
1459
1460                if( value == null || value.length() == 0 || value.equals( "_" ) ) {
1461                        rtn = 0.0d;
1462                }
1463                else if( value.indexOf( ',' ) < 0 ) {
1464                        rtn = Double.parseDouble( value );
1465                }
1466                else {
1467                        char[] chs = value.toCharArray() ;
1468                        int j=0;
1469                        for( int i=0;i<chs.length; i++ ) {
1470                                if( chs[i] == ',' ) { continue; }
1471                                chs[j] = chs[i];
1472                                j++;
1473                        }
1474                        rtn = Double.parseDouble( String.valueOf( chs,0,j ) );
1475                }
1476
1477                return rtn ;
1478        }
1479
1480        /**
1481         * カラーキーワードより、Colorオブジェクトを作成します。
1482         *
1483         * 指定文字列は、java.awt.Color クラスのstatic フィールド名で指定します。
1484         * BLACK , BLUE , CYAN , DARK_GRAY , GRAY , GREEN , LIGHT_GRAY ,
1485         * MAGENTA , ORANGE , PINK , RED , WHITE , YELLOW , PURPLE , TRANSPARENT(透明) が指定できます。
1486         * また、先頭に、# を付ける事で、#XXXXXX形式の16bitRGB表記 でも指定可能です。
1487         * static フィールド名のMapを管理していますが、存在しない場合は、エラーになります。
1488         *
1489         * @og.rev 3.8.9.1 (2007/06/29) 新規作成
1490         * @og.rev 4.1.1.0 (2008/02/04) CLR_MAP に存在しない場合はエラーにします。
1491         *
1492         * @param       value java.awt.Color フィールドを示す文字列または、#XXXXXX形式の16bitRGB表記
1493         *
1494         * @return      Colorオブジェクト
1495         * @see         java.awt.Color#BLACK
1496         */
1497        public static Color getColorInstance( final String value ) {
1498                final Color clr ;
1499
1500                if( value.startsWith("#") ) {
1501                        int code = Integer.parseInt( value.substring(1),16 );
1502                        clr = new Color( code );
1503                }
1504                else {
1505                        clr = CLR_MAP.get( value );
1506                        if( clr == null ) {
1507                                String errMsg = "指定の色コードは使用できません Color=[" + value + "]" + CR
1508                                                        + "ColorMap=" + CLR_MAP.keySet().toString();
1509                                throw new RuntimeException( errMsg );
1510                        }
1511                }
1512
1513                return clr;
1514        }
1515
1516        /**
1517         * 引数からspanタグを取り除いて返します。
1518         *
1519         * 引数が、&lt;span ・・・&gt;XXXX&lt;/span&gt;形式の場合、XXXX のみ出力します。
1520         *
1521         * @og.rev 4.3.4.3 (2008/12/22) TableWriterで利用していたものを移動
1522         *
1523         * @param        data 元のString文字列
1524         *
1525         * @return       spanタグが取り除かれた文字列
1526         */
1527        public static String spanCut( final String data ) {
1528                String rtn = data;
1529                if( data != null && data.startsWith( "<span" ) ) {
1530                        int st = data.indexOf( '>' );
1531                        int ed = data.indexOf( "</span>",st );
1532                        rtn = data.substring( st+1,ed );
1533                }
1534                return rtn ;
1535        }
1536
1537        /**
1538         * 簡易CSS形式のフォーマットを、Mapにセットします。
1539         *
1540         * 簡易CSS形式とは、セレクタのない、{ プロパティ1 : 値1 ; ・・・ } 形式とします。
1541         * これを、プロパティ1 と 値1 のMap にセットする処理を行います。
1542         * コメントは、削除されます。また、同一プロパティが記述されている場合は、後処理を採用します。
1543         *
1544         * なお、入力テキストが、null か、{…} が存在しない場合は、null を返します。
1545         *
1546         * @og.rev 5.6.5.2 (2013/06/21) 新規追加
1547         *
1548         * @param        cssText 簡易CSS形式のフォーマット文字列
1549         *
1550         * @return       パース結果のMap
1551         */
1552        public static Map<String,String> cssParse( final String cssText ) {
1553                Map<String,String> map = null;
1554
1555                if( cssText != null ) {
1556                        // まずコメントを削除します。
1557                        StringBuilder buf = new StringBuilder( cssText );
1558
1559                        int ad1 = buf.indexOf( "/*" );
1560                        while( ad1 >= 0 ) {
1561                                int ad2 = buf.indexOf( "*/" , ad1 );
1562                                if( ad2 < 0 ) { buf = buf.delete( ad1,buf.length() ); break; }          // 閉じてなければ以降を全削除
1563                                buf = buf.delete( ad1,ad2+2 );
1564                                ad1 = buf.indexOf( "/*" );              // コメントは削除されたので、初めから検索する。
1565                        }
1566
1567                        // 処理対象は、{ ~ } の間の文字列。
1568                        ad1 = buf.indexOf( "{" );
1569                        int ad2 = buf.indexOf( "}",ad1 );
1570                        if( ad1 >= 0 && ad2 > 0 ) {
1571                                String tempText = buf.substring( ad1+1,ad2 );           // これが処理対象の文字列
1572
1573                                String[] recode = tempText.split( ";" );                        // KEY1 : VAL1; の ; で分割する。
1574
1575                                for( int i=0; i<recode.length; i++ ) {
1576                                        int ad = recode[i].indexOf( ':' );
1577                                        if( ad > 0 ) {
1578                                                String key = recode[i].substring( 0,ad ).trim();
1579                                                String val = recode[i].substring( ad+1 ).trim();
1580                                                if( key.isEmpty() || val.isEmpty() ) { continue; }
1581
1582                                                if( map == null ) { map = new HashMap<String,String>(); }       // 対象データがある時だけMapを作りたかったので。
1583                                                map.put( key,val );
1584                                        }
1585                                }
1586                        }
1587                }
1588                return map ;
1589        }
1590        
1591        /**
1592         * 引数から空白文字を削除して返します。
1593         *
1594         *
1595         * @og.rev 5.6.9.4 (2013/10/31) TableWriterで利用していたものを移動
1596         *
1597         * @param        data 元のString文字列
1598         *
1599         * @return       空白文字が取り除かれた文字列
1600         */
1601        public static String deleteWhitespace( final String data ) {
1602                if( data == null || data.length() == 0 ){
1603                        return data;
1604                }
1605                return data.replaceAll( "\\s", "" ) ;
1606        }
1607}