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