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 org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
019import java.lang.reflect.InvocationTargetException;                     // Ver7.0.0.0
020import java.io.UnsupportedEncodingException;
021import java.net.URLEncoder;
022import java.net.URLDecoder;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Enumeration;
026import java.util.StringJoiner;                                                          // 6.4.4.2 (2016/04/01)
027import java.util.concurrent.ConcurrentMap;                                      // 6.4.3.3 (2016/03/04)
028import java.util.concurrent.ConcurrentHashMap;                          // 6.4.3.1 (2016/02/12) refactoring
029import java.util.Iterator;
030import java.util.StringTokenizer;
031import java.util.Locale ;                                                                       // 5.7.2.3 (2014/01/31)
032import java.text.DecimalFormat;                                                         // 6.2.0.0 (2015/02/27)
033import java.util.function.UnaryOperator ;                                       // 6.9.2.1 (2018/03/12)
034
035import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
036import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.4.2.0 (2016/01/29) ローカル定義をやめて、HybsConst を使用する様に変更。
037import org.opengion.fukurou.system.ThrowUtil;                                           // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
038
039/**
040 * StringUtil.java は、共通的に使用される String関連メソッドを集約した、クラスです。
041 *
042 * @og.group ユーティリティ
043 *
044 * @version  4.0
045 * @author       Kazuhiko Hasegawa
046 * @since    JDK5.0,
047 */
048public final class StringUtil {
049
050        /**
051         * code39 のチェックデジット計算に使用する モジュラス43 の変換表です。
052         *
053         */
054        private static final String MODULUS_43 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" ;
055
056        /**
057         * getUnicodeEscape で使用する桁合わせ用文字列配列です。
058         * Unicodeの HexString 変換後の桁に応じて、埋め合わせします。
059         *
060         */
061        private static final String[] UTF_STR = { "&#x0000", "&#x000", "&#x00", "&#x0", "&#x" };
062
063        // 4.0.3.0 (2007/12/26) 色コードにPURPLE を追加
064        // 5.7.8.0 (2014/07/04) 透明追加
065        // 6.0.2.1 (2014/09/26) ColorMap クラスに移動
066
067        // 6.2.0.0 (2015/02/27) #numberFormat( String , int ) で使用するフォーマット変換オブジェクト
068        private static final DecimalFormat[] FMT1 = new DecimalFormat[] {
069                                                                        new DecimalFormat( "#,##0" ) ,
070                                                                        new DecimalFormat( "#,##0.0" ) ,
071                                                                        new DecimalFormat( "#,##0.00" ) ,
072                                                                        new DecimalFormat( "#,##0.000" ) ,
073                                                                        new DecimalFormat( "#,##0.0000" ) } ;
074
075        private static final String ZERO = "00000000000000000000" ;             // ゼロ埋めの種
076
077        /**
078         *      デフォルトコンストラクターをprivateにして、
079         *      オブジェクトの生成をさせないようにする。
080         *
081         */
082        private StringUtil() {}
083
084        /**
085         * UTF-8 で、URLエンコードを行います。
086         * このメソッドは、JDK1.4 以上でないと使用できません。
087         *
088         * @param       value エンコードする文字列
089         *
090         * @return       指定の文字コードでURLエンコードされた文字列
091         * @see         #urlEncode2( String )
092         * @og.rtnNotNull
093         */
094        public static String urlEncode( final String value ) {
095                if( value == null ) { return ""; }
096
097                try {
098                        return URLEncoder.encode( value,"UTF-8" );
099                }
100                catch( final UnsupportedEncodingException ex ) {
101                        final String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
102                                                + ex.getMessage() ;
103                        throw new OgRuntimeException( errMsg,ex );
104                }
105                catch( final RuntimeException ex2 ) {           // 3.6.0.0 (2004/09/17)
106                        final String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
107                                                + ex2.getMessage();
108                        throw new OgRuntimeException( errMsg,ex2 );
109                }
110        }
111
112        private static final String UN_CHANGE = ":/?=&._~" ;
113
114        /**
115         * UTF-8 で、ASCII以外の文字の、URLエンコードします。
116         *
117         * 00 ~ 7F までのコードは、変換しません。
118         *
119         * これは、日本語ファイル名の直リンクなど、URLエンコードが必要ですが、
120         * http:// などのURL の場合は、':' , '/' は、エンコードしたくありません。
121         * また、openGion では、[カラム] などの特殊な変数を渡して、処理させているので
122         * それらのキーワードも変換してほしくありません。
123         * ただし、"%" と、";" は、変換します。
124         *
125         * @og.rev 6.2.0.1 (2015/03/06) ASCII以外の文字の、URLエンコードを行う。
126         * @og.rev 6.9.0.0 (2018/01/31) 半角の中でも ':' , '/' , '?' , '=' , '&' , '.' , '_' , '~' 以外の文字は置き換えます。
127         *
128         * @param       value エンコードする文字列
129         *
130         * @return       指定の文字コードでURLエンコードされた文字列(ASCII は省く)
131         * @see         #urlEncode( String )
132         * @og.rtnNotNull
133         */
134        public static String urlEncode2( final String value ) {
135                if( value == null ) { return ""; }
136
137                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
138                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
139
140                for( int i=0; i<value.length(); i++ ) {
141                        final char ch = value.charAt(i);
142//                      if( ch > 0x7f ) { buf.append( ch ); }                           // ASCII以外は、とりあえず貯めておく
143                        if( ch > 0x7f || UN_CHANGE.indexOf( ch ) < 0 ) { buf.append( ch ); }    // ASCII以外は、とりあえず貯めておく
144                        else {
145                                if( buf.length() > 0 ) {                                                // 前回のデータが残っている
146                                        rtn.append( urlEncode( buf.toString() ) );      // ASCII以外のurlEncode処理と追加
147                                        buf.setLength(0);                                                       // 初期化
148                                }
149                                rtn.append( ch );
150        //                      // ファイル名に、";" や "%" が存在すると、認識できないため、半角文字でも変換しておきます。
151        //                      if(      ch == ';' ) {  rtn.append( "%3B" ); }  // 特殊処理
152        //                      else if( ch == '%' ) {  rtn.append( "%25" ); }
153        //                      else {                                  rtn.append( ch );    }  // ASCII文字の追加
154                        }
155                }
156
157                if( buf.length() > 0 ) {                                                                // 残っている分
158                        rtn.append( urlEncode( buf.toString() ) );                      // ASCII以外のurlEncode処理と追加
159                }
160
161                return rtn.toString();
162        }
163
164        /**
165         * UTF-8 でURLエンコードされた文字列をデコードします。
166         * このメソッドは、JDK1.4 以上でないと使用できません。
167         *
168         * @og.rev 5.4.5.0 追加
169         * @param       value デコードする文字列
170         *
171         * @return       デコードされた文字列
172         */
173        public static String urlDecode( final String value ) {
174                try {
175                        return URLDecoder.decode( value,"UTF-8" );
176                }
177                catch( final UnsupportedEncodingException ex ) {
178                        final String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
179                                                + ex.getMessage() ;
180                        throw new OgRuntimeException( errMsg,ex );
181                }
182                catch( final RuntimeException ex2 ) {           // 3.6.0.0 (2004/09/17)
183                        final String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
184                                                + ex2.getMessage();
185                        throw new OgRuntimeException( errMsg,ex2 );
186                }
187        }
188
189        /**
190         * 文字列の後ろのスペースを削除します。
191         * String クラスの trim()メソッドは、文字列の両方のスペースを削除しますが、
192         * この rTrim( String ) は、後ろの半角スペースのみ、詰めます。
193         * 注意:'\u0020' (スペース文字) より小さい文字を切り取ります。
194         *
195         * @param       str 元の文字列
196         *
197         * @return      後ろの半角スペースを詰めた、新しい文字列
198         */
199        public static String rTrim( final String str ) {
200                if( str == null )  { return null; }
201                final int count = str.length();
202
203                int len = count;
204
205                while( 0 < len && str.charAt(len-1) <= ' ' ) {
206                        len--;
207                }
208                return len < count ? str.substring(0, len) : str;
209        }
210
211        /**
212         * 文字列の後ろから、" .0" の文字を削除した数字型文字列を返します。
213         * 数字型文字列は、入力文字列の後ろの スペース、小数点、ゼロを削除します。
214         * また、先頭が、"." で始まる場合は、"0" を追加します。
215         * 例: "123.00" ⇒ "123" , ".123" ⇒ "0.123"
216         *
217         * @og.rev 3.8.8.1 (2007/01/10) 新規作成
218         *
219         * @param       str 元の文字列
220         *
221         * @return      数字文字列化された、新しい文字列
222         */
223        public static String toNumber( final String str ) {
224                if( str == null )  { return null; }
225
226                String rtn = str.trim() ;
227
228                final int adrs = rtn.indexOf( '.' );
229                final int count = rtn.length();
230                int len = count;
231
232                if( adrs >= 0 ) {
233                        while( adrs < len && ".0".indexOf( rtn.charAt(len-1) ) >= 0 ) {
234                                len--;
235                        }
236                }
237
238                if( len < count ) { rtn = rtn.substring(0, len); }
239                if( adrs == 0 ) { rtn = "0" + rtn; }
240
241                return rtn ;
242        }
243
244        /**
245         * 文字列の前方のゼロ(0)を削除します。
246         * 先頭の0を削除するまえに、trim して、スペースを削除しておきます。
247         * すべてがゼロ(0)の場合は、"0" を返します。
248         * 小数点( 0.01 など )の場合は、先頭の 0 がすべて消えるとまずいので、
249         * "0." 部分は、残します。
250         *
251         * @og.rev 3.5.4.5 (2004/01/23) 新規追加
252         *
253         * @param       inStr 元の文字列
254         *
255         * @return      前方のゼロ(0)を削除した、新しい文字列
256         */
257        public static String lTrim0( final String inStr ) {
258                if( inStr == null )  { return null; }
259                final String str = inStr.trim();
260                final int count = str.length();
261
262                int len = 0;
263                while( count > len && str.charAt(len) == '0' ) {
264                        len++;
265                }
266
267                if( len == 0 ) { return str; }                          // 先頭がゼロでない。
268                else if( len == count ) { return "0"; }         // すべてがゼロ
269                else if( str.charAt(len) == '.' ) { return "0" + str.substring(len); }
270                else { return str.substring(len); }
271        }
272
273        /**
274         * 文字列配列の各要素の後ろのスペースを削除します。
275         * 個々の配列要素に対して、rTrim( String str ) を適用します。
276         * 元の文字列配列に直接作用するのではなく、新しい文字列配列に
277         * 結果をコピーして返します。
278         * ただし、元の文字列配列が、null か、length == 0 の場合は、
279         * 元の文字列配列(アドレス)を返します。
280         * 注意:'\u0020' (スペース文字) より小さい文字を切り取ります。
281         *
282         * @param       str 元の文字列配列(可変長引数)
283         *
284         * @return      後ろの半角スペースを詰めた、新しい文字列配列
285         */
286        public static String[] rTrims( final String... str ) {
287                // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
288                if( str == null || str.length == 0 ) { return str; }
289
290                String[] rtn = new String[str.length];  // str.length == 0 の場合、長さゼロの新しい配列を返す。
291                for( int i=0; i<str.length; i++ ) {
292                        rtn[i] = rTrim( str[i] );
293                }
294                return rtn ;
295        }
296
297        /**
298         * 文字列の前後のダブルクオートを取り外します。
299         * 前後にダブルクオートが入っていなければ、そのままの文字列を返します。
300         * 前後に入っていない(片方のみなど)場合も、そのままの文字列を返します。
301         * ※ 先頭に、'0 が含まれる場合は、カンマを削除します。
302         *    従来は、ダブルクオートしてから、rTrim してましたが、trim してから、
303         *    ダブルクオート外しを行います。
304         *
305         * @og.rev 6.2.1.0 (2015/03/13) 先頭に、'0 が含まれる場合は、カンマを削除
306         *
307         * @param       str 元の文字列
308         *
309         * @return      ダブルクオートを取り外した新しい文字列
310         */
311        public static String csvOutQuote( final String str ) {
312                if( str == null )  { return null; }
313
314                // 6.2.1.0 (2015/03/13) 先頭に、'0 が含まれる場合は、カンマを削除
315                String rtn = str.trim();                                                                        // ①前後のスペース削除
316                if( rtn.startsWith( "'0" ) ) { rtn = rtn.substring(1); }        // ②先頭の'0 のカンマ外し
317                else {
318                        final int end = rtn.length();                                                   // ③前後のダブルクオート外し
319                        if( end >= 2 && str.charAt(0) == '"' && str.charAt( end-1 ) == '"' ) {
320                                rtn = rtn.substring( 1,end-1 );
321                        }
322                }
323                return rtn;
324        }
325
326        /**
327         * 内部で使われる byte[] から String 生成 メソッド。
328         *
329         * @param       byteValue        変換するバイト列
330         * @param       start            変換開始アドレス
331         * @param       length           変換バイト数
332         * @param       encode           変換する文字エンコード
333         *
334         * @return      変換後文字列
335         */
336        public static String makeString( final byte[] byteValue, final int start, final int length,final String encode ) {
337
338                if( encode.startsWith( "Unicode" ) ) {
339                        final String errMsg = "Unicode文字列は、変換できません。[" + encode + "]"  + CR;
340                        throw new OgRuntimeException( errMsg );
341                }
342
343                String rtn = null;
344                if( byteValue != null ) {
345                        try {
346                                // encode コードで変換されている byte[] を、String に変換。
347                                rtn = new String( byteValue,start,length,encode );
348                        } catch( final UnsupportedEncodingException ex ) {        // 変換コードが存在しないエラー
349                                final String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
350                                                        + ex.getMessage() ;
351                                throw new OgRuntimeException( errMsg,ex );
352                        }
353                }
354                return rtn;
355        }
356
357        /**
358         * 指定の文字列をバイトコードに変換します。
359         * 引数の文字列が null の場合は、return は、byte[0] を返します。
360         *
361         * @param       value    変換するストリング値
362         * @param       encode   変換する文字エンコード
363         *
364         * @return      変換後文字列
365         */
366        public static byte[] makeByte( final String value,final String encode ) {
367                byte[] rtnByte = new byte[0];
368                if( value != null ) {
369                        try {
370                                rtnByte = value.getBytes( encode );             // byte[] に encode コードで変換。
371                        } catch( final UnsupportedEncodingException ex ) {        // 変換コードが存在しないエラー
372                                final String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
373                                                        + ex.getMessage();
374                                throw new OgRuntimeException( errMsg,ex );
375                        }
376                }
377                return rtnByte;
378        }
379
380        /**
381         * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
382         * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
383         * 内部にセットした文字列は、変化しません。
384         *
385         * @param       str      Fill埋めする文字列
386         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
387         *
388         * @return      Fill埋めした新しいStringを返す。
389         * @og.rtnNotNull
390         */
391        public static String stringXFill( final String str,final int su_fill ) {
392                char[] charValue ;
393
394                if( str == null ) { charValue = new char[0]; }
395                else              { charValue = str.toCharArray(); }
396                final int len = charValue.length;
397
398                if( su_fill < len ) {
399                        final String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。)"
400                                        + "su_fill[" + su_fill + "], len[" + len + "]" + CR
401                                        + "input=[" + str + "]" + CR;
402                        throw new OgRuntimeException( errMsg );
403                }
404
405                final char[] charbuf = new char[ su_fill ];                     // 移す char 配列を新規作成
406                Arrays.fill( charbuf,' ' );
407                System.arraycopy( charValue,0,charbuf,0,len );
408
409                return new String( charbuf );            // コピーした配列全てを文字列に変換
410        }
411
412        /**
413         * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
414         * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
415         * 内部にセットした文字列は、変化しません。
416         *
417         * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
418         *
419         * @param       str      Fill埋めする文字列
420         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
421         * @param       encode   Fill埋めする文字列の文字エンコード
422         *
423         * @return      Fill埋めした新しいStringを返す。
424         */
425        public static String stringFill( final String str,final int su_fill,final String encode ) {
426                if( su_fill < 0 ) {
427                        final String errMsg = "指定文字数が負です。[" + su_fill + "]";
428                        throw new OgRuntimeException( errMsg );
429                }
430
431                final byte[] byteValue = makeByte( str,encode );
432                final int len = byteValue.length;
433
434                // 内部文字列が指定長より長い場合
435                if( len >= su_fill ) {
436                        return makeString( byteValue,0,su_fill,encode );
437                }
438                else {
439                        byte[] space = makeByte( " ",encode );
440                        int spaceLen = space.length ;
441                        if( spaceLen == 4 ) {                                   // encode が、UnicodeLittle の場合の特殊処理
442                                space[0] = space[2];
443                                space[1] = space[3];
444                                spaceLen = 2;
445                        }
446                        byte[] bytebuf = new byte[su_fill];
447                        // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
448                        System.arraycopy( byteValue,0,bytebuf,0,len );          // 6.3.6.0 (2015/08/16)
449
450                        int k = 0;
451                        for( int j=len; j<su_fill; j++ ) {              // 余った部分は、スペース埋め
452                                if( k >= spaceLen ) { k = 0; }
453                                bytebuf[j] = space[k++];
454                        }
455                        return makeString( bytebuf,0,su_fill,encode );  // 新たに、すべての長さの部分文字列を作成する。
456                }
457        }
458
459        /**
460         * 整数のフォーム( 12 で、整数部 12桁を表す)に合った新しい文字列を作り、それを返します。
461         * 実行できるのは、整数の String に対してのみです。
462         * 内部にセットした文字列は、変化しません。
463         * 桁数がオーバーする場合は、RuntimeException を throw します。
464         *
465         *   String str = StringUtil.intFill( "123",10 );
466         *
467         *   実行結果:"0000000123"
468         *
469         * @param       str     整数の String
470         * @param       su_fill フォームを表す正の数字 ( 12 で、整数部 12桁を表す)
471         *
472         * @return      整数のフォームに合った文字列
473         * @og.rtnNotNull
474         * @see         #intFill( int ,int )
475         * @throws      RuntimeException su_fill が、負の数か、元の文字数がフォームより長い場合、エラー
476         */
477        public static String intFill( final String str,final int su_fill ) {
478                if( su_fill < 0 ) {
479                        final String errMsg = "指定文字数が負です。[" + su_fill + "]";
480                        throw new OgRuntimeException( errMsg );
481                }
482
483                final char[] charbuf = new char[ su_fill ];                     // 移す char 配列を新規作成
484                Arrays.fill( charbuf,'0' );
485
486                if( str == null ) { return new String( charbuf ); }
487
488                final char[] charValue = str.toCharArray();
489                final int len = charValue.length;
490
491                if( su_fill < len ) {
492                        final String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。) su_fill[" + su_fill + "], len[" + len + "]";
493                        throw new OgRuntimeException( errMsg );
494                }
495
496                System.arraycopy( charValue,0,charbuf,su_fill-len,len );
497
498                return new String( charbuf );            // コピーした配列全てを文字列に変換
499        }
500
501        /**
502         * 整数のフォーム( 12 で、整数部 12桁を表す)に合った新しい文字列を作り、それを返します。
503         * 実行できるのは、正の整数に対してのみです。
504         * 桁数がオーバーする場合は、オーバーしたまま返します。
505         *
506         *   String str = StringUtil.intFill( 123,10 );
507         *
508         *   実行結果:"0000000123"
509         *
510         * @og.rev 6.0.2.4 (2014/10/17) 新規追加
511         *
512         * @param       num     正の整数
513         * @param       su_fill フォームを表す数字 ( 12 で、整数部 12桁を表す)
514         *
515         * @return      整数のフォームに合った文字列
516         * @see         #intFill( String ,int )
517         * @throws      RuntimeException su_fill または、num が、負の数の場合、エラー
518         */
519        public static String intFill( final int num,final int su_fill ) {
520                if( num < 0 || su_fill < 0 ) {
521                        final String errMsg = "指定文字数が負です。num=[" + num + "] , su_fill=[" + su_fill + "]";
522                        throw new OgRuntimeException( errMsg );
523                }
524
525                String rtn = String.valueOf( num );
526
527                final int len = su_fill - rtn.length();                                         // 桁の不足分を算出
528                if( len > 0 ) {
529                        rtn = "00000000000000000000".substring( 0,len ) + rtn ;
530                }
531
532                return rtn;
533        }
534
535        /**
536         * 全角スペースで固定長(半角換算の数)に変換した文字列を返します。
537         *
538         * @param       str      Fill埋めする文字列
539         * @param       su_fill  Fill埋めする文字列の長さ。(半角換算の数)
540         * @param       encode   Fill埋めする文字列の文字エンコード
541         *
542         * @return      全角スペースでFill埋めした新しいStringを返す。
543         */
544        public static String stringKFill( final String str,final int su_fill,final String encode ) {
545                if( su_fill < 0 ) {
546                        final String errMsg = "指定文字数が負です。[" + su_fill + "]";
547                        throw new OgRuntimeException( errMsg );
548                }
549
550                final byte[] byteValue = makeByte( str,encode );
551                final int len = byteValue.length;
552
553                // 内部文字列が指定長より長い場合
554                if( len >= su_fill ) {
555                        return makeString( byteValue,0,su_fill,encode );
556                }
557                else {
558                        final byte[] bytebuf = new byte[ su_fill ];
559                        System.arraycopy( byteValue, 0, bytebuf, 0, len );                              // 6.3.9.0 (2015/11/06) System.arraycopy is more efficient(PMD)
560
561                        final byte[] space = makeByte( " ",encode );
562                        final int spaceLen = space.length ;
563                        int k = 0;
564                        for( int j=len; j<su_fill; j++ ) {              // 余った部分は、スペース埋め
565                                if( k >= spaceLen ) { k = 0; }
566                                bytebuf[j] = space[k++];
567                        }
568                        return makeString( bytebuf,0,su_fill,encode );  // 新たに、すべての長さの部分文字列を作成する。
569                }
570        }
571
572        /**
573         * 小数点のフォームに合った新しい文字列を作り、文字列を返します。
574         * 現在は、小数点が頭に付いたり、最後に付く場合の対応はしていません。
575         * フォームは、12.4 で、 000000000010.1000 という形で、ピリオドを含みます。
576         *
577         *  // 半角 整数部 10 桁 小数部 5桁で固定長の文字を得る。
578         *  String str = StringUtil.realFill( "123.45" ,10.5 ) ;
579         *
580         *  実行結果:0000000123.45000
581         *
582         * @param       str             整数の String
583         * @param       su_fill フォームを表す実数       ( 12.4 で、整数部 12桁、小数部 4桁 計17桁 )
584         *
585         * @return      value   小数点のフォーム文字列
586         * @og.rtnNotNull
587         */
588        public static String realFill( final String str,final double su_fill ) {
589                if( su_fill < 0 ) {
590                        final String errMsg = "指定文字数が負です。[" + su_fill + "]";
591                        throw new OgRuntimeException( errMsg );
592                }
593
594                final int su_seisu = (int)(su_fill);                                               // 指定のフォームの整数部を取り出す。
595                final int su_shosu = (int)(su_fill*10 - su_seisu*10);              // 小数部を取り出しす。
596                char[] charbuf = new char[ su_seisu + su_shosu + 1 ];  // 移す char 配列
597                Arrays.fill( charbuf,'0' );
598
599                if( str == null ) {
600                        charbuf[su_seisu] = '.' ;
601                        return new String( charbuf );
602                }
603
604                // 検査する文字列の加工(検査文字列は、インデックスの値とバイト数で文字数を求める。)
605                // 小数点の位置を求める。 本当は、String クラスの indexOf で求めず、byte[] で検索すべきである。
606                final int valueindex = str.indexOf( '.' );
607                if( valueindex < 0 ) {                                                                  // valueform 自体が、合っていない。
608                        final String errMsg = "元の文字列に小数点が、含まれません。";
609                        throw new OgRuntimeException( errMsg );
610                }
611                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
612
613                // フォームの整数文字数 ー 加工文字の整数文字部 = 転送先配列位置
614                int toIndex = su_seisu - valueindex;                                                    // 6.4.1.1 (2016/01/16)
615                if( toIndex < 0 ) {
616                        final String errMsg = "元の数字が、フォームより長いです。(数字が壊れます。) form[" + su_fill + "]";
617                        throw new OgRuntimeException( errMsg );
618                }
619                int endIndex;
620                // 転送先配列終了位置は、お互いの小数部の文字数により、短い方を選ぶ。
621                final char[] charValue  = str.toCharArray();
622                final int su_valueshosu = charValue.length - valueindex - 1 ;   // 小数部の文字数は、全文字数-整数文字数-1
623                if( su_shosu < su_valueshosu ) { endIndex = su_seisu + su_shosu + 1; }
624                else                                               { endIndex = su_seisu + su_valueshosu + 1; }
625
626                int fromIndex = 0;
627                while( toIndex < endIndex ) {
628                        charbuf[toIndex++] = charValue[fromIndex++];       // 転送(移し替え)
629                }
630                return new String( charbuf );            // コピーした配列全てを文字列に変換
631        }
632
633        /**
634         * ストリングの部分文字列を,別の文字列に置換えたストリングを返します。
635         * 例えば,リターンコードを&lt; br /&gt;に置換えて,画面上に改行表示させるが可能です。
636         *
637         * @og.rev 5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
638         *
639         * @param       target 元の文字列
640         * @param       from   置換元部分文字列
641         * @param       to         置換先部分文字列
642         *
643         * @return      置換えた文字列
644         */
645        public static String replace( final String target,final String from,final String to ) {
646                if( target == null || from == null || to == null || target.indexOf( from ) < 0 ) { return target; }
647
648                final StringBuilder strBuf = new StringBuilder( target.length() );
649
650                int start = 0;
651                int end   = target.indexOf( from,start );
652                while( end >= 0 ) {
653                        strBuf.append( target.substring( start,end ) );
654                        strBuf.append( to );
655                        start = end + from.length();
656                        end   = target.indexOf( from,start );
657                }
658
659                if( start > 0 ) {
660                        strBuf.append( target.substring( start ) );
661                        return strBuf.toString();
662                }
663                else {
664                        return target;                  // 3.4.0.2 (2003/09/05)
665                }
666        }
667
668        /**
669         * 変数の置き換え処理を行います。
670         *
671         * 変換元の文字列から、prefix と、suffix で囲まれた文字列をピックアップして、
672         * func で指定の関数を、適用します。
673         * 変換元の文字列に、複数含まれていてもかまいません。
674         *
675         * これは、単純な変数ではなく、${env.XXX} の XXX を環境変数に置き換えたり、
676         * {&#064;DATE.XXXX} を、日付文字列に置き換えたりする場合に、使用できます。
677         * 例えば、環境変数 の置き換えは、
678         * replaceText( orgText , "${env." , "}" , System::getenv );
679         * とします。
680         * 日付関数の置き換えは、
681         * replaceText( orgText , "{&#064;DATE." , "}" , HybsDateUtil::getDateFormat );
682         * とします。
683         * orgTxt , prefix , suffix , func は必須で、null,ゼロ文字列、空白文字等の判定で、
684         * true の場合は、変換元の文字列 をそのまま返します。
685         *
686         * @og.rev 6.9.2.1 (2018/03/12) 新規追加
687         *
688         * @param  orgTxt 変換元の文字列
689         * @param  prefix 変換処理を行うキーワードの先頭文字列
690         * @param  suffix 変換処理を行うキーワードの終了文字列
691         * @param  func 変換処理を行う、関数型インタフェース
692         * @return  置換処理したテキスト
693         */
694        public static String replaceText( final String orgTxt,final String prefix,final String suffix,final UnaryOperator<String> func ) {
695                if( isEmpty( orgTxt,prefix,suffix ) || func == null ) { return orgTxt; }
696
697                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
698
699                // 環境変数の処理
700                int st0 = 0;
701                int st1 = orgTxt.indexOf( prefix );
702                final int preLen = prefix.length() ;
703                final int sufLen = suffix.length() ;
704                while( st1 >= 0 ) {
705                        final int ed = orgTxt.indexOf( suffix , st1 );
706                        if( ed >= 0 ) {
707                                buf.append( orgTxt.substring( st0,st1 ) );
708                                final String key = orgTxt.substring( st1 + preLen , ed );
709                                buf.append( func.apply( key ) );
710
711                                st0 = ed + sufLen ;                                                             // suffix の長さ分
712                                st1 = orgTxt.indexOf( prefix,st0 );
713                        }
714                        else {
715                                final String errMsg = orgTxt + "の、prefix[" + prefix + "] と、suffix[" + suffix + "]の整合性が取れていません。" ;
716                                throw new OgRuntimeException( errMsg );
717                        }
718                }
719
720                return buf.append( orgTxt.substring( st0 ) ).toString();
721        }
722
723        /**
724         * 引数の AA:01 BB:02 CC:03 … 形式の、元値:新値のスペース区切り文字列を元に、
725         * 元値を新値に置き換えます。
726         * これは、部分置換ではなく、完全一致で処理します。
727         * caseStr が null や、マッチしなかった場合は、元の値を返します。
728         * その場合、ignoreCase=true としている場合は、元の文字列 も大文字に変換されて返されます。
729         *
730         * ゼロ文字列を元値や新値で使用することは可能ですが、スペースを使用することはできません。
731         *
732         * @og.rev 5.7.2.3 (2014/01/31) 新規追加
733         *
734         * @param       target          元の文字列
735         * @param       caseStr         置換リスト(AA:01 BB:02 CC:03 … 形式)。null の場合は、比較しない。
736         * @param       ignoreCase      true:大文字として比較 / false:そのまま比較
737         *
738         * @return      元の文字列を置き換えた結果。置換リストに存在しなければ、元の文字列を返す。
739         */
740        public static String caseReplace( final String target,final String caseStr,final boolean ignoreCase ) {
741                if( target == null ) { return target; }
742
743                String rtn = ignoreCase ? target.toUpperCase(Locale.JAPAN) : target ;
744
745                if( caseStr != null ) {
746                        final String caseTmp = " " + caseStr.trim() + " " ;             // CASE文字列の形式をそろえる。
747
748                        final int adrs = caseTmp.indexOf( " " + rtn + ":" );            // 前スペースと後ろコロンで、単語を確定する。
749                        if( adrs >= 0 ) {
750                                final int st = caseTmp.indexOf( ':' , adrs+1 );         // 最初のコロンの位置。元値:新値 の 新値 の取出
751                                final int ed = caseTmp.indexOf( ' ' , st+1 );                   // コロンの次から、最初のスペースの位置
752                                if( st >= 0 && ed >= 0 ) {
753                                        rtn = caseTmp.substring( st+1,ed );                     // コロンの次から、スペースの前までを切り出す。
754                                }
755                        }
756                }
757
758                return rtn ;
759        }
760
761        /**
762         * String型の配列から、カンマ(,)で連結されたString を作成します。
763         * これは,配列を表示用に変換する為のものです。
764         * array2line( array, ",", 0 ); と同等です。
765         *
766         * @param       array           元の文字列配列(可変長引数)
767         *
768         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
769         * @og.rtnNotNull
770         */
771        public static String array2csv( final String... array ) {
772                return array2line( array, ",", 0 );
773        }
774
775        /**
776         * String型の配列から、セパレーターで連結されたString を作成します。
777         * これは,配列を表示用に変換する為のものです。
778         *
779         * @param       array           元の文字列配列
780         * @param       separator       区切り記号
781         *
782         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
783         * @og.rtnNotNull
784         */
785        public static String array2line( final String[] array,final String separator ) {
786                return array2line( array, separator,0 );
787        }
788
789        /**
790         * String型の配列から、セパレーターで連結されたString を作成します。
791         * これは,配列を表示用に変換する為のものです。
792         *
793         * @param       array           元の文字列配列
794         * @param       separator       区切り記号
795         * @param       start           配列の連結開始アドレス
796         *
797         * @return      一列に変換した文字列(引数がnullの場合は、長さ0の文字列を返す)
798         * @og.rtnNotNull
799         */
800        public static String array2line( final String[] array,final String separator,final int start ) {
801                if( array == null || array.length <= start ) { return ""; }
802
803                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
804
805                rtn.append( valueOf( array[start] ) );
806                for( int i=start+1; i<array.length; i++ ) {
807                        rtn.append( separator );
808                        rtn.append( valueOf( array[i] ) );
809                }
810                return rtn.toString();
811        }
812
813        /**
814         * Enumerationから、オブジェクト配列データを返します。
815         * これは,Enumerationを表示用に変換する為のものです。
816         *
817         * @param       enume   元のEnumeration
818         *
819         * @return      オブジェクト配列
820         * @og.rtnNotNull
821         */
822        public static Object[] enume2Array( final Enumeration<?> enume ) {              // 4.3.3.6 (2008/11/15) Generics警告対応
823                if( enume == null || ! enume.hasMoreElements() ) { return new Object[0]; }
824
825                final ArrayList<Object> obj = new ArrayList<>();
826
827                while( enume.hasMoreElements() ) {
828                        obj.add( enume.nextElement() );
829                }
830                return obj.toArray();
831        }
832
833        /**
834         * Enumerationから、オブジェクト配列データを返します。
835         * これは,Enumerationを表示用に変換する為のものです。
836         *
837         * @param       enume   元のEnumeration
838         * @param       objs - 配列が十分な大きさを持つ場合は、Vector の要素が格納される配列。
839         *                      そうでない場合は、要素を格納するために同じ実行時の型の新しい配列が割り当てられる
840         * @return      オブジェクト配列
841         */
842        public static Object[] enume2Array( final Enumeration<?> enume,final Object[] objs ) {  // 4.3.3.6 (2008/11/15) Generics警告対応
843                if( enume == null || ! enume.hasMoreElements() ) { return objs ; }
844
845                final ArrayList<Object> list = new ArrayList<>();
846
847                while( enume.hasMoreElements() ) {
848                        list.add( enume.nextElement() );
849                }
850                return list.toArray( objs );
851        }
852
853        /**
854         * Iteratorから、セパレーターで連結されたString を作成します。
855         * これは,Enumerationを表示用に変換する為のものです。
856         *
857         * @param       ite             元のIterator
858         * @param       separator       区切り記号
859         *
860         * @return      一列に変換した文字列
861         * @og.rtnNotNull
862         */
863        public static String iterator2line( final Iterator<?> ite,final String separator ) {
864                if( ite == null || ! ite.hasNext() ) { return ""; }
865
866                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
867
868                rtn.append( valueOf( ite.next() ) );
869                while( ite.hasNext() ) {
870                        rtn.append( separator );
871                        rtn.append( valueOf( ite.next() ) );
872                }
873                return rtn.toString();
874        }
875
876        /**
877         * カンマ(,)で連結された String を、配列に分解して、その値を返します。
878         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
879         * メニューなりリストを作成するのに便利です。
880         * 要素が空の場合は、必ずカンマの間にスペースを入れて記述してください。
881         * 分割後の文字列の前後のスペースは、削除されます。
882         *
883         * @param       csvData         元のデータ
884         *
885         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
886         * @og.rtnNotNull
887         */
888        public static String[] csv2Array( final String csvData ) {
889                return csv2Array( csvData, ',', 0 );
890        }
891
892        /**
893         * 区切り文字で連結された String を、配列に分解して、その値を返します。
894         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
895         * メニューなりリストを作成するのに便利です。
896         * 連続した区切り文字は、1文字に分割します。
897         * 分割後の文字列の前後のスペースは、削除されます。
898         *
899         * @param       csvData         元のデータ
900         * @param       separator       区切り文字
901         *
902         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
903         * @og.rtnNotNull
904         */
905        public static String[] csv2Array( final String csvData,final char separator ) {
906                return csv2Array( csvData,separator,0 );
907        }
908
909        /**
910         * 区切り文字で連結された String を、配列に分解して、その値を返します。
911         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
912         * メニューなりリストを作成するのに便利です。
913         * 連続した区切り文字は、1文字に分割します。
914         * 分割後の文字列の前後のスペースは、削除されます。
915         * 第3の引数は、リターンする配列の個数を指定します。
916         * len=0 だけは特別で、分解したデータの個数分の配列を作成します。指定の長さが短い場合は、
917         * そこまで分のみ取り込みます。指定の長さが長い場合は、余分に配列を作成します。
918         * データがNULLや、ゼロ文字列の場合は、長さゼロの配列を返します。
919         * セットされる値は、"" です。
920         *
921         * @og.rev 3.8.5.1 (2006/05/08) 設定配列の数を指定できるように変更
922         * @og.rev 3.8.8.2 (2007/01/26) 分割後の値の前後のスペースは削除します。
923         * @og.rev 6.4.5.1 (2016/04/28) CSVTokenizer のインターフェースを、Iterator に変更。
924         * @og.rev 6.8.5.0 (2018/01/09) 引数lenを最大配列長として処理します。
925         *
926         * @param       csvData         元のデータ
927         * @param       separator       区切り文字
928         * @param       len                     指定の最大長さの配列で返します(-1の場合は、オリジナルの長さの配列か、長さゼロの配列)。
929         *
930         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ(len)の配列を返す)
931         * @og.rtnNotNull
932         */
933        public static String[] csv2Array( final String csvData,final char separator, final int len ) {
934//              if( csvData == null || csvData.isEmpty() ) {
935                if( isEmpty( csvData ) ) {                                              // 6.9.2.1 (2018/03/12) isEmpty 置き換え
936                        final String[] rtn = new String[len] ;
937                        Arrays.fill( rtn,"" );
938                        return rtn;
939                }
940
941                final CSVTokenizer token = new CSVTokenizer( csvData,separator );
942
943                final int count = len > 0 ? len : token.countTokens() ;
944
945                final String[] rtn = new String[count];
946                int i = 0;
947                for( ; i<count && token.hasNext() ; i++ ) {
948                        rtn[i] = token.next().trim();   // 3.8.8.2 (2007/01/26)
949                }
950                for( ; i<count; i++ ) {
951                        rtn[i] = "" ;
952                }
953
954                return rtn;
955
956        }
957
958        /**
959         * 区切り文字で連結された String を、配列に分解して、その値を返します。
960         * これは,#csv2Array( String,char,int ) メソッドで、分割時のデータが
961         * ゼロ文字列の場合に、セットする初期値です。
962         * 元のデータがnull、ゼロ文字列の場合は、defVal がセットされた サイズlenの配列を返します。
963         *
964         * データ数が、指定の len より少ない場合、先のメソッドでは、ゼロ文字列を追加していましたが、
965         * ここでは、初期値の defVal をセットします。
966         * また、分解後、trim() されたデータが、ゼロ文字列の場合も、defVal をセットします。
967         *
968         * @og.rev 6.8.5.0 (2018/01/09) CSVTokenizer のインターフェースを、Iterator に変更。
969         *
970         * @param       csvData         元のデータ
971         * @param       separator       区切り文字
972         * @param       len                     指定の長さの配列で返します。
973         * @param       defVal          分割したデータが、ゼロ文字列の場合の初期値
974         *
975         * @return      文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
976         * @og.rtnNotNull
977         */
978        public static String[] csv2Array( final String csvData,final char separator, final int len , final String defVal ) {
979                // 処理の中で対応しても良いが、オリジナルを尊重しておきます。
980                final String[] rtn = csv2Array( csvData,separator,len );
981
982                for( int i=0; i<rtn.length; i++ ) {
983                        if( rtn[i].isEmpty() ) { rtn[i] = defVal ; }
984                }
985
986                return rtn;
987
988        }
989
990        /**
991         * 区切り文字で連結された String を、配列に分解して、その値を返します。
992         * これは,たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
993         * メニューなりリストを作成するのに便利です。
994         * csv2Array と異なり、連続した区切り文字は、分割せずにトークンのみ切り出します。
995         * トークンは、カンマ(,)のみで区切り、その後 trim() により
996         * 前後のスペースを削除します。
997         *
998         * @param       csvData         元のデータ
999         *
1000         * @return      文字列配列
1001         * @og.rtnNotNull
1002         */
1003        public static String[] csv2ArrayOnly( final String csvData ) {
1004//              if( csvData == null || csvData.isEmpty() ) { return new String[0] ; }
1005                if( isEmpty( csvData ) ) { return new String[0]; }                                              // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1006
1007                final StringTokenizer token = new StringTokenizer( csvData,"," );
1008
1009                final ArrayList<String> list = new ArrayList<>();
1010                while( token.hasMoreTokens() ) {
1011                        final String temp = token.nextToken().trim();
1012                        if( temp.length() > 0 ) { list.add( temp ); }
1013                }
1014
1015                return list.toArray( new String[list.size()] );
1016        }
1017
1018        /**
1019         * カンマ(,)、ハイフン(-)で連結された String を、配列に分解して、その値を返す処理のスペシャル版です。
1020         * 0,1,3,5-8,10-* などの数字文字列から、必要な数字をピックアップした数字配列を返します。
1021         * 引数の maxNo は、"*" が指定された場合の、最大の数値です。
1022         * よって、"*" は、単独(1文字)では、0-maxNo を表し、N-* では、N-maxNo を意味します。
1023         * CSV形式で指定される値は、基本的に数字で、重複(1,1,2,2)、逆転(3,2,1)で指定できます。
1024         * 5-3 と指定した場合は、5,4,3 に分解されます。逆順に登録されます。
1025         * 重複削除、昇順並べ替え等が、必要な場合は、取得後の配列を操作してください。
1026         *
1027         * @og.rev 5.5.7.2 (2012/10/09) 新規追加
1028         * @og.rev 6.2.6.0 (2015/06/19) アルファベットの対応を廃止し、数字配列のみサポートします。
1029         *
1030         * @param       csvData 0,1,3,5-8,10-* などのCSV-ハイフン文字列
1031         * @param       maxNo "*" が指定された場合の、最大数
1032         * @return      数字配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
1033         * @og.rtnNotNull
1034         */
1035        public static Integer[] csv2ArrayExt( final String csvData , final int maxNo )  {
1036//              if( csvData == null || csvData.isEmpty() ) { return new Integer[0] ; }
1037                if( isEmpty( csvData ) ) { return new Integer[0]; }                             // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1038
1039                String strData = csvData.replace( "-*" , "-" + maxNo );         // まず、N-* 形式を、N-maxNo に変換します。
1040                strData        = strData.replace( "*"  , "0-" + maxNo );        // その後、"*" 単独(1文字)を、0-maxNo に変換します。
1041
1042                final ArrayList<Integer> noList = new ArrayList<>();
1043
1044                final String[] nos = strData.split( "," );              // カンマで分解。N , N-M , N-* のどれか
1045                for( int i=0; i<nos.length; i++ ) {
1046                        final String sno = nos[i] ;
1047                        final int hai = sno.indexOf( '-' );
1048                        // ハイフンが含まれているときは前後に分解して、間を埋める
1049                        if( hai > 0 ) {
1050                                int       ch1 = Integer.parseInt( sno.substring( 0,hai ) );             // 先頭からハイフンまで
1051                                final int ch2 = Integer.parseInt( sno.substring( hai+1 ) );             // ハイフンから最後まで
1052                                if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( ch1++ ); } }
1053                                else                    { while( ch1 >= ch2 ) { noList.add( ch1-- ); } }
1054
1055                        // また、一文字だけの場合は、アルファベット(a-z,A-Zなど)も指定する事が可能です。
1056                        // アルファベットの場合は、"*" は指定できません。
1057                        //      final String st1 = sno.substring( 0,hai );      // 先頭からハイフンまで
1058                        //      final String st2 = sno.substring( hai+1 );      // ハイフンから最後まで
1059                        //      if( st1.length() == 1 &&  st2.length() == 1 ) {         // ともに1文字の場合は、char化して処理。(英数字処理)
1060                        //              char ch1 = st1.charAt(0);
1061                        //              final char ch2 = st2.charAt(0);
1062                        //              if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++ ) ); } }
1063                        //              else                    { while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
1064                        //      }
1065                        //      else {
1066                        //              int ch1 = Integer.parseInt( st1 );
1067                        //              final int ch2 = Integer.parseInt( st2 );
1068                        //              if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++ ) ); } }
1069                        //              else                    { while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
1070                        //      }
1071                        }
1072                        else {
1073                                noList.add( Integer.valueOf( sno ) );
1074                        }
1075                }
1076                return noList.toArray( new Integer[noList.size()] ) ;
1077        }
1078
1079        /**
1080         * Object 引数の文字列表現を返します。
1081         * これは,String.valueOf とほぼ同じ動作をしますが、引数が null の場合に、
1082         * "null" という文字列を返すのではなく、なにもない文字列 "" を返します。
1083         *
1084         * @param       obj    文字列表現すべき元のオブジェクト
1085         *
1086         * @return      引数が null の場合は、"" に等しい文字列。そうでない場合は、obj.toString() の値
1087         * @og.rtnNotNull
1088         */
1089        public static String valueOf( final Object obj ) {
1090                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
1091                return obj == null ? "" : obj.toString();
1092        }
1093
1094        /**
1095         * HTML上のエスケープ文字を変換します。
1096         *
1097         * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
1098         * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
1099         * セキュリティーホールになる可能性があるので、注意してください。
1100         *
1101         * @og.rev 5.8.2.2 (2014/12/19) アポストロフィの対応
1102         * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。互換性の為のメソッド。
1103         *
1104         * @param       input HTMLエスケープ前の文字列
1105         *
1106         * @return      エスケープ文字に変換後の文字列
1107         * @og.rtnNotNull
1108         */
1109        public static String htmlFilter( final String input ) {
1110                return htmlFilter( input , false );
1111        }
1112
1113        /**
1114         * HTML上のエスケープ文字を変換します。
1115         *
1116         * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
1117         * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
1118         * セキュリティーホールになる可能性があるので、注意してください。
1119         *
1120         * 引数のフラグは、BR→改行コード の変換処理を行うかどうかを指定します。
1121         * true が、変換処理を行うです。
1122         * titleなどのTips表示する場合、改行は、「\n(改行コード)」で行います。
1123         * (HTMLで取り扱うので、&amp;#13;&amp;#10; の方が良いかもしれない。
1124         *  その場合は、エスケープ処理と順番を入れ替えないと、そのまま表示されてしまう。)
1125         * 一方、タグ等で改行を行うには、&lt;BR/&gt; で改行を指定します。
1126         * 改行については、「\n」文字列を指定する事で統一します。
1127         *
1128         * @og.rev 5.8.2.2 (2014/12/19) アポストロフィの対応
1129         * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。
1130         * @og.rev 6.2.5.0 (2015/06/05) htmlフィルターで、BR→改行処理が、引数間違いの為うまくできていなかった。
1131         *
1132         * @param       input HTMLエスケープ前の文字列
1133         * @param       flag  [true:BR変換する/false:BR変換しない]
1134         *
1135         * @return      エスケープ文字に変換後の文字列
1136         * @og.rtnNotNull
1137         */
1138        public static String htmlFilter( final String input , final boolean flag ) {
1139//              if( input == null || input.isEmpty() ) { return ""; }
1140                if( isEmpty( input ) ) { return ""; }                                                   // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1141
1142                String temp = input ;
1143                if( flag ) {
1144                        temp = temp.replaceAll( "<[bB][rR][\\s/]*>" , "\n" );           // <br/> を置き換える。
1145                        temp = temp.replaceAll( "\\\\n"             , "\n" );           //「\n」という文字列を置き換える。
1146                }
1147
1148                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1149                char ch;
1150                for( int i=0; i<temp.length(); i++ ) {
1151                        ch = temp.charAt(i);            // 6.2.5.0 (2015/06/05) バグ
1152                        switch( ch ) {
1153                                case '<'  : rtn.append("&lt;");         break;
1154                                case '>'  : rtn.append("&gt;");         break;
1155                                case '"'  : rtn.append("&quot;");       break;
1156                                case '\'' : rtn.append("&apos;");       break;          // 5.8.2.2 (2014/12/19) アポストロフィの対応
1157                                case '&'  : rtn.append("&amp;");        break;
1158                                default   : rtn.append(ch);                     break;          // 6.0.2.5 (2014/10/31) break追記
1159                        }
1160                }
1161                return rtn.toString() ;
1162        }
1163
1164        /**
1165         * 「\n」という文字列を、BRタグに変換します。
1166         *
1167         * titleなどのTips表示する場合、改行は、「\n」で行います。
1168         * 一方、タグ等で改行を行うには、&lt;BR/&gt; で改行を指定します。
1169         * BRタグは、リソーステーブル等に書き込みにくい為、また、本当の改行コードも
1170         * 書き込みにくい為、改行については、「\n」文字列を指定する事で対応できるように
1171         * 統一します。
1172         *
1173         * @og.rev 6.2.2.3 (2015/04/10) 「\n」という文字列を、BRタグに変換する処理を追加
1174         *
1175         * @param       input BR,\n変換前の文字列
1176         *
1177         * @return      変換後の文字列
1178         * @og.rtnNotNull
1179         */
1180        public static String yenN2br( final String input ) {
1181                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
1182//              return input == null || input.isEmpty() ? "" : input.replaceAll( "\\\\n" , "<br/>" );   // \n ではなく、「\n」という文字列と変換
1183                return  isEmpty( input ) ? "" : input.replaceAll( "\\\\n" , "<br/>" );                                  // \n ではなく、「\n」という文字列と変換        // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1184        }
1185
1186        /**
1187         * JavaScript 等の引数でのクオート文字をASCII変換します。
1188         *
1189         * JavaScript の引数の値に、ダブルクオート(")、シングルクオート(')が
1190         * 含まれると、文字列を表す為に前後に指定しているクオートと混乱し、
1191         * データを表現できないケースがあります。その場合には、クオート文字を
1192         * ASCII文字に置き換える事で、指定の文字を渡すことが可能になります。
1193         * ここでは、引数文字列に、ダブルクオート(")、シングルクオート(')が、
1194         * 含まれると、それぞれ、ASCII コード(¥x22、¥x27)に置き換えます。
1195         * なお、null は、ゼロ文字列に変換して返します。
1196         *
1197         * @param       input 入力文字列
1198         *
1199         * @return      クオート文字をASCII文字に置き換えた文字列
1200         * @og.rtnNotNull
1201         */
1202        public static String quoteFilter( final String input ) {
1203//              if( input == null || input.isEmpty() ) { return ""; }
1204                if( isEmpty( input ) ) { return ""; }                                                   // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1205                if( input.indexOf( '\'' ) < 0 && input.indexOf( '"' ) < 0 ) { return input; }
1206
1207                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1208                char ch;
1209                for( int i=0; i<input.length(); i++ ) {
1210                        ch = input.charAt(i);
1211                        switch( ch ) {
1212                                case '"'  : rtn.append( "\\x22" );      break;
1213                                case '\'' : rtn.append( "\\x27" );      break;
1214                                default   : rtn.append( ch );           break;          // 6.0.2.5 (2014/10/31) break追記
1215                        }
1216                }
1217                return rtn.toString() ;
1218        }
1219
1220        /**
1221         * JSON形式で出力する場合のためのエスケープ処理です。
1222         *
1223         * @og.rev 5.9.6.4(2016/03/25) 新規作成
1224         *
1225         * @param       input XMLエスケープ前の文字列
1226         *
1227         * @return      エスケープ文字に変換後の文字列
1228         */
1229        public static String jsonFilter( final String input ) {
1230//              if( input == null || input.length() == 0 ) { return ""; }
1231                if( isEmpty( input ) ) { return ""; }                                                   // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1232
1233                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1234                for(int i=0; i<input.length(); i++) {
1235                        final char ch = input.charAt(i);
1236                        switch( ch ) {
1237                                case '"'        : rtn.append("\\\"");   break;
1238                                case '\\'       : rtn.append("\\\\");   break;
1239                                case '/'        : rtn.append("\\/");    break;
1240                                case '\b'       : rtn.append("\\b");    break;
1241                                case '\f'       : rtn.append("\\f");    break;
1242                                case '\n'       : rtn.append("\\n");    break;
1243                                case '\r'       : rtn.append("\\r");    break;
1244                                case '\t'       : rtn.append("\\t");    break;
1245                                default         : rtn.append(ch);               break;
1246                        }
1247                }
1248                return rtn.toString() ;
1249        }
1250
1251        /**
1252         * 所定のキャラクタコードを取り除いた文字列を作成します。
1253         *
1254         * 実現したい機能は、String#replace( 'x','' ) 的な表現です。
1255         * つまり、指定のキャラクタを取り除きたいのですが、上記コマンドでは、
1256         * コンパイル時にエラーが発生します。
1257         * 取り除きたいキャラクタコードが存在しない場合は、指定の文字列を
1258         * そのまま返します。
1259         *
1260         * @param       value 処理対象の文字列
1261         * @param       ch 取り除きたいキャラクタ
1262         *
1263         * @return      処理後の文字列
1264         */
1265        public static String deleteChar( final String value,final char ch ) {
1266                if( value == null || value.indexOf( ch ) < 0 ) { return value; }
1267                char[] chs = value.toCharArray() ;
1268                int j=0;
1269                for( int i=0;i<chs.length; i++ ) {
1270                        // 6.3.9.0 (2015/11/06) true/false を変更します。
1271                        if( chs[i] != ch ) { chs[j++] = chs[i]; }
1272                }
1273                return String.valueOf( chs,0,j );
1274        }
1275
1276        /**
1277         * 文字列に含まれる、特定の文字の個数をカウントして返します。
1278         *
1279         * @og.rev 5.2.0.0 (2010/09/01)
1280         *
1281         * @param       value 処理対象の文字列
1282         * @param       ch カウントする文字
1283         *
1284         * @return      カウント数
1285         */
1286        public static int countChar( final String value,final char ch ) {
1287                if( value == null || value.indexOf( ch ) < 0 ) { return 0; }
1288                final char[] chs = value.toCharArray() ;
1289                int cnt=0;
1290                for( int i=0;i<chs.length; i++ ) {
1291                        if( chs[i] == ch ) { cnt++; }
1292                }
1293                return cnt;
1294        }
1295
1296        /**
1297         * CODE39 の 文字列を作成します。
1298         *
1299         * CODE39 は、『0~9, A~Z,-,・, ,$,/,+,%』のコードが使用できる
1300         * バーコードの体系です。通常 * で始まり * で終了します。
1301         * また、チェックデジット に、モジュラス43 が使われます。
1302         * ここでは、指定の文字列の前後に、* を付与し、必要であれば
1303         * チェックデジットも付与します。
1304         * 指定の入力文字列には、* を付けないでください。
1305         *
1306         * @param       value 処理対象の文字列
1307         * @param       checkDigit チェックデジットの付与(true:付ける/false:付けない)
1308         *
1309         * @return      処理後の文字列
1310         * @og.rtnNotNull
1311         */
1312        public static String code39( final String value,final boolean checkDigit ) {
1313                final String rtn = ( value == null ) ? "" : value ;
1314                if( ! checkDigit ) { return "*" + rtn + "*"; }
1315
1316                int kei = 0;
1317                int cd;
1318                for( int i=0; i<rtn.length(); i++ ) {
1319                        cd = MODULUS_43.indexOf( rtn.charAt(i) );
1320                        if( cd < 0 ) {
1321                                final String errMsg = "指定の文字中に、CODE39 規定外文字が使用されています。[" + rtn.charAt(i) + "]" ;
1322                                throw new OgRuntimeException( errMsg );
1323                        }
1324                        kei += cd ;
1325                }
1326                final char digit = MODULUS_43.charAt( kei % 43 );
1327
1328                return "*" + rtn + digit + "*" ;
1329        }
1330
1331        /**
1332         * 引数 inStr が、null または、ゼロ文字列の場合は、デフォルト値 def を返します。
1333         * もちろん、inStr も def も null の場合は、null を返します。
1334         *
1335         * ※ 影響範囲が大きいので、空白文字の判定は入れません。
1336         *
1337         * @param    inStr 基準となる文字列
1338         * @param    def デフォルト文字列
1339         *
1340         * @return   引数 inStr が、null または、ゼロ文字列の場合は、デフォルト値を返す。
1341         */
1342        public static String nval( final String inStr,final String def ) {
1343//              return inStr == null || inStr.isEmpty() ? def : inStr ;
1344                return isEmpty( inStr ) ? def : inStr ;                                                 // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1345        }
1346
1347        /**
1348         * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1349         *
1350         * 数値変換なので、空白文字列の場合も、デフォルト値を使用します。
1351         *
1352         * @param    inStr 基準となる文字列
1353         * @param    def デフォルト数字
1354         *
1355         * @return   引数 inStr を変換した数字(int)。変換できない場合は デフォルト値 def
1356         */
1357        public static int nval( final String inStr,final int def ) {
1358//              return inStr == null || inStr.isEmpty() ? def : Integer.parseInt( inStr ) ;
1359                return isNull( inStr ) ? def : Integer.parseInt( inStr ) ;              // 6.9.2.1 (2018/03/12) isNull 置き換え
1360        }
1361
1362        /**
1363         * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1364         *
1365         * @param    inStr 基準となる文字列
1366         * @param    def デフォルト数字
1367         *
1368         * @return   引数 inStr を変換した数字(long)。変換できない場合は デフォルト値 def
1369         */
1370        public static long nval( final String inStr,final long def ) {
1371//              return inStr == null || inStr.isEmpty() ? def : Long.parseLong( inStr ) ;
1372                return isNull( inStr ) ? def : Long.parseLong( inStr ) ;                        // 6.9.2.1 (2018/03/12) isNull 置き換え
1373        }
1374
1375        /**
1376         * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1377         *
1378         * @og.rev 6.9.2.1 (2018/03/12) 新規追加
1379         *
1380         * @param    inStr 基準となる文字列
1381         * @param    def デフォルト数字
1382         *
1383         * @return   引数 inStr を変換した数字(double)。変換できない場合は デフォルト値 def
1384         */
1385        public static double nval( final String inStr,final double def ) {
1386                return isNull( inStr ) ? def : Double.parseDouble( inStr ) ;            // 6.9.2.1 (2018/03/12) isNull 置き換え
1387        }
1388
1389        /**
1390         * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1391         * 通常は、"true" または、 "TRUE" 文字列を、論理値の true に変換します。
1392         * ただし、文字列長が 1文字の場合のみ、"0" 以外を true に変換します。
1393         *
1394         * @og.rev 6.8.0.1 (2017/06/30) つづり間違いに対応するため、厳密にチェックします。
1395         *
1396         * @param    inStr 基準となる文字列
1397         * @param    def デフォルト論理値
1398         *
1399         * @return   引数 inStr を変換した論理値。変換できない場合は デフォルト値 def
1400         */
1401        public static boolean nval( final String inStr,final boolean def ) {
1402                // 6.8.0.1 (2017/06/30) つづり間違いに対応するため、厳密にチェックします。
1403                if( inStr != null && inStr.length() > 1 && !"true".equalsIgnoreCase( inStr ) && !"false".equalsIgnoreCase( inStr ) ) {
1404                        final String errMsg = "指定の文字列には、true か、false を指定してください。[" + inStr + "]" ;
1405                        throw new OgRuntimeException( errMsg );
1406                }
1407
1408                // 6.4.1.1 (2016/01/16) PMD refactoring.
1409//              return inStr == null || inStr.isEmpty()
1410                return isNull( inStr )                                                                                  // 6.9.2.1 (2018/03/12) isNull 置き換え
1411                                        ? def
1412                                        : inStr.length() == 1
1413                                                ? ! "0".equals( inStr )
1414                                                : "true".equalsIgnoreCase( inStr ) ;
1415
1416        }
1417
1418        /**
1419         * 引数 inStr が、null、"_"、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1420         *
1421         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1422         * ※ #nval(String) との整合性を取るため、空白文字の判定は入れません。
1423         *
1424         * @og.rev 5.2.2.0 (2010/11/01) "_" の取り扱い変更
1425         *
1426         * @param    inStr 基準となる文字列
1427         * @param    def デフォルト文字列
1428         *
1429         * @return  null、ゼロ文字列、"_"の場合は、デフォルト文字列を、そうでなければ、入力文字を返す。
1430         */
1431        public static String nval2( final String inStr,final String def ) {
1432//              return inStr == null || inStr.isEmpty() || "_".equals( inStr ) ? def : inStr.intern() ;
1433                return isEmpty( inStr ) || "_".equals( inStr ) ? def : inStr.intern() ;         // 6.9.2.1 (2018/03/12) isNull 置き換え
1434        }
1435
1436        /**
1437         * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は、デフォルト値 def を返します。
1438         * ただし、NULL代替文字(_)は デフォルト値 def2 に置き換えます。
1439         *
1440         * さらに、メモリ領域を節約する為、intern() の結果を返します。
1441         * ※ #nval(String) との整合性を取るため、空白文字の判定は入れません。
1442         *
1443         * @og.rev 5.2.2.0 (2010/11/01) "_" の取り扱い変更
1444         *
1445         * @param    inStr 基準となる文字列
1446         * @param    def デフォルト文字列
1447         * @param    def2 NULL代替文字(_)の場合のデフォルト文字列
1448         *
1449         * @return  null、ゼロ文字列の場合は、def1文字列を、"_"の場合は、def2文字列を、そうでなければ、入力文字を返す。
1450         */
1451        public static String nval2( final String inStr,final String def,final String def2 ) {
1452//              return inStr == null || inStr.isEmpty() ? def : "_".equals( inStr ) ? def2 : inStr.intern() ;
1453                return isEmpty( inStr ) ? def : "_".equals( inStr ) ? def2 : inStr.intern() ;           // 6.9.2.1 (2018/03/12) isNull 置き換え
1454        }
1455
1456        /**
1457         * 指定のCharSequence同士を連結させます。
1458         * CharSequenceが、 null の場合は、連結しません。
1459         * すべてが null の場合は、ゼロ文字列が返されます。
1460         *
1461         * ここでは、空白文字やタブ、改行コードも、指定されていれば、連結されます。
1462         *
1463         * @og.rev 6.0.2.4 (2014/10/17) 新規追加
1464         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
1465         *
1466         * @param       strs... 可変長CharSequence
1467         *
1468         * @return      null以外の文字列が連結された状態
1469         * @see         #join( String,CharSequence... )
1470         * @og.rtnNotNull
1471         */
1472        public static String nvalAdd( final CharSequence... strs ) {
1473                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
1474
1475                for( final CharSequence str : strs ) {
1476                        if( str != null ) { buf.append( str ); }
1477                }
1478
1479                return buf.toString();
1480        }
1481
1482        /**
1483         * 最初の null(または、ゼロ文字列) 以外の値を返します。
1484         * nval の 変数が、無制限版です。
1485         * すべてが null(または、ゼロ文字列) の場合は、null が返されます。
1486         * 空白文字、タブや改行コードが来ても、返されます。
1487         *
1488         * @og.rev 6.0.2.4 (2014/10/17) 新規追加
1489         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
1490         *
1491         * @param       strs... 可変長CharSequence
1492         *
1493         * @return      最初に現れた、null以外のCharSequenceを、Stringに変換したもの
1494         */
1495//      public static CharSequence coalesce( final CharSequence... strs ) {
1496        public static String coalesce( final CharSequence... strs ) {
1497                for( final CharSequence str : strs ) {
1498//                      if( str != null && str.length() > 0 ) { return str.toString(); }
1499                        if( ! isEmpty( str ) ) { return str.toString(); }                               // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1500                }
1501
1502                return null;
1503        }
1504
1505        /**
1506         * キーワードに対して、可変長引数の文字列が、含まれているかどうかを判定します。
1507         * キーワードが、null でなく、比較先の文字列が、ひとつでも含まれると、true が返ります。
1508         * 大文字小文字は、厳密に判定されます。
1509         *
1510         * key != null &amp;&amp; ( key.contains( val1 ) || key.contains( val2 ) ・・・ )
1511         * の結果と同じです。
1512         *
1513         * @og.rev 6.4.4.2 (2016/04/01) contains 判定を行う新しいメソッドを新規追加します。
1514         *
1515         * @param       key     キーワード
1516         * @param       vals... 比較先の可変長文字列(OR判定)
1517         *
1518         * @return      キーワード文字列の中に、比較先文字列がひとつでも含まれると、true
1519         */
1520        public static boolean contains( final String key , final String... vals ) {
1521                if( key != null && vals != null ) {
1522                        for( final String val : vals ) {
1523                                if( val != null && key.contains( val ) ) { return true; }       // ひとつでも、contains があれば、true
1524                        }
1525                }
1526                return false;
1527        }
1528
1529        /**
1530         * 連結文字列を、使用して、可変長引数のCharSequenceを連結して返します。
1531         * 連結文字列が、null の場合は、CharSequenceをそのまま連結していきます。
1532         * 連結する文字列が null の場合は、連結しません。
1533         * 連結文字列は、一番最後は出力されません。
1534         * 処理できない場合は、長さゼロの文字列を返します。
1535         *
1536         * @og.rev 6.4.4.2 (2016/04/01) join 処理を行う新しいメソッドを新規追加します。
1537         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
1538         *
1539         * @param       delimiter       連結文字列
1540         * @param       vals... 連結するCharSequence
1541         *
1542         * @return      連結された結果の文字列
1543         * @see         #nvalAdd( CharSequence... )
1544         * @og.rtnNotNull
1545         */
1546        public static String join( final String delimiter , final CharSequence... vals ) {
1547                if( delimiter == null ) { return nvalAdd( vals ); }
1548
1549                final StringJoiner sjo = new StringJoiner( delimiter );
1550                for( final CharSequence val : vals ) {
1551//                      if( val != null && val.length() > 0 ) { sjo.add( val ); }
1552                        if( ! isEmpty( val ) ) { sjo.add( val ); }                                              // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1553                }
1554
1555                return sjo.toString();
1556        }
1557
1558        /**
1559         * 引数 vals が、null または、ゼロ文字列の場合は、true を返します。
1560         * それ以外は false を返します。
1561         * isNull との違いは、スペースやタブ、改行だけの文字列は、null と判定しません。
1562         * 文字列置換などで、スペースやタブなどと置換する場合、null やゼロ文字列では困る場合などの
1563         * 判定で使用します。
1564         *
1565         * @og.rev 6.9.2.1 (2018/03/12) 新規追加
1566         *
1567         * @param    vals 判定するCharSequence(可変長引数)
1568         *
1569         * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
1570         * @see         #isNull( CharSequence... )
1571         */
1572        public static boolean isEmpty( final CharSequence... vals ) {
1573                if( vals != null && vals.length > 0 ) {
1574                        for( final CharSequence val : vals ) {
1575                                if( val == null || val.length()==0 ) { return true; }   // val の nullチェック 必要?
1576                        }
1577                        return false;
1578                }
1579                return true;
1580        }
1581
1582        /**
1583         * 引数 vals が、null または、ゼロ文字列、またはすべて空白文字(スペース、タブ、改行)の場合は、true を返します。
1584         * それ以外は false を返します。
1585         * isNull との違いは、判定前に、trim() 処理を行っているため、スペースやタブ、改行だけの文字列も、null と判定します。
1586         * キーワードとして使用できないケースで、この判定を利用します。
1587         * また、引数は可変長になっており、指定の「どれか」が、成立すれば、true と判定します。
1588         * 処理的には、 val1 == null || val1.trim().length()==0 || val2 == null || val2.trim().length()==0 ・・・ 
1589         *
1590         * CharSequence 系の配列自体の null チェックも兼ねています。
1591         *
1592         * 注意は、オールスペースやタブ文字、改行文字も true になります。
1593         *
1594         * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
1595         * @og.rev 6.9.0.0 (2018/01/31) 引数を可変長引数に変更
1596         * @og.rev 6.9.2.1 (2018/03/12) 新規追加
1597         *
1598         * @param    vals 判定するCharSequence(可変長引数)
1599         *
1600         * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
1601         * @see         #isNotNull( CharSequence... )
1602         */
1603        public static boolean isNull( final CharSequence... vals ) {
1604                // 6.9.0.0 (2018/01/31) 引数を可変長引数に変更
1605                if( vals != null && vals.length > 0 ) {
1606                        for( final CharSequence val : vals ) {
1607                                if( val == null || val.length()==0 ) { return true; }   // val の nullチェック 必要?
1608
1609                                boolean flag = true;
1610                                // String.trim().isEmpty() の高速版
1611                                for( int i=0; i<val.length(); i++ ) {
1612                                        if( !Character.isWhitespace( val.charAt(i) ) ) {        // 空白文字でなければ
1613                                                flag = false;                                                                   // 小ループを抜ける。
1614                                                break;
1615                                        }
1616                                }
1617                                if( flag ) { return true; }                                                             // すべてが空白文字なら、true
1618                        }
1619                        return false;
1620                }
1621                return true;
1622        }
1623
1624        /**
1625         * 引数 vals が、null または、ゼロ文字列、またはすべて空白文字(スペース、タブ、改行)でない場合は、true を返します。
1626         * 
1627         * #isNull( CharSequence... ) の反転です。
1628         * そのため、「すべて」の文字列が、null か、ゼロ文字、空白文字でない場合のみ、true になります。
1629         * isNull の表示上、'!' マークのあるなしは、判別しにくいため、メソッドを用意しています。
1630         *
1631         * @og.rev 6.9.2.1 (2018/03/12) 新規追加
1632         *
1633         * @param    vals 判定するCharSequence(可変長引数)
1634         *
1635         * @return  NULL文字列関係でない場合は、true を、「どれか」が、NULL文字列関係の場合は、false を返す。
1636         * @see         #isNull( CharSequence... )
1637         */
1638        public static boolean isNotNull( final CharSequence... vals ) {
1639                return !isNull( vals );
1640        }
1641
1642        /**
1643         * 浮動小数点数について、カンマ編集を行います。
1644         *
1645         * このメソッドでは、1.23 E12 などの数字は扱いません。通常の
1646         * 数字とピリオドで構成された文字列のみ、変換対象になります。
1647         * (ただし、不正な文字列を与えてもエラーチェックはしていません。)
1648         * minFraction には、小数点部に与える固定値を指定します。入力文字列が
1649         * その桁数より少ない場合は、0埋めします。
1650         * 多い場合は、四捨五入します。
1651         * minFraction が 0 の場合は、小数点は付きません。
1652         * ".12" などの小数点は、必ず先頭に 0 が付きます。
1653         * 入力文字列が null か、ゼロ文字列時は、そのまま入力データを返します。
1654         *
1655         * <pre>
1656         *      DecimalFormat format = new DecimalFormat( "#,##0.00########" );
1657         *      double dd = Double.parseDouble( val );
1658         *      return format.format( dd );
1659         * </pre>
1660         * に対して、minFraction分の小数以下のゼロの指定と、inに ',' が
1661         * 含まれた処理を追加した感じになります。
1662         *
1663         * @og.rev 4.0.0.0 (2007/10/26) 空白のトリム処理を追加
1664         * @og.rev 6.0.4.0 (2014/11/28) 小数点指定が、0 の場合、小数点以下は表示しない
1665         * @og.rev 6.2.0.0 (2015/02/27) 小数点指定の精度に合わせるのと、内部ロジック完全置換
1666         * @og.rev 6.2.0.1 (2015/03/06) 互換性の関係で、nullかゼロ文字列の時は、そのまま、in を返す。
1667         * @og.rev 6.3.6.1 (2015/08/28) throw new OgRuntimeException するのではなく、System.err.println する。
1668         * @og.rev 6.3.8.5 (2015/10/16) ogErrMsgPrint 使用。
1669         * @og.rev 6.4.2.0 (2016/01/29) ogErrMsgPrint メソッドを、ThrowUtil クラスに移動のため、修正
1670         *
1671         * @param       inStr                   変換元の文字列
1672         * @param       minFraction     変換時の小数点以下の固定桁数
1673         *
1674         * @return      カンマ編集後の数字型文字列
1675         */
1676        public static String numberFormat( final String inStr, final int minFraction ) {
1677//              if( inStr == null || inStr.isEmpty() ) { return inStr ; }               // 6.2.0.1 (2015/03/06) 互換性の関係
1678                if( isNull( inStr ) ) { return inStr ; }                                                // 6.9.2.1 (2018/03/12) isNull 置き換え
1679
1680                String rtn = inStr;
1681
1682                try {
1683                        final double dd = StringUtil.parseDouble( rtn );
1684
1685                        if( FMT1.length > minFraction ) {
1686                                synchronized( FMT1[minFraction] ) {
1687                                        rtn = FMT1[minFraction].format( dd );
1688                                }
1689                        }
1690                        else {
1691                                final String fmt = "#,##0." + ZERO.substring( 0,minFraction );
1692                                rtn = new DecimalFormat( fmt ).format( dd );
1693                        }
1694                }
1695                catch( final Throwable th ) {
1696                        final String errMsg = "ERROR:" + th.getLocalizedMessage() + CR
1697                                                        + " in=[" + inStr + "] , minFraction=[" + minFraction + "]" ;
1698                        // 6.3.8.5 (2015/10/16) ogErrMsgPrint 使用。
1699                        System.err.println( ThrowUtil.ogThrowMsg( errMsg,th ) );                                // 6.4.2.0 (2016/01/29)
1700                }
1701
1702                return rtn;
1703        }
1704
1705        /**
1706         * 識別id に応じた オブジェクトを作成します。
1707         * 作成するには、デフォルトコンストラクターが必要です。
1708         *
1709         * @param       cls 作成するクラスのフルネーム
1710         *
1711         * @return      オブジェクト
1712         * @og.rtnNotNull
1713         * @throws RuntimeException 何らかのエラーが発生した場合
1714         */
1715        public static Object newInstance( final String cls ) {
1716                return newInstance( cls,Thread.currentThread().getContextClassLoader() );
1717        }
1718
1719        /**
1720         * 指定されたクラスローダを使って、識別id に応じた オブジェクトを作成します。
1721         * 作成するには、デフォルトコンストラクターが必要です。
1722         * initialize パラメータは true 相当(それまでに初期化されていない場合だけ初期化)です。
1723         *
1724         * @og.rev 6.4.3.3 (2016/03/04) リフレクション系の例外の共通クラスに置き換えます。
1725         * @og.rev 6.8.2.3 (2017/11/10) java9対応(cls.newInstance() → cls.getDeclaredConstructor().newInstance())
1726         *
1727         * @param       cls             作成するクラスのフルネーム
1728         * @param       loader  作成するクラスのクラスローダ
1729         *
1730         * @return      オブジェクト
1731         * @og.rtnNotNull
1732         * @throws RuntimeException 何らかのエラーが発生した場合
1733         */
1734        public static Object newInstance( final String cls,final ClassLoader loader ) {
1735                try {
1736                        return Class.forName( cls,true,loader ).getDeclaredConstructor().newInstance();                 // 6.8.2.3 (2017/11/10)
1737                }
1738                catch( final NoSuchMethodException | InvocationTargetException ex ) {                                           // 6.8.2.3 (2017/11/10)
1739                        final String errMsg = "指定のメソッド(コンストラクタ)が見つかりませんでした。class=[" + cls + "]" + CR
1740                                                + ex.getMessage();
1741                        throw new OgRuntimeException( errMsg,ex );
1742                }
1743                catch( final ReflectiveOperationException ex ) {
1744                        final String errMsg = "Class.forName( String,boolean,ClassLoader ).newInstance() 処理に失敗しました class=[" + cls + "]" + CR
1745                                                + ex.getMessage() ;
1746                        throw new OgRuntimeException( errMsg,ex );
1747                }
1748        }
1749
1750        /**
1751         * 指定のURL文字列同士を連結させます。
1752         * そのとき、後方URLが、絶対パスの場合は、連結せず 後方URLを返します。
1753         * 第2引数以降は、絶対パス判定をせず直前のURLの末尾判定のみで連結します。
1754         *
1755         * 絶対パスかどうかは、通常のファイル属性と同様に、先頭が、'/' (UNIX)または、
1756         * 2文字目が、":" (Windows)の場合、または、先頭が "\" (ネットワークパス)で
1757         * 始まる場合で判断します。
1758         * 連結時に、前方URLの末尾に "/" を付加します。
1759         *
1760         * 処理の互換性確保のため、第3引数の可変長引数を追加しています。
1761         *
1762         * @og.rev 5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
1763         * @og.rev 5.6.5.2 (2013/06/21) 第3引数を可変長引数に変更
1764         * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
1765         * @og.rev 6.4.7.2 (2016/06/20) 絶対パスの判定を、可変長URLにも適用する。
1766         *
1767         * @param       url1 先頭URLCharSequence
1768         * @param       urls 後方URL可変長CharSequence(絶対パスの場合は、返り値)
1769         *
1770         * @return      URL文字列同士の連結結果 url1 + url2(url2が絶対パスの場合は、url2から連結開始)
1771         * @og.rtnNotNull
1772         */
1773        public static String urlAppend( final CharSequence url1,final CharSequence... urls ) {
1774                final StringBuilder rtnUrl = new StringBuilder( BUFFER_MIDDLE );
1775
1776//              if( url1 != null && url1.length() > 0 ) { rtnUrl.append( url1 ) ; }
1777                if( isNotNull( url1 ) ) { rtnUrl.append( url1 ) ; }                                     // 6.9.2.1 (2018/03/12) isNotNull 置き換え
1778
1779                // ここからが、追加分
1780                for( final CharSequence url : urls ) {
1781//                      if( url != null && url.length() > 0 ) {
1782                        if( isNotNull( url ) ) {                                                                                // 6.9.2.1 (2018/03/12) isNotNull 置き換え
1783                                if( rtnUrl.length() == 0 ||                                                                     // 戻り値が未設定の場合。
1784                                         ( url.charAt(0) == '/'  ) ||                                                   // 実ディレクトリが UNIX
1785                                         ( url.length() > 1 && url.charAt(1) == ':' ) ||                // 実ディレクトリが Windows
1786                                         ( url.charAt(0) == '\\' ) ) {                                                  // 実ディレクトリが ネットワークパス
1787                                                rtnUrl.setLength( 0 );                          // クリア
1788                                                rtnUrl.append( url ) ;
1789                                }
1790                                else {
1791                                        final char ch = rtnUrl.charAt( rtnUrl.length()-1 ) ;    // 必ず、何らかのURLがappend済みのはず。
1792                                        if( ch == '/' || ch == '\\' ) {
1793                                                rtnUrl.append( url ) ;
1794                                        }
1795                                        else {
1796                                                rtnUrl.append( '/' ).append( url ) ;                            // 6.0.2.5 (2014/10/31) char を append する。
1797                                        }
1798                                }
1799                        }
1800                }
1801
1802                return rtnUrl.toString() ;
1803        }
1804
1805        /**
1806         * Unicode文字列の値を HTML のエスケープ記号(&amp;#xZZZZ;)に変換します。
1807         *
1808         * SJIS(JA16SJIS) で作成されたデータベースに、(NVARCHAR2)を使用して中国語等を登録するのは
1809         * 非常に複雑でかつ、リスクが大きい処理になります。
1810         * ORACLE殿でも、自信を持っては勧められない機能とのコメントを頂いています。
1811         * そこで、HTMLでのエスケープ文字を使用して、Unicodeを文字列化して登録する為の
1812         * DBType として、新規に作成します。
1813         * ここでは、入力文字を、キャラクタ(char)型に分解し、(&amp;#xZZZZ;)に変換していきます。
1814         * よって、通常に1文字(Shift-JISで2Byte,UTF-8で3Byte)が、8Byteになります。
1815         * この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為、
1816         * 通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。
1817         * ここでは、2バイト文字のみ、変換しています。
1818         *
1819         * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
1820         *
1821         * @param       value 変換前のCharSequence
1822         *
1823         * @return      HTMLのエスケープ記号(&amp;#xZZZZ;)
1824         * @og.rtnNotNull
1825         */
1826        public static String getUnicodeEscape( final CharSequence value ) {
1827//              if( value == null || value.length() == 0 ) { return ""; }
1828                if( isEmpty( value ) ) { return ""; }                                   // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1829
1830                final StringBuilder rtn = new StringBuilder( value.length() * 4 );
1831
1832                for( int i=0; i<value.length(); i++ ) {
1833                        final char ch = value.charAt(i);
1834
1835                        if( ch > 0xff ) {
1836                                final String hex = Integer.toHexString( (int)ch ) ;
1837                                rtn.append( UTF_STR[hex.length()] ).append( hex ).append( ';' );                // 6.0.2.5 (2014/10/31) char を append する。
1838                        }
1839                        else {
1840                                rtn.append( ch );
1841                        }
1842                }
1843
1844                return rtn.toString();
1845        }
1846
1847        /**
1848         * HTML のエスケープ記号(&amp;#xZZZZ;)をUnicode文字列に戻します。
1849         *
1850         * HTMLでのエスケープ文字を使用して登録された文字を、Unicodeに戻します。
1851         * (&amp;#xZZZZ;)の8Byteを、もとのキャラクタコードに戻し、合成します。
1852         * ここでは、通常の文字列に混在したエスケープ文字も戻せるようにします。
1853         * 
1854         * @og.rev 5.9.5.3 (2016/02/26) 無限ループ対応
1855         *
1856         * @param       value   HTMLのエスケープ記号(&amp;#xZZZZ;)を含む文字列
1857         *
1858         * @return      通常のUnicode文字列
1859         * @og.rtnNotNull
1860         */
1861        public static String getReplaceEscape( final String value ) {
1862//              if( value == null || value.isEmpty() ) { return ""; }
1863                if( isEmpty( value ) ) { return ""; }                                   // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1864
1865                final StringBuilder rtn = new StringBuilder( value );
1866
1867                int st = rtn.indexOf( "&#" );
1868                while( st >= 0 ) {
1869                        if( st+7 < rtn.length() && rtn.charAt( st+7 ) == ';' ) {
1870                                final int ch = Integer.parseInt( rtn.substring( st+3,st+7 ),16 );
1871                                rtn.replace( st,st+8, Character.toString( (char)ch ) );
1872                        }
1873                        st = rtn.indexOf( "&#",st + 1 ); // 5.9.5.3 (2016/02/26) 無限ループ対応 
1874                }
1875
1876                return rtn.toString();
1877        }
1878
1879        /**
1880         * 文字列をdoubleに変換します。
1881         *
1882         * これは、Double.parseDouble( value ) と、ほぼ同じ動作を行います。
1883         * 内部的には、引数の カンマ(,) を削除した文字列を、Double.parseDouble( value )
1884         * に渡します。
1885         * また、引数が、null,ゼロ文字列,'_' の時には、0.0 を返します。
1886         *
1887         * @og.rev 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。)
1888         *
1889         * @param       value   doubleに変換する元の文字列
1890         *
1891         * @return      変換後のdouble数値
1892         */
1893        public static double parseDouble( final String value ) {
1894                double rtn ;
1895
1896//              if( value == null || value.isEmpty() || value.equals( "_" ) ) {
1897                if( isNull( value ) || value.equals( "_" ) ) {                                  // 6.9.2.1 (2018/03/12) isNull 置き換え
1898                        rtn = 0.0d;
1899                }
1900                else if( value.indexOf( ',' ) < 0 ) {
1901                        rtn = Double.parseDouble( value );
1902                }
1903                else {
1904                        // 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。)
1905                        rtn = Double.parseDouble( value.replaceAll( ",","" ) );
1906                }
1907
1908                return rtn ;
1909        }
1910
1911        /**
1912         * 引数からspanタグを取り除いて返します。
1913         *
1914         * 引数が、&lt;span ・・・&gt;YYYY&lt;/span&gt;形式の場合、YYYY のみ出力します。
1915         * この処理では、先頭にspan が一つだけある場合、削除します。
1916         * 複数の span や、div などを削除する場合は、#tagCut(String) メソッドで処理します。
1917         *
1918         * @og.rev 4.3.4.3 (2008/12/22) TableWriterで利用していたものを移動
1919         *
1920         * @param        data 元のString文字列
1921         *
1922         * @return      spanタグが取り除かれた文字列(引数が null の場合は、そのまま null が返ります)
1923         * @see         #tagCut(String)
1924         */
1925        public static String spanCut( final String data ) {
1926                String rtn = data;
1927                if( data != null && data.startsWith( "<span" ) ) {
1928                        final int st = data.indexOf( '>' );
1929                        final int ed = data.indexOf( "</span>",st );
1930                        rtn = data.substring( st+1,ed );
1931                }
1932
1933                return rtn ;
1934        }
1935
1936        /**
1937         * 引数からタグを取り除いて返します。
1938         *
1939         * 引数が、&lt;xxxx ・・・&gt;YYYY&lt;/xxxx&gt;形式の場合、YYYY のみ出力します。
1940         * この処理では、すべてのタグを削除し、BODY部をつなげます。
1941         * &lt;xxxx/&gt; の様な、BODY要素を持たない場合は、ゼロ文字列になります。
1942         *
1943         * @og.rev 6.2.0.0 (2015/02/27) 引数からタグを削除し、BODY文字列を切り出します。
1944         *
1945         * @param        data 元のString文字列
1946         *
1947         * @return       タグが取り除かれた文字列(引数が null の場合は、そのまま null が返ります)
1948         */
1949        public static String tagCut( final String data ) {
1950//              if( data == null || data.isEmpty() || data.indexOf( '<' ) < 0 ) { return data; }
1951                if( isEmpty( data ) || data.indexOf( '<' ) < 0 ) { return data; }                       // 6.9.2.1 (2018/03/12) isEmpty 置き換え
1952
1953                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1954
1955                boolean tagOut = true;
1956                for( int i=0; i<data.length(); i++ ) {
1957                        final char ch =data.charAt( i );
1958                        if(      ch == '<' ) { tagOut = false; continue; }      // タグの開始
1959                        else if( ch == '>' ) { tagOut = true;  continue; }      // タグの終了
1960
1961                        if( tagOut ) { rtn.append( ch ); }
1962                }
1963
1964                return rtn.toString() ;
1965        }
1966
1967        /**
1968         * 簡易CSS形式のフォーマットを、Mapにセットします。
1969         *
1970         * 簡易CSS形式とは、セレクタのない、{ プロパティ1 : 値1 ; ・・・ } 形式とします。
1971         * これを、プロパティ1 と 値1 のMap にセットする処理を行います。
1972         * コメントは、削除されます。また、同一プロパティが記述されている場合は、後処理を採用します。
1973         *
1974         * なお、入力テキストが、null か、{…} が存在しない場合は、空のMapを返します。
1975         *
1976         * @og.rev 5.6.5.2 (2013/06/21) 新規追加
1977         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
1978         * @og.rev 6.4.3.3 (2016/03/04) 戻すMapが、not null制限つきであることを示すため、ConcurrentMap に置き換えます。
1979         *
1980         * @param        cssText 簡易CSS形式のフォーマット文字列
1981         *
1982         * @return       パース結果のMap(ConcurrentMap)
1983         * @og.rtnNotNull
1984         */
1985        public static ConcurrentMap<String,String> cssParse( final String cssText ) {
1986                final ConcurrentMap<String,String> cssMap = new ConcurrentHashMap<>();
1987
1988                if( cssText != null ) {
1989                        // まずコメントを削除します。
1990                        StringBuilder buf = new StringBuilder( cssText );
1991
1992                        int ad1 = buf.indexOf( "/*" );
1993                        while( ad1 >= 0 ) {
1994                                final int ad2 = buf.indexOf( "*/" , ad1 );
1995                                if( ad2 < 0 ) { buf = buf.delete( ad1,buf.length() ); break; }          // 閉じてなければ以降を全削除
1996                                buf = buf.delete( ad1,ad2+2 );
1997                                ad1 = buf.indexOf( "/*" );              // コメントは削除されたので、初めから検索する。
1998                        }
1999
2000                        // 処理対象は、{ ~ } の間の文字列。
2001                        ad1 = buf.indexOf( "{" );
2002                        final int ad2 = buf.indexOf( "}",ad1 );
2003                        if( ad1 >= 0 && ad2 > 0 ) {
2004                                final String tempText = buf.substring( ad1+1,ad2 );             // これが処理対象の文字列
2005
2006                                // 6.4.3.3 (2016/03/04) ちょっとした変更
2007                                for( final String recode : tempText.split( ";" ) ) {    // KEY1 : VAL1; の ; で分割する。
2008                                        final int ad = recode.indexOf( ':' );
2009                                        if( ad > 0 ) {
2010                                                final String key = recode.substring( 0,ad ).trim();
2011                                                final String val = recode.substring( ad+1 ).trim();
2012                                                if( key.isEmpty() || val.isEmpty() ) { continue; }
2013
2014                                                cssMap.put( key,val );
2015                                        }
2016                                }
2017                        }
2018                }
2019
2020                return cssMap ;
2021        }
2022
2023//      /**
2024//       * 引数から空白文字を削除して返します。
2025//       *
2026//       * @og.rev 5.6.9.4 (2013/10/31) TableWriterで利用していたものを移動
2027//       * @og.rev 6.9.2.1 (2018/03/12) 使用箇所が、1箇所だけなので、StringUtilから移動する。
2028//       *
2029//       * @param        data 元のString文字列
2030//       *
2031//       * @return       空白文字が取り除かれた文字列
2032//       */
2033//      public static String deleteWhitespace( final String data ) {
2034//              // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
2035//              return data == null || data.isEmpty() ? data : data.replaceAll( "\\s", "" ) ;           // isNull 判定は使えない。
2036//      }
2037
2038        /**
2039         * 引数の文字列が、引数の char で始まるかどうか判定します[始まる場合は、true]。
2040         *
2041         * これは、PMDで言う所の、String.startsWith can be rewritten using String.charAt(0)
2042         * の書き換え処理に相当します。
2043         * boolean flag = data != null &amp;&amp; data.startsWith( chStr ); 的な処理を、
2044         * boolean flag = data != null &amp;&amp; data.length() &gt; 0 &amp;&amp; data.charAt(0) == ch;
2045         * に書き換える代わりに、このメソッドを使用します。
2046         *
2047         * 内部ロジックは、上記の相当します。
2048         *
2049         * @og.rev 6.2.0.0 (2015/02/27) 1文字 String.startsWith の String.charAt(0) 変換
2050         * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
2051         *
2052         * @param        data 引数のCharSequence
2053         * @param        ch   チェックするchar
2054         *
2055         * @return      引数文字列が、nullでなく、ゼロ文字列でなく、引数char で始まる場合は、true
2056         * @see         java.lang.String#startsWith(String)
2057         */
2058        public static boolean startsChar( final CharSequence data , final char ch ) {
2059                return data != null && data.length() > 0 && data.charAt(0) == ch;                       // スペースも判定対象にするため、isNull は使わない。
2060        }
2061
2062        /**
2063         * 引数から指定文字の分のバイト数で切った文字列を返します。
2064         * 文字列のバイト数は指定のエンコードでカウントします。
2065         * (文字の途中で切れる事はありません)
2066         *
2067         * @og.rev 5.9.1.3 (2015/10/30) 新規作成
2068         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil##ogStackTrace(Throwable) に変更。
2069         *
2070         * @param       org                     元のString文字列
2071         * @param       cutBytes        切るバイト数
2072         * @param       enc                     文字列のエンコード
2073         *
2074         * @return       バイト数で切った文字列
2075         */
2076        public static String cut( final String org, final int cutBytes, final String enc ) {
2077                try {
2078//                      if( org == null || org.length() == 0 || cutBytes <= 0 || org.getBytes(enc).length <= cutBytes ) {               // isNul 判定は使いません。
2079//                              return org;
2080//                      }
2081                        if( isEmpty( org,enc ) || cutBytes <= 0 || org.getBytes(enc).length <= cutBytes ) { return org; }               // 6.9.2.1 (2018/03/12) isEmpty 置き換え
2082
2083                        final StringBuilder cutSb = new StringBuilder( BUFFER_MIDDLE );
2084                        final StringBuilder tmpSb = new StringBuilder( BUFFER_MIDDLE );
2085
2086                        for( int i=0; i<org.length(); i++ ) {
2087                                final String cut = org.substring(i, i + 1);
2088                                if( cutBytes < tmpSb.toString().getBytes(enc).length + cut.getBytes(enc).length ) {
2089                                        cutSb.append( tmpSb.toString() );
2090                                        break;
2091                                }
2092                                tmpSb.append(cut);
2093                        }
2094                        return cutSb.toString();
2095                } 
2096                catch( final UnsupportedEncodingException ex ) {
2097                        // 6.4.1.1 (2016/01/16) PMD refactoring.        Avoid printStackTrace(); use a logger call instead.
2098                        // 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil##ogStackTrace(Throwable) に変更。
2099                        final String errMsg = "エンコードが不正のため、バイトカットできません。"
2100                                                                                + " org=[" + org + "] , byteSize=[" + cutBytes + "] , encode=[" + enc + "]" ;
2101
2102                        System.err.println( ThrowUtil.ogThrowMsg( errMsg,ex ) );
2103                        return org;
2104                }
2105        }
2106
2107        /**
2108         * 引数から指定文字の分のバイト数で切った文字列を返します。
2109         * バイト数のカウントはUTF-8として行います。
2110         *
2111         * @og.rev 5.9.1.3 (2015/10/30) 新規作成
2112         *
2113         * @param       org 元のString文字列
2114         * @param       cutBytes 切るバイト数
2115         *
2116         * @return       バイト数で切った文字列
2117         */
2118        public static String cut( final String org, final int cutBytes ) {
2119                return cut( org, cutBytes, "UTF-8");
2120        }
2121}