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         * 重複削除、昇順並べ替え等が、必要な場合は、取得後の配列を操作してください。
830         *
831         * @og.rev 5.5.7.2 (2012/10/09) 新規追加
832         * @og.rev 5.9.12.4 (2016/09/30) アルファベットの対応を廃止し、数字配列のみサポートします。
833         *
834         * @param       csvData 0,1,3,5-8,10-* などのCSV-ハイフン文字列
835         * @param       maxNo "*" が指定された場合の、最大数
836         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
837         */
838        public static String[] csv2ArrayExt( final String csvData , final int maxNo )  {
839                if( csvData == null || csvData.length() == 0 ) { return new String[0] ; }
840
841                String strData = csvData.replace( "-*" , "-" + maxNo );         // まず、N-* 形式を、N-maxNo に変換します。
842                strData        = strData.replace( "*"  , "0-" + maxNo );        // その後、"*" 単独(1文字)を、0-maxNo に変換します。
843
844                ArrayList<String> noList = new ArrayList<String>();
845
846                String[] nos = strData.split( "," );            // カンマで分解。N , N-M , N-* のどれか
847                for( int i=0; i<nos.length; i++ ) {
848                        String sno = nos[i] ;
849                        int hai = sno.indexOf( '-' );
850                        // ハイフンが含まれているときは前後に分解して、間を埋める
851                        if( hai > 0 ) {
852                                String st1 = sno.substring( 0,hai );    // 先頭からハイフンまで
853                                String st2 = sno.substring( hai+1 );    // ハイフンから最後まで
854                                // 5.9.12.4 (2016/09/30) アルファベット対応をやめる
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         * Integer限定版です。
877         * V6では返り値の型変更をしていますが、V5では一旦別名にしておきます。
878         *
879         * @og.rev 5.9.0.0 (2015/09/04) 新規追加
880         * @og.rev 5.9.12.4 (2016/09/30) アルファベットの対応を廃止し、数字配列のみサポートします。
881         *
882         * @param       csvData 0,1,3,5-8,10-* などのCSV-ハイフン文字列
883         * @param       maxNo "*" が指定された場合の、最大数
884         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
885         */
886        public static Integer[] csv2ArrayExt2( final String csvData , final int maxNo )  {
887                if( csvData == null || csvData.length() == 0 ) { return new Integer[0] ; }
888
889                String strData = csvData.replace( "-*" , "-" + maxNo );         // まず、N-* 形式を、N-maxNo に変換します。
890                strData        = strData.replace( "*"  , "0-" + maxNo );        // その後、"*" 単独(1文字)を、0-maxNo に変換します。
891
892                ArrayList<Integer> noList = new ArrayList<Integer>();
893
894                String[] nos = strData.split( "," );            // カンマで分解。N , N-M , N-* のどれか
895                for( int i=0; i<nos.length; i++ ) {
896                        String sno = nos[i] ;
897                        int hai = sno.indexOf( '-' );
898                        // ハイフンが含まれているときは前後に分解して、間を埋める
899                        if( hai > 0 ) {
900                                String st1 = sno.substring( 0,hai );    // 先頭からハイフンまで
901                                String st2 = sno.substring( hai+1 );    // ハイフンから最後まで
902                                // 5.9.12.4 (2016/09/30) アルファベット対応をやめる
903//                              if( st1.length() == 1 &&  st2.length() == 1 ) {         // ともに1文字の場合は、char化して処理。(英数字処理)
904//                                      char ch1 = st1.charAt(0);
905//                                      char ch2 = st2.charAt(0);
906//                                      if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( Integer.valueOf(ch1++) ); } }
907//                                      else                    { while( ch1 >= ch2 ) { noList.add( Integer.valueOf(ch1--) ); } }
908//                              }
909//                              else {
910                                        int ch1 = Integer.parseInt( st1 );
911                                        int ch2 = Integer.parseInt( st2 );
912                                        if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( Integer.valueOf(ch1++) ); } }
913                                        else                    { while( ch1 >= ch2 ) { noList.add( Integer.valueOf(ch1--) ); } }
914//                              }
915                        }
916                        else {
917                                noList.add( Integer.valueOf(sno) );
918                        }
919                }
920                return noList.toArray( new Integer[noList.size()] ) ;
921        }
922
923        /**
924         * Object 引数の文字列表現を返します。
925         * これは,String.valueOf とほぼ同じ動作をしますが、引数が null の場合に、
926         * "null" という文字列を返すのではなく、なにもない文字列 "" を返します。
927         *
928         * @param       obj    文字列表現すべき元のオブジェクト
929         *
930         * @return      引数が null の場合は、"" に等しい文字列。そうでない場合は、obj.toString() の値
931         */
932        public static String valueOf( final Object obj ) {
933                if( obj == null ) { return "";                     }
934                else                      { return obj.toString(); }
935        }
936
937        /**
938         * HTML上のエスケープ文字を変換します。
939         *
940         * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
941         * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
942         * セキュリティーホールになる可能性があるので、注意してください。
943         *
944         * @param       input HTMLエスケープ前の文字列
945         *
946         * @return      エスケープ文字に変換後の文字列
947         */
948        public static String htmlFilter( final String input ) {
949                if( input == null || input.length() == 0 ) { return ""; }
950                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
951                char ch;
952                for(int i=0; i<input.length(); i++) {
953                        ch = input.charAt(i);
954                        switch( ch ) {
955                                case '<'  : rtn.append("&lt;");   break;
956                                case '>'  : rtn.append("&gt;");   break;
957                                case '"'  : rtn.append("&quot;"); break;
958                                case '\'' : rtn.append("&#39;");  break;
959                                case '&'  : rtn.append("&amp;");  break;
960                                default   : rtn.append(ch);
961                        }
962                }
963                return rtn.toString() ;
964        }
965        
966        /**
967         * XML上のエスケープ文字を変換します。
968         * 
969         * HTMLとの違いはアポストロフィです。
970         *
971         * @og.rev 5.8.2.2 (2014/12/19) 新規作成
972         *
973         * @param       input XMLエスケープ前の文字列
974         *
975         * @return      エスケープ文字に変換後の文字列
976         */
977        public static String xmlFilter( final String input ) {
978                if( input == null || input.length() == 0 ) { return ""; }
979                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
980                char ch;
981                for(int i=0; i<input.length(); i++) {
982                        ch = input.charAt(i);
983                        switch( ch ) {
984                                case '<'  : rtn.append("&lt;");   break;
985                                case '>'  : rtn.append("&gt;");   break;
986                                case '"'  : rtn.append("&quot;"); break;
987                                case '\'' : rtn.append("&apos;");  break;
988                                case '&'  : rtn.append("&amp;");  break;
989                                default   : rtn.append(ch);
990                        }
991                }
992                return rtn.toString() ;
993        }
994
995        /**
996         * JavaScript 等の引数でのクオート文字をASCII変換します。
997         *
998         * JavaScript の引数の値に、ダブルクオート(")、シングルクオート(')が
999         * 含まれると、文字列を表す為に前後に指定しているクオートと混乱し、
1000         * データを表現できないケースがあります。その場合には、クオート文字を
1001         * ASCII文字に置き換える事で、指定の文字を渡すことが可能になります。
1002         * ここでは、引数文字列に、ダブルクオート(")、シングルクオート(')が、
1003         * 含まれると、それぞれ、ASCII コード(¥x22、¥x27)に置き換えます。
1004         * なお、null は、ゼロ文字列に変換して返します。
1005         *
1006         * @param       input 入力文字列
1007         *
1008         * @return      クオート文字をASCII文字に置き換えた文字列
1009         */
1010        public static String quoteFilter( final String input ) {
1011                if( input == null || input.length() == 0 ) { return ""; }
1012                if( input.indexOf( '\'' ) < 0 && input.indexOf( '"' ) < 0 ) { return input; }
1013
1014                StringBuilder rtn = new StringBuilder();
1015                char ch;
1016                for(int i=0; i<input.length(); i++) {
1017                        ch = input.charAt(i);
1018                        switch( ch ) {
1019                                case '"'  : rtn.append( "\\x22" ); break;
1020                                case '\'' : rtn.append( "\\x27" ); break;
1021                                default   : rtn.append( ch );
1022                        }
1023                }
1024                return rtn.toString() ;
1025        }
1026        
1027        /**
1028         * JSON形式で出力する場合のためのエスケープ処理です。
1029         * 
1030         *
1031         * @og.rev 5.9.6.4(2016/03/25) 新規作成
1032         *
1033         * @param       input XMLエスケープ前の文字列
1034         *
1035         * @return      エスケープ文字に変換後の文字列
1036         */
1037        public static String jsonFilter( final String input ) {
1038                if( input == null || input.length() == 0 ) { return ""; }
1039                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1040                char ch;
1041                for(int i=0; i<input.length(); i++) {
1042                        ch = input.charAt(i);
1043                        switch( ch ) {
1044                                case '"'  : rtn.append("\\\"");   break;
1045                                case '\\'  : rtn.append("\\\\");   break;
1046                                case '/'  : rtn.append("\\/"); break;
1047                                case '\b' : rtn.append("\\b");  break;
1048                                case '\f'  : rtn.append("\\f");  break;
1049                                case '\n'  : rtn.append("\\n"); break;
1050                                case '\r' : rtn.append("\\r");  break;
1051                                case '\t'  : rtn.append("\\t");  break;
1052                                default   : rtn.append(ch);
1053                        }
1054                }
1055                return rtn.toString() ;
1056        }
1057
1058        /**
1059         * 所定のキャラクタコードを取り除いた文字列を作成します。
1060         *
1061         * 実現したい機能は、String#replace( 'x','' ) 的な表現です。
1062         * つまり、指定のキャラクタを取り除きたいのですが、上記コマンドでは、
1063         * コンパイル時にエラーが発生します。
1064         * 取り除きたいキャラクタコードが存在しない場合は、指定の文字列を
1065         * そのまま返します。
1066         *
1067         * @param       value 処理対象の文字列
1068         * @param       ch 取り除きたいキャラクタ
1069         *
1070         * @return      処理後の文字列
1071         */
1072        public static String deleteChar( final String value,final char ch ) {
1073                if( value == null || value.indexOf( ch ) < 0 ) { return value; }
1074                char[] chs = value.toCharArray() ;
1075                int j=0;
1076                for( int i=0;i<chs.length; i++ ) {
1077                        if( chs[i] == ch ) { continue; }
1078                        chs[j] = chs[i];
1079                        j++;
1080                }
1081                return String.valueOf( chs,0,j );
1082        }
1083
1084        /**
1085         * 文字列に含まれる、特定の文字の個数をカウントして返します。
1086         *
1087         * @og.rev 5.2.0.0 (2010/09/01)
1088         *
1089         * @param       value 処理対象の文字列
1090         * @param       ch カウントする文字
1091         *
1092         * @return      カウント数
1093         */
1094        public static int countChar( final String value,final char ch ) {
1095                if( value == null || value.indexOf( ch ) < 0 ) { return 0; }
1096                char[] chs = value.toCharArray() ;
1097                int cnt=0;
1098                for( int i=0;i<chs.length; i++ ) {
1099                        if( chs[i] == ch ) { cnt++; }
1100                }
1101                return cnt;
1102        }
1103
1104        /**
1105         * CODE39 の 文字列を作成します。
1106         *
1107         * CODE39 は、『0〜9, A〜Z,-,・, ,$,/,+,%』のコードが使用できる
1108         * バーコードの体系です。通常 * で始まり * で終了します。
1109         * また、チェックデジット に、モジュラス43 が使われます。
1110         * ここでは、指定の文字列の前後に、* を付与し、必要であれば
1111         * チェックデジットも付与します。
1112         * 指定の入力文字列には、* を付けないでください。
1113         *
1114         * @param       value 処理対象の文字列
1115         * @param       checkDigit チェックデジットの付与(true:付ける/false:付けない)
1116         *
1117         * @return      処理後の文字列
1118         */
1119        public static String code39( final String value,final boolean checkDigit ) {
1120                String rtn = ( value == null ) ? "" : value ;
1121                if( ! checkDigit ) { return "*" + rtn + "*"; }
1122
1123                int kei = 0;
1124                int cd;
1125                for( int i=0; i<rtn.length(); i++ ) {
1126                        cd = MODULUS_43.indexOf( rtn.charAt(i) );
1127                        if( cd < 0 ) {
1128                                String errMsg = "指定の文字中に、CODE39 規定外文字が使用されています。[" + rtn.charAt(i) + "]" ;
1129                                throw new RuntimeException( errMsg );
1130                        }
1131                        kei += cd ;
1132                }
1133                char digit = MODULUS_43.charAt( kei % 43 );
1134
1135                return "*" + rtn + digit + "*" ;
1136        }
1137
1138        /**
1139         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1140         * もちろん、in も def も null の場合は、null を返します。
1141         *
1142         * @param    in 基準となる文字列
1143         * @param    def デフォルト文字列
1144         *
1145         * @return   ( in != null ) ? in : def ;
1146         */
1147        public static String nval( final String in,final String def ) {
1148                return ( in == null || in.length() == 0 ) ? def : in ;
1149        }
1150
1151        /**
1152         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1153         *
1154         * @param    in 基準となる文字列
1155         * @param    def デフォルト数字
1156         *
1157         * @return   引数 in を変換した数字。変換できない場合は デフォルト値 def
1158         */
1159        public static int nval( final String in,final int def ) {
1160                return ( in == null || in.length() == 0 ) ? def : Integer.parseInt( in ) ;
1161        }
1162
1163        /**
1164         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1165         *
1166         * @param    in 基準となる文字列
1167         * @param    def デフォルト数字
1168         *
1169         * @return   引数 in を変換した数字。変換できない場合は デフォルト値 def
1170         */
1171        public static long nval( final String in,final long def ) {
1172                return ( in == null || in.length() == 0 ) ? def : Long.parseLong( in ) ;
1173        }
1174
1175        /**
1176         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1177         * 通常は、"true" または、 "TRUE" 文字列を、論理値の true に変換します。
1178         * ただし、文字列長が 1文字の場合のみ、"0" 以外を true に変換します。
1179         *
1180         * @param    in 基準となる文字列
1181         * @param    def デフォルト論理値
1182         *
1183         * @return   引数 in を変換した論理値。変換できない場合は デフォルト値 def
1184         */
1185        public static boolean nval( final String in,final boolean def ) {
1186                boolean rtn = def;
1187                if( in != null && in.length() != 0 ) {
1188                        rtn = "true".equalsIgnoreCase( in )  ;
1189                        if( in.length() == 1 ) { rtn = ! "0".equals( in ); }
1190                }
1191                return rtn ;
1192        }
1193
1194        /**
1195         * 引数 in が、null、"_"、ゼロ文字列の場合は、デフォルト値 def を返します。
1196         *
1197         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1198         *
1199         * @og.rev  5.2.2.0 (2010/11/01) "_" の取り扱い変更
1200         *
1201         * @param    in 基準となる文字列
1202         * @param    def デフォルト文字列
1203         *
1204         * @return  null、"_"、ゼロ文字列の場合は、デフォルト文字列を、そうでなければ、入力文字を返す。
1205         */
1206        public static String nval2( final String in,final String def ) {
1207                return ( in == null || in.length() == 0 || "_".equals( in ) ) ? def : in.intern() ;
1208        }
1209
1210        /**
1211         * 引数 in が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1212         * ただし、NULL代替文字(_)は デフォルト値 def2 に置き換えます。
1213         *
1214         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1215         *
1216         * @og.rev  5.2.2.0 (2010/11/01) "_" の取り扱い変更
1217         *
1218         * @param    in 基準となる文字列
1219         * @param    def デフォルト文字列
1220         * @param    def2 NULL代替文字(_)の場合のデフォルト文字列
1221         *
1222         * @return  NULL文字列関係の場合は、ゼロ文字列を、そうでなければ、入力文字を返す。
1223         */
1224        public static String nval2( final String in,final String def,final String def2 ) {
1225                return ( in == null || in.length() == 0 ) ? def : ( "_".equals( in ) ? def2 : in.intern() ) ;
1226        }
1227
1228        /**
1229         * 引数 in が、null または、ゼロ文字列、またはすべて空白文字の場合は、true を返します。
1230         * それ以外は false を返します。
1231         *
1232         * 注意は、オールスペースやタブ文字、改行文字も true になります。
1233         *
1234         * @param    in 基準となる文字列
1235         *
1236         * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
1237         */
1238        public static boolean isNull( final String in ) {
1239                if( in == null || in.length() == 0 ) { return true; }
1240
1241                // String.trim().length()==0 の高速版
1242                for( int i=0; i<in.length(); i++ ) {
1243                        if( !Character.isWhitespace( in.charAt(i) ) ) {
1244                                return false;
1245                        }
1246                }
1247                return true;
1248        }
1249
1250        /**
1251         * Throwable の printStackTrace() 結果を文字列に変換して返します。
1252         *
1253         * @param    th   printStackTraceすべき元のThrowableオブジェクト
1254         *
1255         * @return   Throwableの詳細メッセージ( th.printStackTrace() )
1256         */
1257        public static String stringStackTrace( final Throwable th ) {
1258                if( th == null ) { return null; }
1259
1260                StringWriter sw = new StringWriter();
1261                th.printStackTrace( new PrintWriter( sw ) );
1262
1263                return String.valueOf( sw );
1264        }
1265
1266        /**
1267         * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。
1268         *
1269         * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" を
1270         * 含む箇所だけを、抜粋します。
1271         *
1272         * @og.rev 5.7.2.0 (2014/01/10) 新規作成
1273         *
1274         * @param    th 元のThrowableオブジェクト
1275         *
1276         * @return   Throwableの詳細メッセージ( StackTraceElement の抜粋 )
1277         */
1278        public static String ogStackTrace( final Throwable th ) {
1279                if( th == null ) { return null; }
1280
1281                StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1282
1283                StackTraceElement[] eles = th.getStackTrace();
1284                if( eles.length > 0 ) {
1285                        rtn.append( "  " ).append( eles[0].toString() ).append( CR );
1286                }
1287
1288                for( int i=1; i<eles.length; i++ ) {
1289                        String cls = eles[i].getClassName();
1290                        if( cls.indexOf( "org.opengion" ) >= 0 ) {
1291                                rtn.append( "    at " ).append( eles[i].toString() ).append( CR );
1292                        }
1293                }
1294
1295                return rtn.toString();
1296        }
1297
1298        /**
1299         * 大きな浮動小数点数について、カンマ編集を行います。
1300         *
1301         * このメソッドでは、1.23 E12 などの数字は扱いません。通常の
1302         * 数字とピリオドで構成された文字列のみ、変換対象になります。
1303         * (ただし、不正な文字列を与えてもエラーチェックはしていません。)
1304         * minFraction には、少数点部に与える固定値を指定します。入力文字列が
1305         * その桁数より少ない場合は、0埋めします。
1306         * 多い場合でもカットしません。
1307         * minFraction が 0 の場合は、少数点は付きません。
1308         * ".12" などの少数点は、必ず先頭に 0 が付きます。
1309         * 入力文字列が null か、ゼロ文字列時は、そのまま入力データを返します。
1310         *
1311         * <pre>
1312         *      DecimalFormat format = new DecimalFormat( "#,##0.00########" );
1313         *      double dd = Double.parseDouble( val );
1314         *      return format.format( dd );
1315         * </pre>
1316         * に対して、minFraction分の少数以下のゼロの指定と、inに ',' が
1317         * 含まれた処理を追加した感じになります。
1318         *
1319         * @og.rev  4.0.0.0 (2007/10/26) 空白のトリム処理を追加
1320         *
1321         * @param       in              変換元の文字列
1322         * @param       minFraction     変換時の少数点以下の固定桁数
1323         *
1324         * @return      カンマ編集後の数字型文字列
1325         */
1326        public static String numberFormat( final String in, final int minFraction ) {
1327                if( in == null || in.length() == 0 ) { return in ; }
1328
1329                // 4.0.0.0 (2007/10/26)
1330                String tmp = in.trim();
1331
1332                if( tmp.length() == 0 ) { return tmp ; }
1333
1334                char[] chs = tmp.toCharArray();
1335                int pos = 0;
1336
1337                // 整数部の設定
1338                boolean firstZero = true;
1339                StringBuilder buf1 = new StringBuilder();
1340                while( pos < chs.length ) {
1341                        char ch = chs[pos++];
1342                        if( ch == '.' ) { break; }
1343                        else if( ch != '-' && ch != ',' && ( ch != '0' || !firstZero )) {
1344                                buf1.append( ch );
1345                                firstZero = false;
1346                        }
1347                }
1348                if( buf1.length() == 0 ) {
1349                        buf1.append( '0' );
1350                }
1351
1352                for( int i=buf1.length()-3; i>0; i-=3 ) {
1353                        buf1.insert( i,',' );
1354                }
1355                if( chs[0] == '-' ) { buf1.insert( 0,'-' ); }
1356
1357                // 少数部の設定
1358                // 3.6.0.3 (2004/10/05) 桁数が多い場合でもカットしない
1359                StringBuilder buf2 = new StringBuilder();
1360                while( pos < chs.length ) {
1361                        buf2.append( chs[pos++] );
1362                }
1363
1364                while( buf2.length() < minFraction ) {
1365                        buf2.append( '0' );
1366                }
1367
1368                if( buf2.length() > 0 ) {
1369                        buf1.append( '.' ).append( buf2 );
1370                }
1371
1372                return buf1.toString();
1373        }
1374
1375        /**
1376         * 識別id に応じた オブジェクトを作成します。
1377         * 作成するには、デフォルトコンストラクターが必要です。
1378         *
1379         * @param       cls 作成するクラスのフルネーム
1380         *
1381         * @return      オブジェクト
1382         * @throws RuntimeException 何らかのエラーが発生した場合
1383         */
1384        public static Object newInstance( final String cls ) {
1385                return newInstance( cls,Thread.currentThread().getContextClassLoader() );
1386        }
1387
1388        /**
1389         * 指定されたクラスローダを使って、識別id に応じた オブジェクトを作成します。
1390         * 作成するには、デフォルトコンストラクターが必要です。
1391         * initialize パラメータは true 相当(それまでに初期化されていない場合だけ初期化)です。
1392         *
1393         * @param       cls             作成するクラスのフルネーム
1394         * @param       loader  作成するクラスのクラスローダ
1395         *
1396         * @return      オブジェクト
1397         * @throws RuntimeException 何らかのエラーが発生した場合
1398         */
1399        public static Object newInstance( final String cls,final ClassLoader loader ) {
1400                try {
1401                        return Class.forName( cls,true,loader ).newInstance();
1402                }
1403                catch( ClassNotFoundException ex1 ) {
1404                        String errMsg = "クラスが見つかりません。class=[" + cls + "]" + CR
1405                                                + ex1.getMessage() ;
1406                        throw new RuntimeException( errMsg,ex1 );
1407                }
1408                catch( LinkageError ex2 ) {
1409                        String errMsg = "リンケージが失敗しました。class=[" + cls + "]" + CR
1410                                                + ex2.getMessage();
1411                        throw new RuntimeException( errMsg,ex2 );
1412                }
1413                catch( InstantiationException ex3 ) {
1414                        String errMsg = "インスタンスの生成が失敗しました。class=[" + cls + "]" + CR
1415                                                + ex3.getMessage() ;
1416                        throw new RuntimeException( errMsg,ex3 );
1417                }
1418                catch( IllegalAccessException ex4 ) {
1419                        String errMsg = "クラスまたは初期化子にアクセスできません。class=[" + cls + "]" + CR
1420                                                + ex4.getMessage();
1421                        throw new RuntimeException( errMsg,ex4 );
1422                }
1423                catch( RuntimeException ex5 ) {         // 3.6.0.0 (2004/09/17)
1424                        String errMsg = "予期せぬエラー class=[" + cls + "]" + CR
1425                                                + ex5.getMessage() ;
1426                        throw new RuntimeException( errMsg,ex5 );
1427                }
1428        }
1429
1430        /**
1431         * 指定のURL文字列同士を連結させます。
1432         * そのとき、後方URLが、絶対パスの場合は、連結せず 後方URLを返します。
1433         * 第2引数以降は、絶対パス判定をせず直前のURLの末尾判定のみで連結します。
1434         *
1435         * 絶対パスかどうかは、通常のファイル属性と同様に、先頭が、'/' (UNIX)または、
1436         * 2文字目が、":" (Windows)の場合、または、先頭が "\" (ネットワークパス)で
1437         * 始まる場合で判断します。
1438         * 連結時に、前方URLの末尾に "/" を付加します。
1439         *
1440         * 処理の互換性確保のため、第3引数の可変長引数を追加しています。
1441         *
1442         * @og.rev  5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
1443         * @og.rev  5.6.5.2 (2013/06/21) 第3引数を可変長引数に変更
1444         *
1445         * @param       url1 先頭URL文字列
1446         * @param       url2 後方URL文字列(絶対パスの場合は、返り値)
1447         * @param       urls 後方URL文字列
1448         *
1449         * @return      URL文字列同士の連結結果 url1 + url2(url2が絶対パスの場合は、url2から連結開始)
1450         */
1451        public static String urlAppend( final String url1,final String url2,final String... urls ) {
1452                StringBuilder rtnUrl = new StringBuilder( 200 );
1453
1454                if(        url2 == null || url2.length() == 0 ) { rtnUrl.append( url1 ) ; }
1455                else if( ( url1 == null || url1.length() == 0 ) ||
1456                                 ( url2.charAt(0) == '/'  ) ||                                                  // 実ディレクトリが UNIX
1457                                 ( url2.length() > 1 && url2.charAt(1) == ':' ) ||           // 実ディレクトリが Windows
1458                                 ( url2.charAt(0) == '\\' )     ) {                                                     // 実ディレクトリが ネットワークパス
1459                                        rtnUrl.append( url2 ) ;
1460                }
1461                else {
1462                        char ch = url1.charAt( url1.length()-1 ) ;
1463                        if( ch == '/' || ch == '\\' ) {
1464                                rtnUrl.append( url1 ).append( url2 ) ;
1465                        }
1466                        else {
1467                                rtnUrl.append( url1 ).append( "/" ).append( url2 ) ;
1468                        }
1469                }
1470
1471                // ここからが、追加分
1472                for( String url : urls ) {
1473                        if( url != null && url.length() > 0 ) {
1474                                char ch = rtnUrl.charAt( rtnUrl.length()-1 ) ;
1475                                if( ch == '/' || ch == '\\' ) {
1476                                        rtnUrl.append( url ) ;
1477                                }
1478                                else {
1479                                        rtnUrl.append( "/" ).append( url ) ;
1480                                }
1481                        }
1482                }
1483
1484                return rtnUrl.toString() ;
1485        }
1486
1487        /**
1488         * Unicode文字列の値を HTML のエスケープ記号(&amp;#xZZZZ;)に変換します。
1489         *
1490         * SJIS(JA16SJIS) で作成されたデータベースに、(NVARCHAR2)を使用して中国語等を登録するのは
1491         * 非常に複雑でかつ、リスクが大きい処理になります。
1492         * ORACLE殿でも、自信を持っては勧められない機能とのコメントを頂いています。
1493         * そこで、HTMLでのエスケープ文字を使用して、Unicodeを文字列化して登録する為の
1494         * DBType として、新規に作成します。
1495         * ここでは、入力文字を、キャラクタ(char)型に分解し、(&amp;#xZZZZ;)に変換していきます。
1496         * よって、通常に1文字(Shift-JISで2Byte,UTF-8で3Byte)が、8Byteになります。
1497         * この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為、
1498         * 通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。
1499         * ここでは、2バイト文字のみ、変換しています。
1500         *
1501         * @param       value 変換前の文字列
1502         *
1503         * @return      HTMLのエスケープ記号(&amp;#xZZZZ;)
1504         */
1505        public static String getUnicodeEscape( final String value ) {
1506                if( value == null || value.length() == 0 ) { return ""; }
1507
1508                StringBuilder rtn = new StringBuilder( value.length() * 4 );
1509
1510                for( int i=0; i<value.length(); i++ ) {
1511                        char ch = value.charAt(i);
1512
1513                        if( ch > 0xff ) {
1514                                String hex = Integer.toHexString( (int)ch ) ;
1515                                rtn.append( UTF_STR[hex.length()] ).append( hex ).append( ";" );
1516                        }
1517                        else {
1518                                rtn.append( ch );
1519                        }
1520                }
1521
1522                return rtn.toString();
1523        }
1524
1525        /**
1526         * HTML のエスケープ記号(&amp;#xZZZZ;)をUnicode文字列に戻します。
1527         *
1528         * HTMLでのエスケープ文字を使用して登録された文字を、Unicodeに戻します。
1529         * (&amp;#xZZZZ;)の8Byteを、もとのキャラクタコードに戻し、合成します。
1530         * ここでは、通常の文字列に混在したエスケープ文字も戻せるようにします。
1531         *
1532         * @param       value   HTMLのエスケープ記号(&amp;#xZZZZ;)を含む文字列
1533         * 
1534         * @og.rev 5.9.5.3 (2016/02/26) 無限ループ対応
1535         *
1536         * @return      通常のUnicode文字列
1537         */
1538        public static String getReplaceEscape( final String value ) {
1539                if( value == null || value.length() == 0 ) { return ""; }
1540
1541                StringBuilder rtn = new StringBuilder( value );
1542
1543                int st = rtn.indexOf( "&#" );
1544                while( st >= 0 ) {
1545                        if( st+7 < rtn.length() && rtn.charAt( st+7 ) == ';' ) {
1546                                int ch = Integer.parseInt( rtn.substring( st+3,st+7 ),16 );
1547                                rtn.replace( st,st+8, Character.toString( (char)ch ) );
1548                        }
1549//                      st = rtn.indexOf( "&#",st );
1550                        st = rtn.indexOf( "&#",st + 1 ); // 5.9.5.3 (2016/02/26) 無限ループ対応 
1551                }
1552
1553                return rtn.toString();
1554        }
1555
1556        /**
1557         * 文字列をdoubleに変換します。
1558         *
1559         * これは、Double.parseDouble( value ) と、ほぼ同じ動作を行います。
1560         * 内部的には、引数の カンマ(,) を削除した文字列を、Double.parseDouble( value )
1561         * に渡します。
1562         * また、引数が、null,ゼロ文字列,'_' の時には、0.0 を返します。
1563         *
1564         * @param       value   doubleに変換する元の文字列
1565         *
1566         * @return      変換後のdouble数値
1567         */
1568        public static double parseDouble( final String value ) {
1569                double rtn ;
1570
1571                if( value == null || value.length() == 0 || value.equals( "_" ) ) {
1572                        rtn = 0.0d;
1573                }
1574                else if( value.indexOf( ',' ) < 0 ) {
1575                        rtn = Double.parseDouble( value );
1576                }
1577                else {
1578                        char[] chs = value.toCharArray() ;
1579                        int j=0;
1580                        for( int i=0;i<chs.length; i++ ) {
1581                                if( chs[i] == ',' ) { continue; }
1582                                chs[j] = chs[i];
1583                                j++;
1584                        }
1585                        rtn = Double.parseDouble( String.valueOf( chs,0,j ) );
1586                }
1587
1588                return rtn ;
1589        }
1590
1591        /**
1592         * カラーキーワードより、Colorオブジェクトを作成します。
1593         *
1594         * 指定文字列は、java.awt.Color クラスのstatic フィールド名で指定します。
1595         * BLACK , BLUE , CYAN , DARK_GRAY , GRAY , GREEN , LIGHT_GRAY ,
1596         * MAGENTA , ORANGE , PINK , RED , WHITE , YELLOW , PURPLE , TRANSPARENT(透明) が指定できます。
1597         * また、先頭に、# を付ける事で、#XXXXXX形式の16bitRGB表記 でも指定可能です。
1598         * static フィールド名のMapを管理していますが、存在しない場合は、エラーになります。
1599         *
1600         * @og.rev 3.8.9.1 (2007/06/29) 新規作成
1601         * @og.rev 4.1.1.0 (2008/02/04) CLR_MAP に存在しない場合はエラーにします。
1602         *
1603         * @param       value java.awt.Color フィールドを示す文字列または、#XXXXXX形式の16bitRGB表記
1604         *
1605         * @return      Colorオブジェクト
1606         * @see         java.awt.Color#BLACK
1607         */
1608        public static Color getColorInstance( final String value ) {
1609                final Color clr ;
1610
1611                if( value.startsWith("#") ) {
1612                        int code = Integer.parseInt( value.substring(1),16 );
1613                        clr = new Color( code );
1614                }
1615                else {
1616                        clr = CLR_MAP.get( value );
1617                        if( clr == null ) {
1618                                String errMsg = "指定の色コードは使用できません Color=[" + value + "]" + CR
1619                                                        + "ColorMap=" + CLR_MAP.keySet().toString();
1620                                throw new RuntimeException( errMsg );
1621                        }
1622                }
1623
1624                return clr;
1625        }
1626
1627        /**
1628         * 引数からspanタグを取り除いて返します。
1629         *
1630         * 引数が、&lt;span ・・・&gt;XXXX&lt;/span&gt;形式の場合、XXXX のみ出力します。
1631         *
1632         * @og.rev 4.3.4.3 (2008/12/22) TableWriterで利用していたものを移動
1633         * @og.rev 5.9.11.1 (2016/08/10) spanだけでなく、pre,textareaも除外するようにしておく
1634         *
1635         * @param        data 元のString文字列
1636         *
1637         * @return       spanタグが取り除かれた文字列
1638         */
1639        public static String spanCut( final String data ) {
1640                String rtn = data;
1641                if( data != null ){
1642                        if( data.startsWith( "<span" ) ) {
1643                                int st = data.indexOf( '>' );
1644                                int ed = data.indexOf( "</span>",st );
1645                                rtn = data.substring( st+1,ed );
1646                        }
1647                        else if( data.startsWith( "<pre" ) ) {  // 5.9.11.1 本来はV6のようにするべきだが、V5は暫定的な対応にしておく
1648                                int st = data.indexOf( '>' );
1649                                int ed = data.indexOf( "</pre>",st );
1650                                rtn = data.substring( st+1,ed );
1651                        }
1652                        else if( data.startsWith( "<textarea" ) ) {
1653                                int st = data.indexOf( '>' );
1654                                int ed = data.indexOf( "</textarea>",st );
1655                                rtn = data.substring( st+1,ed );
1656                        }
1657                }
1658                
1659                return rtn ;
1660        }
1661
1662        /**
1663         * 簡易CSS形式のフォーマットを、Mapにセットします。
1664         *
1665         * 簡易CSS形式とは、セレクタのない、{ プロパティ1 : 値1 ; ・・・ } 形式とします。
1666         * これを、プロパティ1 と 値1 のMap にセットする処理を行います。
1667         * コメントは、削除されます。また、同一プロパティが記述されている場合は、後処理を採用します。
1668         *
1669         * なお、入力テキストが、null か、{…} が存在しない場合は、null を返します。
1670         *
1671         * @og.rev 5.6.5.2 (2013/06/21) 新規追加
1672         *
1673         * @param        cssText 簡易CSS形式のフォーマット文字列
1674         *
1675         * @return       パース結果のMap
1676         */
1677        public static Map<String,String> cssParse( final String cssText ) {
1678                Map<String,String> map = null;
1679
1680                if( cssText != null ) {
1681                        // まずコメントを削除します。
1682                        StringBuilder buf = new StringBuilder( cssText );
1683
1684                        int ad1 = buf.indexOf( "/*" );
1685                        while( ad1 >= 0 ) {
1686                                int ad2 = buf.indexOf( "*/" , ad1 );
1687                                if( ad2 < 0 ) { buf = buf.delete( ad1,buf.length() ); break; }               // 閉じてなければ以降を全削除
1688                                buf = buf.delete( ad1,ad2+2 );
1689                                ad1 = buf.indexOf( "/*" );              // コメントは削除されたので、初めから検索する。
1690                        }
1691
1692                        // 処理対象は、{ 〜 } の間の文字列。
1693                        ad1 = buf.indexOf( "{" );
1694                        int ad2 = buf.indexOf( "}",ad1 );
1695                        if( ad1 >= 0 && ad2 > 0 ) {
1696                                String tempText = buf.substring( ad1+1,ad2 );           // これが処理対象の文字列
1697
1698                                String[] recode = tempText.split( ";" );                        // KEY1 : VAL1; の ; で分割する。
1699
1700                                for( int i=0; i<recode.length; i++ ) {
1701                                        int ad = recode[i].indexOf( ':' );
1702                                        if( ad > 0 ) {
1703                                                String key = recode[i].substring( 0,ad ).trim();
1704                                                String val = recode[i].substring( ad+1 ).trim();
1705                                                if( key.isEmpty() || val.isEmpty() ) { continue; }
1706
1707                                                if( map == null ) { map = new HashMap<String,String>(); } // 対象データがある時だけMapを作りたかったので。
1708                                                map.put( key,val );
1709                                        }
1710                                }
1711                        }
1712                }
1713                return map ;
1714        }
1715        
1716        /**
1717         * 引数から空白文字を削除して返します。
1718         *
1719         *
1720         * @og.rev 5.6.9.4 (2013/10/31) TableWriterで利用していたものを移動
1721         *
1722         * @param        data 元のString文字列
1723         *
1724         * @return       空白文字が取り除かれた文字列
1725         */
1726        public static String deleteWhitespace( final String data ) {
1727                if( data == null || data.length() == 0 ){
1728                        return data;
1729                }
1730                return data.replaceAll( "\\s", "" ) ;
1731        }
1732        
1733        /**
1734         * 引数から指定文字の分のバイト数で切った文字列を返します。
1735         * 文字列のバイト数は指定のエンコードでカウントします。
1736         * (文字の途中で切れる事はありません)
1737         *
1738         *
1739         * @og.rev 5.9.1.3 (2015/10/30) 新規作成
1740         *
1741         * @param       org 元のString文字列
1742         * @param       cutBytes 切るバイト数
1743         * @param       enc 文字列のエンコード
1744         *
1745         * @return       バイト数で切った文字列
1746         */
1747        public static String cut( String org, int cutBytes, String enc ) {
1748                try {
1749                        if ( org == null   || org.length() == 0 || cutBytes <= 0 || org.getBytes(enc).length <= cutBytes ) {
1750                                return org;
1751                        }
1752
1753                        StringBuilder cutSb = new StringBuilder();
1754                        StringBuilder tmpSb = new StringBuilder();
1755
1756                        for (int i = 0; i < org.length(); i++) {
1757                                String cut = org.substring(i, i + 1);
1758                                if (cutBytes < tmpSb.toString().getBytes(enc).length + cut.getBytes(enc).length) {
1759                                        cutSb.append(tmpSb.toString());
1760                                        break;
1761                                }
1762                                tmpSb.append(cut);
1763                        }
1764                        return cutSb.toString();
1765
1766                } 
1767                catch (UnsupportedEncodingException e) {
1768                        e.printStackTrace();
1769                        return org;
1770                }
1771        }
1772        
1773        /**
1774         * 引数から指定文字の分のバイト数で切った文字列を返します。
1775         * バイト数のカウントはUTF-8として行います。
1776         *
1777         *
1778         * @og.rev 5.9.1.3 (2015/10/30) 新規作成
1779         *
1780         * @param       org 元のString文字列
1781         * @param       cutBytes 切るバイト数
1782         *
1783         * @return       バイト数で切った文字列
1784         */
1785        public static String cut( String org, int cutBytes ) {
1786                return cut( org, cutBytes, "UTF-8");
1787        }
1788}