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