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