/*
 * 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.plugin.view;

import java.util.List;

import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.html.TableFormatter;

/**
 * ヘッダ、フッタ、ボディを指定して作成する、自由レイアウトが可能な、カスタムテーブル表示クラスです。
 * 従来は、内部バグのため、thead,tbody,tfoot タグを使わないと処理できませんでしたが、
 * viewタグの BODY 部にフォーマットを記述するだけで処理するように改善しました。(5.6.3.3 (2013/04/19))
 *
 * このタグでは、BODY部、または、bodyFormats を繰り返す処理を行います。
 * ヘッダ があれば、最初に、１度のみ実行し、フッタがあれば、最後に実行します。
 * このクラスが他と異なるのは、ヘッダのみ記述した場合、ヘッダとして使われず、ボディとしてのみ繰返し
 * 使われます。また、bodyFormats のみの記述も可能です。
 *
 * このクラスは、ViewForm_HTMLFormatTable クラスの代替えとしても使用できます。
 * その場合は、thead のみ指定すれば、同じフォームが tbody にも適用されます。
 * これは、まさに、ViewForm_HTMLFormatTable と同じです。
 * (※ 上記仕様が、未実装でしたので、対応しました。 5.6.3.3 (2013/04/19) )
 *
 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。
 * 各HTMLのタグに必要な setter/getterメソッドのみ，追加定義しています。
 *
 * AbstractViewForm を継承している為,ロケールに応じたラベルを出力させる事が出来ます。
 *
 * <table class="plain">
 *   <caption>ヘッダ と ボディ の組み合わせ</caption>
 *   <tr><th>番号</th><th>headerFormat</th><th>bodyFormats</th><th>現状動作    </th><th>変更後(5.6.3.3以降)      </th></tr>
 *   <tr><td>①  </td><td>あり        </td><td>なし       </td><td>headerのみ  </td><td>body の繰り返し          </td></tr>
 *   <tr><td>②  </td><td>なし        </td><td>あり       </td><td>エラー      </td><td>bodyFormats のみ繰り返す </td></tr>
 *   <tr><td>③  </td><td>あり        </td><td>あり       </td><td>それぞれ動作</td><td>← 同じ                  </td></tr>
 *   <tr><td>④  </td><td>なし        </td><td>なし       </td><td>エラー      </td><td>← 同じ                  </td></tr>
 * </table>
 *
 * @og.rev 3.7.1.1 (2005/05/23) 新規作成
 * @og.rev 5.6.3.3 (2013/04/19) 処理変更
 * @og.group 画面表示
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ViewForm_CustomData extends ViewForm_HTMLTable	{
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "7.3.0.0 (2021/01/06)" ;

	private static final int BODYFORMAT_MAX_COUNT = 10;

	private TableFormatter 		headerFormat	;
	private TableFormatter[]	bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT];	// 7.3.0.0 (2021/01/06)
	private TableFormatter		footerFormat	;
	private int					bodyFormatsCount;

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

	/**
	 * DBTableModel から HTML文字列を作成して返します。
	 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
	 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
	 *
	 * @og.rev 4.3.1.0 (2008/09/08) フォーマットが設定されていない場合のエラー追加・編集行のみを表示する属性(isSkipNoEdit)追加
	 * @og.rev 5.6.3.3 (2013/04/19) headerFormatのみ、bodyFormatsのみ対応
	 * @og.rev 6.2.0.0 (2015/02/27) フォーマット系の noDisplay 対応
	 * @og.rev 6.3.9.0 (2015/11/06) 引数にTableFormatterを渡して、処理の共有化を図る。
	 * @og.rev 6.4.4.2 (2016/04/01) TableFormatterのタイプ別値取得処理の共通部をまとめる。
	 *
	 * @param  startNo	  表示開始位置
	 * @param  pageSize   表示件数
	 *
	 * @return	DBTableModelから作成された HTML文字列
	 * @og.rtnNotNull
	 */
	@Override
	public String create( final int startNo, final int pageSize )  {
		if( getRowCount() == 0 ) { return ""; }	// 暫定処置

		// 5.6.3.3 (2013/04/19) headerFormatのみ、bodyFormatsのみ対応
		headerLine	 = null;		// 3.5.3.1 (2003/10/31) キャッシュクリア

		final int lastNo = getLastNo( startNo, pageSize );

		// 5.6.3.3 (2013/04/19) headerFormatのみ、bodyFormatsのみ対応
		if( headerFormat != null ) {
			headerFormat.makeFormat( getDBTableModel() );	// 3.5.6.2 (2004/07/05) 移動
			// 6.2.0.0 (2015/02/27) フォーマット系の noDisplay 対応
			setFormatNoDisplay( headerFormat );
		}

		// 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
		// bodyFormatsCount が、0 でない場合は、bodyFormats は、設定済みなので、null チェック不要
//		if( bodyFormatsCount != 0 ) {
		if( bodyFormatsCount > 0 ) {
			for( int i=0; i<bodyFormatsCount; i++ ) {
				bodyFormats[i].makeFormat( getDBTableModel() );
				// 6.2.0.0 (2015/02/27) フォーマット系の noDisplay 対応
				setFormatNoDisplay( bodyFormats[i] );
			}
		}

		final StringBuilder out = new StringBuilder( BUFFER_LARGE );
		out.append( getHeader() );
		for( int row=startNo; row<lastNo; row++ ) {
			if( isSkip( row ) || isSkipNoEdit( row ) ) { continue; } // 4.3.1.0 (2008/09/08)
			for( int i=0; i<bodyFormatsCount; i++ ) {
				final TableFormatter bodyFormat = bodyFormats[i];
				if( ! bodyFormat.isUse( row,getDBTableModel() ) ) { continue; }		// 3.5.4.0 (2003/11/25)

				int cl = 0;
				for( ; cl<bodyFormat.getLocationSize(); cl++ ) {
					final String fmt = bodyFormat.getFormat(cl);
					out.append( fmt );			// 3.5.0.0

					final int loc = bodyFormat.getLocation(cl);	// 3.5.5.0
					if( loc >= 0 ) {
						// 6.4.4.2 (2016/04/01) 処理の共通部をまとめる。
						out.append( getTypeCaseValue( bodyFormat.getType(cl),row,loc ) );
					}
					else {
						out.append( bodyFormat.getSystemFormat(row,loc) );
					}
				}
				out.append( bodyFormat.getFormat(cl) );
			}
		}

		if( footerFormat != null ) {
			// 6.3.9.0 (2015/11/06) 引数にTableFormatterを渡して、処理の共有化を図る。
			out.append( getTableFoot( footerFormat ) );
		}

		return out.toString();
	}

	/**
	 * 内容をクリア(初期化)します。
	 *
	 */
	@Override
	public void clear() {
		super.clear();
		headerFormat		= null;
//		bodyFormats			= null;
		bodyFormats			= new TableFormatter[BODYFORMAT_MAX_COUNT];	// 7.3.0.0 (2021/01/06) SpotBugs:null チェックなしで null 値を利用
		footerFormat		= null;
		bodyFormatsCount	= 0;
	}

	/**
	 * DBTableModel から テーブルのヘッダータグ文字列を作成して返します。
	 *
	 * @og.rev 6.4.4.2 (2016/04/01) TableFormatterのタイプ別値取得処理の共通部をまとめる。
	 *
	 * @return	テーブルのヘッダータグ文字列
	 * @og.rtnNotNull
	 */
	@Override
	protected String getHeader() {
		// 6.3.9.1 (2015/11/27) A method should have only one exit point, and that should be the last statement in the method.(PMD)

		if( headerLine == null ) {
			if( headerFormat == null ) { headerLine = ""; }
			else {
				final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

				int cl = 0;
				for( ; cl<headerFormat.getLocationSize(); cl++ ) {
					buf.append( headerFormat.getFormat(cl) );
					final int loc = headerFormat.getLocation(cl);
					// 6.4.4.2 (2016/04/01)
					if( loc >= 0 ) {
						buf.append( getTypeCaseValue( headerFormat.getType(cl),-1,loc ) );
					}
				}
				buf.append( headerFormat.getFormat(cl) ).append( CR );

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

	/**
	 * DBTableModel から テーブルのタグ文字列を作成して返します。
	 *
	 * @og.rev 6.2.0.0 (2015/02/27) フォーマット系の noDisplay 対応
	 * @og.rev 6.3.9.0 (2015/11/06) 引数にTableFormatterを渡して、処理の共有化を図る。
	 * @og.rev 6.4.4.2 (2016/04/01) TableFormatterのタイプ別値取得処理の共通部をまとめる。
	 *
	 * @param	footerFormat TableFormatterオブジェクト
	 * @return	テーブルのタグ文字列
	 * @og.rtnNotNull
	 */
	@Override
	protected String getTableFoot( final TableFormatter footerFormat ) {
		footerFormat.makeFormat( getDBTableModel() );
		// 6.2.0.0 (2015/02/27) フォーマット系の noDisplay 対応
		setFormatNoDisplay( footerFormat );

		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		int cl = 0;
		for( ; cl<footerFormat.getLocationSize(); cl++ ) {
			final int loc = footerFormat.getLocation(cl);
			// 6.4.4.2 (2016/04/01)
			if( loc >= 0 ) {
				buf.append( getTypeCaseValue( footerFormat.getType(cl),-1,loc ) );
			}
		}
		buf.append( footerFormat.getFormat(cl) ).append( CR );

		return buf.toString();
	}

	/**
	 * フォーマットを設定します。
	 *
	 * @og.rev 5.6.3.3 (2013/04/19) headerFormatのみの場合、bodyFormats として使う。
	 * @og.rev 7.3.0.0 (2021/01/06) SpotBugs:null チェックなしで null 値を利用対策
	 *
	 * @param	list	TableFormatterのリスト
	 */
	@Override
	public void setFormatterList( final List<TableFormatter> list ) {		// 4.3.3.6 (2008/11/15) Generics警告対応
//		bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT];				// 7.3.0.0 (2021/01/06)

		bodyFormatsCount = 0;
		// 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
		for( final TableFormatter format : list ) {
//		for( int i=0; i<list.size(); i++ ) {
//			final TableFormatter format = list.get( i );		// 4.3.3.6 (2008/11/15) Generics警告対応

			switch( format.getFormatType() ) {
				case TYPE_HEAD : headerFormat = format; break;
				case TYPE_BODY : bodyFormats[bodyFormatsCount++] = format; break;
				case TYPE_FOOT : footerFormat = format; break;
				default : final String errMsg = "FormatterType の定義外の値が指定されました。";
						// 4.3.4.4 (2009/01/01)
						  throw new HybsSystemException( errMsg );
			}
		}

	 	// 5.6.3.3 (2013/04/19) headerFormatのみの場合、bodyFormats として使う。
		if( bodyFormatsCount == 0 ) {		// bodyFormats がない場合は、headerFormatをコピーする。
			if( headerFormat == null ) {
				final String errMsg = "thead タグか、または、tbody タグによるフォーマットの指定は必須です。";
				throw new HybsSystemException( errMsg );
			}
			else {
				bodyFormats[bodyFormatsCount++] = headerFormat;
				headerFormat = null;
			}
		}
	}

	/**
	 * フォーマットメソッドを使用できるかどうかを問い合わせます。
	 *
	 * @return  使用可能(true)/ 使用不可能 (false)
	 */
	@Override
	public boolean canUseFormat() {
		return true;
	}

	/**
	 * 表示項目の編集(並び替え)が可能かどうかを返します。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規追加
	 *
	 * @return	表示項目の編集(並び替え)が可能かどうか(false:不可能)
	 */
	@Override
	public boolean isEditable() {
		return false;
	}
}
