/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.system;

import java.util.function.Function;
import java.util.function.IntFunction;						// 6.4.4.2 (2016/04/01)
import java.util.function.Supplier;							// 6.4.4.2 (2016/04/01)

/**
 * 内部にStringBuilderを持った、文字列連結クラスです。
 *
 * 文字列連結時に、取り込む／取り込まないの判断を行う、boolean 付きの
 * appendIf メソッドや、null値を無視する append など、用意しています。
 *
 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
 *
 * @og.group その他
 *
 * @version  6.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public final class OgBuilder {
	private final StringBuilder buf = new StringBuilder( HybsConst.BUFFER_MIDDLE );

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 */
	public OgBuilder() {
		super();
	}

	/**
	 * 引数の可変長文字列を追加する appendです。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param arys	追加する可変長文字列
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder append( final String... arys ) {
		if( arys != null ) {
			for( final String str : arys ) {
				if( str != null ) { buf.append( str ); }
			}
		}

		return this;
	}

	/**
	 * 引数の可変長文字列を追加し、最後に改行コードを追加する appendです。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
	 * 引数がnull値の場合は、改行コードのみ追加されます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param arys	追加する文字列可変長(nullは追加しません)
	 * @return	自分自身
	 * @see		#append( String... )
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCR( final String... arys ) {
		append( arys );
		buf.append( HybsConst.CR );

		return this;
	}

	/**
	 * 引数の可変長文字列を追加する appendです。
	 *
	 * 引数の可変長文字列の個々の文字列の中に、一つでも null の場合は、
	 * すべて追加しません。
	 * 例えば、key="AAAA" のような文字列を構築したい場合、AAAA 部分が、nullの
	 * 場合は、key= の箇所も出しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param arys	追加する可変長文字列
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendNN( final String... arys ) {
		if( arys != null ) {
			final StringBuilder tmp = new StringBuilder();
			for( final String str : arys ) {
				if( str == null ) { return this; }		// 一つでも null があれば、すぐに抜ける。
				else { tmp.append( str ); }
			}
			buf.append( tmp );
		}
		return this;
	}

	/**
	 * 文字列を追加するかどうか判定するフラグ付きのappendメソッドです。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param arys	追加する可変長文字列
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendIf( final boolean flag , final String... arys ) {
		if( flag && arys != null ) {
			for( final String str : arys ) {
				if( str != null ) { buf.append( str ); }
			}
		}

		return this;
	}

	/**
	 * 引数の可変長文字列を追加し、最後に改行コードを追加する appendです。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
	 * 引数がnull値の場合は、改行コードのみ追加されます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param arys	追加する可変長文字列
	 * @return	自分自身
	 * @see		#appendIf( boolean,String... )
	 * @og.rtnNotNull
	 */
	public OgBuilder appendIfCR( final boolean flag , final String... arys ) {
		appendIf( flag,arys );
		buf.append( HybsConst.CR );

		return this;
	}

	/**
	 * 関数を実行した結果を追加するかどうか判定するフラグ付きのappendメソッドです。
	 * 実行した結果が、null値の場合は、無視します。
	 * 引数が関数型インタフェースなので、遅延実行することが可能です。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param <T>	関数型インタフェース(Function)の引数(総称型)
	 * @param <R>	関数型インタフェース(Function)の戻り値(総称型)
	 * @param flag	追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param key	関数の引数(総称型)
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <T,R> OgBuilder appendIf( final boolean flag , final T key , final Function<? super T, ? extends R> func ) {
		if( flag && func != null ) {
			R obj = func.apply( key );
			if( obj != null ) { buf.append( obj ); }
		}
		return this;
	}

	/**
	 * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
	 * 実行した結果が、null値の場合は、無視します。
	 * 引数が関数型インタフェースなので、遅延実行することが可能です。
	 * 関数の結果は、オブジェクトを返します。それを、内部でappendループします。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.4.2 (2016/04/01) IntFunction に変更。配列ではなく、オブジェクトに変更。
	 *
	 * @param <R>	IntFunctionの戻り値の仮想型
	 * @param st	ループカウンタの初期値(この値を含む)
	 * @param ed	ループカウンタの終了値(この値を含まない)
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <R> OgBuilder appendRoop( final int st , final int ed , final IntFunction<R> func ) {
		for( int i=st; i<ed; i++ ) {
			R obj = func.apply( i );
			if( obj != null ) {
				buf.append( obj );
			}
		}
		return this;
	}

	/**
	 * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
	 * 連結文字列に null は指定できません。
	 * 連結文字列は、一番最後には使用しません。
	 *
	 * @og.rev 6.4.4.2 (2016/04/01) 連結文字列を指定。
	 *
	 * @param <R>	IntFunctionの戻り値の仮想型
	 * @param st	ループカウンタの初期値(この値を含む)
	 * @param ed	ループカウンタの終了値(この値を含まない)
	 * @param sepa	文字列連結する場合の文字列。nullは指定できません。
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <R> OgBuilder appendRoop( final int st , final int ed , final String sepa , final IntFunction<R> func ) {
		boolean fstFlg = true;
		for( int i=st; i<ed; i++ ) {
			R obj = func.apply( i );
			if( obj != null ) {
				if( fstFlg ) {
					buf.append( obj );
					fstFlg = false;
				}
				else {
					buf.append( sepa ).append( obj );
				}
			}
		}
		return this;
	}

	/**
	 * 文字列を追加するかどうか判定するフラグ付きのappendメソッドです。
	 * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
	 * 第二引数は、可変長文字列です。
	 * ともに、文字列がnullの場合は、無視します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ
	 * @param trueStr	flagがtrueの場合に追加する文字列(一つだけ)
	 * @param falseStr	flagがfalseの場合に追加する可変長文字列
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCase( final boolean flag , final String trueStr , final String... falseStr ) {
		if( flag ) {
			if( trueStr != null ) { buf.append( trueStr ); }
		}
		else {
			if( falseStr != null ) {
				for( final String str : falseStr ) {
					if( str != null ) { buf.append( str ); }
				}
			}
		}

		return this;
	}

	/**
	 * 文字列を追加するかどうか判定するフラグ付きのappendメソッドです。
	 * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
	 * 第二引数は、可変長文字列です。
	 * ともに、文字列がnullの場合は、無視します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.4.2 (2016/04/01) 引数を、Supplierクラスに変更して、結果を複数指定できるようにします。
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ
	 * @param trueFunc	flagがtrueの場合に実行するFunctionオブジェクト
	 * @param falseFunc	flagがfalseの場合に追加するFunctionオブジェクト
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCase( final boolean flag
										, final Supplier<String[]> trueFunc
										, final Supplier<String[]> falseFunc ) {
		if( flag ) {
			final String[] tStrs = trueFunc.get();
			if( tStrs != null ) {
				for( final String tStr : tStrs ) {
					if( tStr != null ) { buf.append( tStr ); }
				}
			}
		}
		else {
			final String[] fStrs = falseFunc.get();
			if( fStrs != null ) {
				for( final String fStr : fStrs ) {
					if( fStr != null ) { buf.append( fStr ); }
				}
			}
		}

		return this;
	}

	/**
	 * 内部のStringBuilderそのものを返します。
	 *
	 * StringBuilderを関数等に渡して追加処理する場合に、
	 * 内部のStringBuilderを渡して、処理させることが出来ます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return	内部のStringBuilder
	 * @og.rtnNotNull
	 */
	public StringBuilder getBuilder() {
		return buf;
	}

	/**
	 * 内部のStringBuilderをクリアします。
	 *
	 * 具体的には、StringBuilder#setLength(0) を実行します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return	内部のStringBuilder
	 */
	public OgBuilder clear() {
		buf.setLength(0);
		return this;
	}

	/**
	 * このシーケンス内のデータを表す文字列を返します。
	 *
	 * 新しいStringオブジェクトが割り当てられ、現在このオブジェクトで表されている
	 * 文字シーケンスを含むように初期化されます。
	 * その後、このStringが返されます。その後このシーケンスが変更されても、Stringの内容には影響ありません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return この文字シーケンスの文字列表現。
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return buf.toString();
	}
}
