/*
 * 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.hayabusa.taglib;

import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.XHTMLTag;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ToString;						// 6.1.1.0 (2015/01/17)

import static org.opengion.fukurou.util.StringUtil.nval ;

/**
 * 隠しフィールドを作成するタグです(bodyHidden は廃止しました)。
 * name属性をキーにvalue属性をセットします。
 * 通常のhiddenタグの他に、BODY部に記述した値を送信することができます。
 * BODY部に記述した値は、リターンコードが取り除かれます。
 * また、keys属性、vals属性を指定することで、複数のキー＋値を同時に設定できます。
 *
 * @og.formSample
 * ●形式：
 *     ・&lt;og:hidden name="…" value="…" /&gt;
 *     ・&lt;og:hidden keys="…" vals="…" /&gt;
 *     ・&lt;og:hidden name="…" /&gt;
 *             ･･･
 *         &lt;/og:hidden&gt;
 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
 *
 * ●Tag定義：
 *   &lt;og:hidden
 *       name               【TAG】名前を指定します(name属性とkeys属性は同時には登録できません)
 *       value              【TAG】値を指定します(この属性を使うとBODY部は無視されます)
 *       keys               【TAG】名前をCSV形式で複数指定します(name属性とkeys属性は同時には登録できません)
 *       vals               【TAG】keys属性に対応する値をCSV形式で複数指定します
 *       defaultVal         【TAG】value属性に値がセットされていないときに、初期値をセットします
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:hidden&gt;
 *
 * ●使用例
 *    ・次画面にPNという名前で{&#064;PN}に入っている値を渡します。
 *        &lt;input:hidden name=&quot;PN&quot; value=&quot;{&#064;PN}&quot; /&gt;
 *
 *    ・次画面で使用するPLSQLと引数を渡します。
 *      &lt;input:hidden name=&quot;name&quot; value=&quot;UNIQ,LKIS,KBLOYA,KBLKO,KBNGK,HJO,KBLKNM&quot; /&gt;
 *      &lt;input:hidden name=&quot;SQL&quot; value=&quot;{ call RKP0011B.RK0011B( ?,?,?,?,? ) }&quot; /&gt;
 *            ↓
 *      &lt;og:plsqlUpdate
 *             command    = "{&#064;command}"
 *             names      = "{&#064;names}"
 *             dbType     = "RK0011ARG"
 *             queryType  = "JDBCPLSQL" &gt;
 *         {&#064;SQL}
 *      &lt;/og:plsqlUpdate&gt;
 *
 *    ・次画面にSQLという名前でBodyに記述した値を渡します。
 *      &lt;og:hidden name="SQL"
 *              SELECT AAA
 *              FROM RK01
 *              WHERE UNIQ = ?
 *      &lt;/og:hidden&gt;
 *
 * @og.group 画面部品
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class HiddenTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.4.2.0 (2016/01/29)" ;
	private static final long serialVersionUID = 642020160129L ;

	private boolean 	isValue		;
	private	String		name		;
	private	String		value		;		// 3.5.5.5 (2004/04/23) value属性を追加
	private	String		keys		;
	private	String[]	vals		;
	private String		defaultVal	;		// 3.8.5.1 (2006/05/08) defaultVal 属性を追加

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public HiddenTag() {
		super();		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
	}

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) BodyHiddenTag 廃止に伴う、機能拡張。
	 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doStartTag() {
		// 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
		// 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
		return useTag() && !isValue
				? EVAL_BODY_BUFFERED		// Body を評価する。( extends BodyTagSupport 時)
				: SKIP_BODY ;				// Body を評価しない

//		if( useTag() && !isValue ) {
//			return EVAL_BODY_BUFFERED ;			// Body を評価する。( extends BodyTagSupport 時)
//		}
//		return SKIP_BODY ;							// Body を評価しない
	}

	/**
	 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) BodyHiddenTag 廃止に伴う、機能拡張。
	 * @og.rev 3.1.1.0 (2003/03/28) ボディの内容を取得する処理を、CommonTagSupport で行う。
	 * @og.rev 3.5.5.5 (2004/04/23) value属性を追加
	 *
	 * @return	後続処理の指示(SKIP_BODY)
	 */
	@Override
	public int doAfterBody() {
		final String val = getBodyString();

		if( val != null && val.length() > 0 ) {
			value = StringUtil.replace( val,CR," " );
		}

		return SKIP_BODY ;
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		// 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
		if( useTag() ) {
			jspPrint( makeTag() );
		}
		return EVAL_PAGE ;
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) BodyHiddenTag 廃止に伴う、機能拡張。
	 * @og.rev 3.0.1.0 (2003/03/03) names属性とvals属性を追加
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.5.5.5 (2004/04/23) value属性を追加
	 * @og.rev 3.8.5.1 (2006/05/08) defaultVal 属性を追加
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		isValue		= false;
		name		= null;
		value		= null;
		keys		= null;
		vals		= null;
		defaultVal	= null;		// 3.8.5.1 (2006/05/08) defaultVal 属性を追加
	}

	/**
	 * 隠しフィールドを作成します。
	 *
	 * hidden( name ,value )
	 *
	 * &lt;input type="hidden" name="名前" value="隠し文字"&gt;
	 * &lt;input type="hidden" keys="名前1,名前2･･･" vals="値1,値2･･･"&gt;
	 *
	 * @og.rev 3.0.1.0 (2003/03/03) names属性とvals属性を追加
	 * @og.rev 3.5.5.5 (2004/04/23) hidden の出力に、XHTMLTag.hidden を使用します。
	 * @og.rev 3.8.5.1 (2006/05/08) defaultVal 属性を追加
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 *
	 * @return  隠しフィールドタグ文字列
	 * @og.rtnNotNull
	 */
	protected String makeTag() {

		checkName() ;

		final String rtn ;
//		if( keys == null ) {
		if( keys == null || vals == null ) {	// 6.3.9.0 (2015/11/06)
			// 注意：互換性維持の為、defaultVal を使用した場合の設定では null がセット
			// されないようにしています。使用しない場合は、null のままです。
			// 3.8.5.1 (2006/05/08) defaultVal 属性を追加
			if( defaultVal != null ) {
				value = nval( value,defaultVal );
			}
			rtn = XHTMLTag.hidden( name,value );
		} else {
			final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
			final String[] nm = StringUtil.csv2Array( keys );
			for( int i=0; i<nm.length; i++ ) {
				final String nn = getRequestParameter( nm[i] );
				final String vv = getRequestParameter( vals[i] );
				buf.append( XHTMLTag.hidden( nn,vv ) ).append( CR );

			}
			rtn = buf.toString();
		}
		return rtn ;
	}

	/**
	 * name,value,keys,vals の相関関係をチェックします。
	 *
	 * name,value が、ペアで、keys,valsが、ペアです。
	 * name属性とkeys属性は同時には登録できません。
	 *
	 * @og.rev 4.0.0.0 (2007/06/06) 新規追加
	 */
	private void checkName() {
		if( name != null && keys != null ) {
			final String errMsg = "name属性とkeys属性は同時には登録できません。: " + CR
						+ "name=[" + name + "] keys=[" + keys + "]";
			throw new HybsSystemException( errMsg );
		}

		if( name != null && vals != null ) {
			final String errMsg = "name属性とvals属性が設定されています。: " + CR
						+ "name属性を指定する場合は、value属性を使用してください。" + CR
						+ "name=[" + name + "] vals=[" + StringUtil.array2line( vals,"," ) + "]";
			throw new HybsSystemException( errMsg );
		}

		if( keys != null && value != null ) {
			final String errMsg = "keys属性とvalue属性が設定されています。: " + CR
						+ "keys属性を指定する場合は、vals属性を使用してください。" + CR
						+ "keys=[" + keys + "] value=[" + value + "]";
			throw new HybsSystemException( errMsg );
		}
	}

	/**
	 * 【TAG】名前を指定します(name属性とkeys属性は同時には登録できません)。
	 *
	 * @og.tag
	 * 名前を指定します。
	 * name属性とkeys属性は同時には登録できません。
	 *
	 * @og.rev 3.0.1.0 (2003/03/03) names属性とvals属性を追加
	 * @og.rev 3.5.5.5 (2004/04/23) hidden の出力に、XHTMLTag.hidden を使用します。
	 *
	 * @param   nm 名前
	 */
	public void setName( final String nm ) {
		name = nval( getRequestParameter( nm ),name );
	}

	/**
	 * 【TAG】値を指定します(この属性を使うとBODY部は無視されます)。
	 *
	 * @og.tag
	 * ここで値を設定した場合は、BODY 部の値は使用しません。
	 * value="{&#064;value}" などとリクエスト変数を使用した場合に、その値が
	 * 設定されていないケースでも、この属性を使用したとみなされます。
	 *
	 * @og.rev 3.5.5.5 (2004/04/23) hidden の出力に、XHTMLTag.hidden を使用します。
	 *
	 * @param   val 値
	 */
	public void setValue( final String val ) {
		value = nval( getRequestParameter( val ),"" );
		isValue = true;
	}

	/**
	 * 【TAG】名前をCSV形式で複数指定します(name属性とkeys属性は同時には登録できません)。
	 *
	 * @og.tag
	 * 名前をCSV形式で複数指定します。
	 * name属性とkeys属性は同時には登録できません。
	 *
	 * @og.rev 3.0.1.0 (2003/03/03) names属性とvals属性を追加
	 * @og.rev 4.0.0.0 (2007/06/06) names属性をkeys属性に変更
	 *
	 * @param	ns 名前(CSV形式)
	 */
	public void setKeys( final String ns ) {
		keys = nval( getRequestParameter( ns ),keys ) ;
	}

	/**
	 * 【TAG】keys属性に対応する値をCSV形式で複数指定します。
	 *
	 * @og.tag
	 * keys属性に対応する値をCSV形式で複数指定します。
	 * keys属性が設定されている場合のみ、使用されます。
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。
	 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。
	 *
	 * @og.rev 3.0.1.0 (2003/03/03) names属性とvals属性を追加
	 * @og.rev 3.5.6.2 (2004/07/05) CommonTagSupport#getCSVParameter を使用
	 *
	 * @param	val 値(CSV形式)
	 */
	public void setVals( final String val ) {
		vals = getCSVParameter( val );
	}

	/**
	 * 【TAG】value属性に値がセットされていないとき使用する、初期値を指定します。
	 *
	 * @og.tag
	 * 注意：互換性維持の為、defaultVal を使用した場合の設定では null がセット
	 * されないようにしています。使用しない場合は、null のままです。
	 *
	 * @og.rev 3.8.5.1 (2006/05/08) defaultVal 属性を追加
	 *
	 * @param	dv 初期値
	 */
	public void setDefaultVal( final String dv ) {
		defaultVal = nval( getRequestParameter( dv ),"" );		// 特殊
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"		,VERSION	)
				.println( "isValue" 	,isValue	)
				.println( "name"		,name		)
				.println( "value"		,value		)
				.println( "keys"		,keys		)
				.println( "vals"		,vals		)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
