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.system;
017
018import java.util.function.Function;
019import java.util.function.IntFunction;                                          // 6.4.4.2 (2016/04/01)
020import java.util.function.Supplier;                                                     // 6.4.4.2 (2016/04/01)
021
022/**
023 * 内部にStringBuilderを持った、文字列連結クラスです。
024 *
025 * 文字列連結時に、取り込む/取り込まないの判断を行う、boolean 付きの
026 * appendIf メソッドや、null値を無視する append など、用意しています。
027 *
028 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
029 * @og.rev 6.4.5.0 (2016/04/08) CharSequenceインタフェースの追加
030 *
031 * @og.group その他
032 *
033 * @version  6.0
034 * @author       Kazuhiko Hasegawa
035 * @since    JDK8.0,
036 */
037public final class OgBuilder implements CharSequence {
038//      private final StringBuilder buildBuf = new StringBuilder( HybsConst.BUFFER_MIDDLE );            // 8.5.6.1 (2024/03/29) コンストラクタで作成
039        private final StringBuilder buildBuf;
040
041        /**
042         * デフォルトコンストラクター
043         *
044         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
045         */
046        public OgBuilder() {
047                buildBuf = new StringBuilder( HybsConst.BUFFER_MIDDLE );
048        }
049
050        /**
051         * StringBuilder を引数にしたコンストラクター
052         *
053         * @og.rev 8.5.6.1 (2024/03/29) 新規作成
054         *
055         * @param buf   内部に設定する StringBuilder オブジェクト
056         */
057        public OgBuilder( final StringBuilder buf ) {
058                buildBuf = buf;
059        }
060
061        /**
062         * 引数の可変長文字列を追加する appendです。
063         *
064         * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
065         *
066         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
067         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
068         *
069         * @param arys  追加する可変長CharSequence
070         * @return      自分自身
071         * @og.rtnNotNull
072         */
073        public OgBuilder append( final CharSequence... arys ) {
074                if( arys != null ) {
075                        for( final CharSequence str : arys ) {
076                                // 8.5.5.1 (2024/02/29) nullか、ゼロ文字列 に判定条件を変更する。
077//                              if( str != null ) { buildBuf.append( str ); }
078                                if( str != null && !str.isEmpty() ) { buildBuf.append( str ); }
079                        }
080                }
081
082                return this;
083        }
084
085        /**
086         * 連結文字列を、使用して、可変長引数のCharSequenceを連結して返します。
087         * 連結文字列が、null の場合は、CharSequenceをそのまま連結していきます。
088         * 連結するCharSequenceが null の場合は、連結しません。
089         * 連結文字列は、一番最後は出力されません。
090         *
091         * @og.rev 6.4.5.0 (2016/04/08) 新規追加
092         *
093         * @param       delimiter       連結文字列
094         * @param       arys    連結する可変長CharSequence
095         *
096         * @return      自分自身
097         * @og.rtnNotNull
098         */
099        public OgBuilder join( final String delimiter , final CharSequence... arys ) {
100                if( delimiter == null ) { return append( arys ); }
101
102                if( arys != null ) {
103                        boolean isAdd = false;
104                        for( final CharSequence str : arys ) {
105                                if( str != null && str.length() > 0 ) {
106                                        buildBuf.append( str ).append( delimiter );
107                                        isAdd = true;
108                                }
109                        }
110                        if( isAdd ) { buildBuf.setLength( buildBuf.length()-delimiter.length() ); }     // 最後に追加した delimiter を削除。
111                }
112
113                return this;
114        }
115
116        /**
117         * 引数の可変長文字列を内部バッファーから削除します。
118         *
119         * 引数の可変長文字列の個々の文字列が null の場合は、なにもしません。
120         * また、内部バッファーに存在しない場合も、何もしません。
121         *
122         * ※ 削除の指定は、CharSequenceではなく文字列です。
123         *
124         * @og.rev 6.4.5.0 (2016/04/08) 新規追加
125         *
126         * @param arys  削除する可変長文字列
127         * @return      自分自身
128         * @og.rtnNotNull
129         */
130        public OgBuilder delete( final String... arys ) {
131                if( arys != null ) {
132                        for( final String str : arys ) {
133                                // 8.5.5.1 (2024/02/29) nullか、ゼロ文字列 に判定条件を変更する。
134//                              if( str != null ) {
135                                if( str != null && !str.isEmpty() ) {
136                                        final int len = str.length();           // 終了の位置を求めるのに使う。
137                                        int st;
138                                        while( ( st = buildBuf.indexOf( str ) ) >= 0 ) {
139                                                buildBuf.delete( st , st+len );
140                                        }
141                                }
142                        }
143                }
144
145                return this;
146        }
147
148        /**
149         * 引数の可変長CharSequenceを追加し、最後に改行コードを追加する appendです。
150         *
151         * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
152         * 引数がnull値の場合は、改行コードのみ追加されます。
153         *
154         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
155         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
156         *
157         * @param arys  追加する文字列可変長(nullは追加しません)
158         * @return      自分自身
159         * @see         #append( CharSequence... )
160         * @og.rtnNotNull
161         */
162        public OgBuilder appendCR( final CharSequence... arys ) {
163                return append( arys ).append( HybsConst.CR );
164        }
165
166        /**
167         * 引数の可変長CharSequenceを追加する appendです。
168         *
169         * 引数の可変長CharSequenceの個々のCharSequenceの中に、一つでも null の場合は、
170         * すべて追加しません。
171         * 例えば、key="AAAA" のような文字列を構築したい場合、AAAA 部分が、nullの
172         * 場合は、key= の箇所も出しません。
173         *
174         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
175         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
176         * @og.rev 6.9.0.2 (2018/02/13) not null の対象に、ゼロ文字列も含むようにします。
177         *
178         * @param arys  追加する可変長CharSequence
179         * @return      自分自身
180         * @og.rtnNotNull
181         */
182        public OgBuilder appendNN( final CharSequence... arys ) {
183                // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要
184                if( arys == null || arys.length == 0 ) { return this; }         // 判定条件変更
185
186//              if( arys != null && arys.length > 0 ) {
187                        final StringBuilder tmp = new StringBuilder( HybsConst.BUFFER_MIDDLE );
188                        for( final CharSequence str : arys ) {
189//                              if( str == null ) { return this; }              // 一つでも null があれば、すぐに抜ける。
190                                // 6.9.0.2 (2018/02/13) 一つでも nullか、ゼロ文字列 があれば、すぐに抜ける。
191                                if( str == null || str.length() == 0 ) { return this; }         // buildBuf に反映させない。
192                                else { tmp.append( str ); }
193                        }
194                        buildBuf.append( tmp );
195//              }
196                        return this;
197        }
198
199        /**
200         * 文字列を追加するかどうか判定するフラグ付きのappendメソッドです。
201         *
202         * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
203         *
204         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
205         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
206         *
207         * @param flag  文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
208         * @param arys  追加する可変長CharSequence
209         * @return      自分自身
210         * @og.rtnNotNull
211         */
212        public OgBuilder appendIf( final boolean flag , final CharSequence... arys ) {
213                if( flag && arys != null ) {
214                        for( final CharSequence str : arys ) {
215                                // 8.5.5.1 (2024/02/29) nullか、ゼロ文字列 に判定条件を変更する。
216//                              if( str != null ) { buildBuf.append( str ); }
217                                if( str != null && !str.isEmpty() ) { buildBuf.append( str ); }
218                        }
219                }
220
221                return this;
222        }
223
224        /**
225         * 引数の可変長CharSequenceを追加し、最後に改行コードを追加する appendです。
226         *
227         * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
228         * 引数がnull値の場合は、改行コードのみ追加されます。
229         *
230         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
231         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
232         *
233         * @param flag  文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
234         * @param arys  追加する可変長CharSequence
235         * @return      自分自身
236         * @see         #appendIf( boolean,CharSequence... )
237         * @og.rtnNotNull
238         */
239        public OgBuilder appendIfCR( final boolean flag , final CharSequence... arys ) {
240                return appendIf( flag,arys ).append( HybsConst.CR );
241        }
242
243        /**
244         * 関数を実行した結果を追加するかどうか判定するフラグ付きのappendメソッドです。
245         * 実行した結果が、null値の場合は、無視します。
246         * 引数が関数型インタフェースなので、遅延実行することが可能です。
247         *
248         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
249         *
250         * @param <T>   関数型インタフェース(Function)の引数(総称型)
251         * @param <R>   関数型インタフェース(Function)の戻り値(総称型)
252         * @param flag  追加するかどうか判定するフラグ(trueの時のみ追加)
253         * @param key   関数の引数(総称型)
254         * @param func  関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
255         * @return      自分自身
256         * @og.rtnNotNull
257         */
258        public <T,R> OgBuilder appendIf( final boolean flag , final T key , final Function<? super T, ? extends R> func ) {
259                if( flag && func != null ) {
260                        final R obj = func.apply( key );
261                        if( obj != null ) { buildBuf.append( obj ); }
262                }
263                return this;
264        }
265
266        /**
267         * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
268         * 実行した結果が、null値の場合は、無視します。
269         * 引数が関数型インタフェースなので、遅延実行することが可能です。
270         * 関数の結果は、オブジェクトを返します。それを、内部でappendループします。
271         *
272         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
273         * @og.rev 6.4.4.2 (2016/04/01) IntFunction に変更。配列ではなく、オブジェクトに変更。
274         *
275         * @param <R>   IntFunctionの戻り値の仮想型
276         * @param st    ループカウンタの初期値(この値を含む)
277         * @param ed    ループカウンタの終了値(この値を含まない)
278         * @param func  関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
279         * @return      自分自身
280         * @og.rtnNotNull
281         */
282        public <R> OgBuilder appendRoop( final int st , final int ed , final IntFunction<R> func ) {
283                for( int i=st; i<ed; i++ ) {
284                        final R obj = func.apply( i );
285                        if( obj != null ) {
286                                buildBuf.append( obj );
287                        }
288                }
289                return this;
290        }
291
292        /**
293         * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
294         * 連結文字列に null は指定できません。
295         * 連結文字列は、一番最後には使用しません。
296         *
297         * @og.rev 6.4.4.2 (2016/04/01) 連結文字列を指定。
298         *
299         * @param <R>   IntFunctionの戻り値の仮想型
300         * @param st    ループカウンタの初期値(この値を含む)
301         * @param ed    ループカウンタの終了値(この値を含まない)
302         * @param delimiter     文字列連結する場合の文字列。nullは指定できません。
303         * @param func  関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
304         * @return      自分自身
305         * @og.rtnNotNull
306         */
307        public <R> OgBuilder appendRoop( final int st , final int ed , final String delimiter , final IntFunction<R> func ) {
308                boolean fstFlg = true;
309                for( int i=st; i<ed; i++ ) {
310                        final R obj = func.apply( i );
311                        if( obj != null ) {
312                                if( fstFlg ) {
313                                        buildBuf.append( obj );
314                                        fstFlg = false;
315                                }
316                                else {
317                                        buildBuf.append( delimiter ).append( obj );
318                                }
319                        }
320                }
321                return this;
322        }
323
324        /**
325         * CharSequenceを追加するかどうか判定するフラグ付きのappendメソッドです。
326         * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
327         * 第二引数は、可変長CharSequenceです。
328         * ともに、CharSequenceがnullの場合は、無視します。
329         *
330         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
331         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
332         *
333         * @param flag  CharSequenceを追加するかどうか判定するフラグ
334         * @param trueStr       flagがtrueの場合に追加するCharSequence(一つだけ)
335         * @param falseStr      flagがfalseの場合に追加する可変長CharSequence
336         * @return      自分自身
337         * @og.rtnNotNull
338         */
339        public OgBuilder appendCase( final boolean flag , final CharSequence trueStr , final CharSequence... falseStr ) {
340                if( flag ) {
341                        if( trueStr != null ) { buildBuf.append( trueStr ); }
342                }
343                else {
344                        if( falseStr != null ) {
345                                for( final CharSequence str : falseStr ) {
346                                        // 8.5.5.1 (2024/02/29) nullか、ゼロ文字列 に判定条件を変更する。
347//                                      if( str != null ) { buildBuf.append( str ); }
348                                        if( str != null && !str.isEmpty() ) { buildBuf.append( str ); }
349                                }
350                        }
351                }
352
353                return this;
354        }
355
356        /**
357         * CharSequenceを追加するかどうか判定するフラグ付きのappendメソッドです。
358         * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
359         * 第一引数、第二引数ともに、配列を返すSupplierクラスです。
360         * ともに、個々のCharSequenceがnullの場合は、無視します。
361         *
362         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
363         * @og.rev 6.4.4.2 (2016/04/01) 引数を、Supplierクラスに変更して、結果を複数指定できるようにします。
364         * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
365         *
366         * @param flag  CharSequenceを追加するかどうか判定するフラグ
367         * @param trueFunc      flagがtrueの場合に実行するFunctionオブジェクト
368         * @param falseFunc     flagがfalseの場合に追加するFunctionオブジェクト
369         * @return      自分自身
370         * @og.rtnNotNull
371         */
372        public OgBuilder appendCase( final boolean flag
373                                                                                , final Supplier<CharSequence[]> trueFunc
374                                                                                , final Supplier<CharSequence[]> falseFunc ) {
375                if( flag ) {
376                        final CharSequence[] tStrs = trueFunc.get();
377                        if( tStrs != null ) {
378                                for( final CharSequence tStr : tStrs ) {
379                                        // 8.5.5.1 (2024/02/29) !tStr.isEmpty() 追加
380//                                      if( tStr != null ) { buildBuf.append( tStr ); }
381                                        if( tStr != null && !tStr.isEmpty() ) { buildBuf.append( tStr ); }
382                                }
383                        }
384                }
385                else {
386                        final CharSequence[] fStrs = falseFunc.get();
387                        if( fStrs != null ) {
388                                for( final CharSequence fStr : fStrs ) {
389                                        // 8.5.5.1 (2024/02/29) !tStr.isEmpty() 追加
390//                                      if( fStr != null ) { buildBuf.append( fStr ); }
391                                        if( fStr != null && !fStr.isEmpty() ) { buildBuf.append( fStr ); }
392                                }
393                        }
394                }
395
396                return this;
397        }
398
399        /**
400         * 内部のStringBuilderそのものを返します。
401         *
402         * StringBuilderを関数等に渡して追加処理する場合に、
403         * 内部のStringBuilderを渡して、処理させることが出来ます。
404         *
405         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
406         *
407         * @return      内部のStringBuilder
408         * @og.rtnNotNull
409         */
410        public StringBuilder getBuilder() {
411                return buildBuf;
412        }
413
414        /**
415         * 内部のStringBuilderをクリアします。
416         *
417         * 具体的には、StringBuilder#setLength(0) を実行します。
418         *
419         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
420         *
421         * @return      内部のStringBuilder
422         */
423        public OgBuilder clear() {
424                buildBuf.setLength(0);
425                return this;
426        }
427
428        // ********************************** 以下は、CharSequence インタフェース の実装 **********************************
429
430        /**
431         * 指定されたインデックスのchar値を返します。
432         *
433         * インデックスは、0からlength() - 1の範囲になります。配列のインデックス付けの場合と同じように、
434         * シーケンスの最初のcharのインデックスは0、次の文字のインデックスは1と続きます。
435         *
436         * インデックスで指定されたchar値がサロゲートの場合、サロゲート値が返されます。
437         *
438         * @og.rev 6.4.5.0 (2016/04/08) 新規追加
439         *
440         * @param index 返されるchar値のインデックス
441         * @return 指定されたchar値
442         * @see         java.lang.CharSequence#charAt(int)
443         */
444        @Override       // CharSequence
445        public char charAt( final int index ) {
446                return buildBuf.charAt( index );
447        }
448
449        /**
450         * この文字シーケンスの長さを返します。
451         *
452         * 長さはシーケンス内の16ビットcharの数に等しくなります。
453         *
454         * @og.rev 6.4.5.0 (2016/04/08) 新規追加
455         *
456         * @return このシーケンスのcharの数
457         * @see         java.lang.CharSequence#length()
458         */
459        @Override       // CharSequence
460        public int length() {
461                return buildBuf.length();
462        }
463
464        /**
465         * この文字シーケンスの長さを返します。
466         *
467         * 長さはシーケンス内の16ビットcharの数に等しくなります。
468         *
469         * @og.rev 6.4.5.0 (2016/04/08) 新規追加
470         *
471         * @param start 開始インデックス(この値を含む)
472         * @param end   終了インデックス(この値を含まない)
473         * @return 指定されたサブシーケンス
474         * @see         java.lang.CharSequence#subSequence(int,int)
475         */
476        @Override       // CharSequence
477        public CharSequence subSequence( final int start , final int end ) {
478                return buildBuf.subSequence( start , end );
479        }
480
481        /**
482         * このシーケンス内のデータを表す文字列を返します。
483         *
484         * 新しいStringオブジェクトが割り当てられ、現在このオブジェクトで表されている
485         * 文字シーケンスを含むように初期化されます。
486         * その後、このStringが返されます。その後このシーケンスが変更されても、Stringの内容には影響ありません。
487         *
488         * @og.rev 6.4.4.1 (2016/03/18) 新規追加
489         *
490         * @return この文字シーケンスの文字列表現。
491         * @see         java.lang.CharSequence#toString()
492         * @og.rtnNotNull
493         */
494        @Override       // CharSequence
495        public String toString() {
496                return buildBuf.toString();
497        }
498}