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