2011-08-28 23:14:46 (Sun)
package tag;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.Tag;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.apache.struts.taglib.TagUtils;
import org.apache.struts.util.MessageResources;
/**
 * パンくずリストタグのハンドラクラス。
 * 
 * @author TAKAO
 */
public class BreadNaviTag extends BodyTagSupport {
	/** serialVersionUID。 */
	private static final long serialVersionUID = 1L;
	/** ログ出力。 */
	private static final Log LOGGER = LogFactory.getLog( BreadNaviTag.class );
	/** アクション名ならび */
	private String actions;
	/** ナビリンクを配置する <td>タグに対するCSSクラス名称。 */
	private String naviCellClass;
	/** 末端ナビリンクを配置する <td>タグに対するCSSクラス名称。 */
	private String naviTailCellClass;
	/** ナビリンク <a>タグに対するCSSクラス名称。 */
	private String naviLinkClass;
	/** 末端ナビリンク  <a>タグに対するCSSクラス名称。 */
	private String naviTailLinkClass;
	
	/**
	 *  末端をリンクで出力するか否かの設定。
	 *  リンクとする場合、true。
	 *  リンクとしない場合、false。
	 */
	private boolean tailLinked;
	/**
	 * テーブル全体のborder属性。
	 */
	private String border;
	/**
	 * テーブル全体のcellpadding属性。
	 */
	private String cellpadding;
	/**
	 * テーブル全体のcellspacing属性。
	 */
	private String cellspacing;
	/**
	 * テーブル全体のCSSクラス名称。
	 */
	private String styleClass;
	/**
	 * <tr>タグに対するCSSクラス名称。
	 */
	private String rowClass;
	/**
	 * ボディ部評価後の処理。 <br/>
	 * ボディ部分の文字列をトリム取得して、アクション並びとして保存します。
	 * 
	 * @return {@link Tag#SKIP_BODY}
	 * @throws JspException
	 *             ボディ部の文字列を読み取りに失敗したとき。
	 * @see BodyTagSupport#doAfterBody()
	 */
	@Override
	public int doAfterBody() throws JspException {
		BufferedReader reader = null;
		StringWriter stringWriter = null;
		PrintWriter printWriter = null;
		try {
			reader = new BufferedReader( this.bodyContent.getReader() );
			stringWriter = new StringWriter();
			printWriter = new PrintWriter( stringWriter );
			while ( true) {
				String line = reader.readLine();
				if ( line == null ) {
					break;
				}
				printWriter.println( line );
			}
			printWriter.close();
			printWriter = null;
			this.actions = stringWriter.toString().trim();
			BreadNaviTag.LOGGER.debug( "actions=[" + this.actions + "]" );
		}
		catch ( IOException e ) {
			new JspException( e );
		}
		finally {
			if ( reader != null ) {
				try {
					reader.close();
				}
				catch ( IOException e ) {
					BreadNaviTag.LOGGER.error( e, e );
				}
			}
		}
		return Tag.SKIP_BODY;
	}
	/**
	 * 終了タグの評価します。
	 * 
	 * @return {@link Tag#EVAL_PAGE}
	 * @throws JspException
	 *             出力に失敗した場合。
	 * @see BodyTagSupport#doEndTag()
	 */
	@Override
	public int doEndTag() throws JspException {
		try {
			this.doEndTagMain();
		}
		catch ( IOException e ) {
			throw new JspException( e );
		}
		return Tag.EVAL_PAGE;
	}
	/**
	 * 終了タグの評価時の処理を実施します。
	 * 
	 * @throws IOException 出力に失敗した場合。
	 */
	private void doEndTagMain() throws IOException {
		ServletRequest request = this.pageContext.getRequest();
		String actions = (String) request.getAttribute(
				BreadNaviTag.class.getName() );
		if ( !BreadNaviTag.isEmpty( actions ) ) {
			this.actions = actions;
		}
		BreadNaviTag.LOGGER.debug( "actions=[" + this.actions + "]" );
		if ( BreadNaviTag.isEmpty( this.actions ) ) {
			return;
		}
		String[] actionNames = this.actions.split( "," );
		BreadNaviTag.LOGGER.debug( "actionNames.length=[" + actionNames.length + "]" );
		if ( ( actionNames == null ) || ( actionNames.length == 0 ) ) {
			return;
		}
		JspWriter out = this.pageContext.getOut();
		// <table>タグ
		out.print( "<table" );
		if (! BreadNaviTag.isEmpty( this.border )) {
			out.print( " border=\"" + this.border + "\"" );
		}
		if (! BreadNaviTag.isEmpty( this.cellpadding )) {
			out.print( " cellpadding=\"" + this.cellpadding + "\"" );
		}
		if (! BreadNaviTag.isEmpty( this.cellspacing )) {
			out.print( " cellspacing=\"" + this.cellspacing + "\"" );
		}
		if (! BreadNaviTag.isEmpty( this.styleClass )) {
			out.print( " class=\"" + this.styleClass + "\"" );
		}
		out.print( ">" );
		out.println();
		// <tr>タグ
		out.print( "<tr" );
		if (! BreadNaviTag.isEmpty( this.rowClass )) {
			out.print( " class=\"" + this.rowClass + "\"" );
		}
		out.print( ">" );
		out.println();
		int actionNamesLength = actionNames.length;
		for ( int index = 0 ; index < actionNamesLength - 1 ; index++ ) {
			String actionName = actionNames[ index ];
			this.outputLink( actionName, this.naviCellClass, this.naviLinkClass );
		}
		if ( actionNamesLength > 0 ) {
			String actionName = actionNames[ actionNamesLength - 1 ];
			String naviCellClass = this.naviTailCellClass;
			if ( BreadNaviTag.isEmpty( naviCellClass ) ) {
				naviCellClass = this.naviCellClass;
			}
			String naviLinkClass = this.naviTailLinkClass;
			if ( BreadNaviTag.isEmpty( naviLinkClass ) ) {
				naviLinkClass = this.naviLinkClass;
			}
			if (this.tailLinked) {
				this.outputLink( actionName, naviCellClass, naviLinkClass );
			}
			else {
				this.outputBreadNaviCell( actionName, naviCellClass, naviLinkClass );
			}
		}
		out.println( "</tr>" );
		out.println( "</table>" );
	}
	/**
	 * リンクとなっていないセルを出力します。
	 * 
	 * @param actionName アクション名称。この名称はあくまでリンクの
	 * @param naviCellClass <td>の CSSクラス。
	 * @param naviLinkClass 文言に設定するCSSクラス。
	 * @throws IOException 出力に失敗した場合。
	 */
	private void outputBreadNaviCell(
			String actionName,
			String naviCellClass, String naviLinkClass ) throws IOException {
		// アクション名称に対応するリンク・タイトルを取得
		String title = getBreadNaviTitle( actionName );
		
		JspWriter out = this.pageContext.getOut();
		// TDタグ
		out.print( "<td" );
		// セルのCSSクラス
		if ( !BreadNaviTag.isEmpty( naviCellClass ) ) {
			out.print( " class=\"" );
			out.print( naviCellClass );
			out.print( "\"" );
		}
		out.print( ">" );
		// 文言に対するCSSクラス
		if ( !BreadNaviTag.isEmpty( naviLinkClass ) ) {
			out.print( "<span class=\"" );
			out.print( naviLinkClass );
			out.print( "\"" );
			out.print( ">" );
		}
		// タイトルの出力
		out.print( title );
		if ( !BreadNaviTag.isEmpty( naviLinkClass ) ) {
			out.print( "</span>" );	
		}
		// 終了タグの出力
		out.print( "</td>" );
		out.println();
	}
	/**
	 * 指定したアクション名称に対応するリンク・セルを出力します。
	 * 
	 * @param actionName アクション名称。
	 * @param naviCellClass リンクを囲む <td>の CSSクラス。
	 * @param naviLinkClass リンクに設定するCSSクラス。
	 * @throws IOException 出力に失敗した場合。
	 */
	private void outputLink(
			String actionName, String naviCellClass, String naviLinkClass )
			throws IOException {
		// アクション名称に対応するリンク・タイトルを取得
		String title = getBreadNaviTitle( actionName );
		// アクション名称に対応するリンクURLを取得
		TagUtils tagUtils = TagUtils.getInstance();
		String url = tagUtils.getActionMappingURL(
				actionName, null, this.pageContext, false );
		JspWriter out = this.pageContext.getOut();
		// TDタグ
		out.print( "<td" );
		// セルのCSSクラス
		if ( !BreadNaviTag.isEmpty( naviCellClass ) ) {
			out.print( " class=\"" );
			out.print( naviCellClass );
			out.print( "\"" );
		}
		out.print( ">" );
		// リンク
		out.print( "<a" );
		// リンクのCSSクラス
		if ( !BreadNaviTag.isEmpty( naviLinkClass ) ) {
			out.print( " class=\"" );
			out.print( naviLinkClass );
			out.print( "\"" );
		}
		
		// リンク先の設定
		out.print( " href=\"" );
		out.print( url );
		out.print( "\"" );
		out.print( ">" );
		// タイトルの出力
		out.print( title );
		// 終了タグの出力
		out.print( "</a>" );
		out.print( "</td>" );
		out.println();
	}
	/**
	 * 指定したアクション名称に対応するパンくずタイトル(リンク文言)を取得します。
	 * @param actionName アクション名称。
	 * @return パンくずタイトル。
	 * @see MessageResources
	 */
	private String getBreadNaviTitle( String actionName ) {
		// Strutsが使用するメッセージ・リソース。
		MessageResources mr = (MessageResources) this.pageContext
				.getServletContext().getAttribute( Globals.MESSAGES_KEY );
		
		// リンクに表示する文字列を取得します。
		String title = mr.getMessage( "breadnavi." + actionName );
		return title;
	}
	/**
	 * 指定した文字列が空文字列であるか判定します。
	 * 
	 * @param str 判定対象の文字列。
	 * @return 引数の文字列が null または 長さ0の場合、true。その他はfalse。
	 */
	private static boolean isEmpty( String str ) {
		return ( ( str == null ) || ( str.length() == 0 ) );
	}
	/**
	 * ナビリンクを配置する<td>タグに対するCSSクラス名称を設定します。
	 * 
	 * @param naviCellClass ナビリンクを配置するセルのCSSクラス名称。
	 */
	public void setNaviCellClass( String naviCellClass ) {
		this.naviCellClass = naviCellClass;
	}
	/**
	 * 末端ナビリンクを配置する<td>タグに対するCSSクラス名称を設定します。
	 * 
	 * @param naviTailCellClass 末端ナビリンクを配置するセルのCSSクラス名称。
	 */
	public void setNaviTailCellClass( String naviTailCellClass ) {
		this.naviTailCellClass = naviTailCellClass;
	}
	/**
	 * ナビリンクの<a>タグに対するCSSクラス名称を設定します。
	 * 
	 * @param naviLinkClass ナビリンクのCSSクラス名称。
	 */
	public void setNaviLinkClass( String naviLinkClass ) {
		this.naviLinkClass = naviLinkClass;
	}
	/**
	 * 末端ナビリンクの<a>タグに対するCSSクラス名称を設定します。
	 * 
	 * @param naviLinkCellClass 末端ナビリンクのCSSクラス名称。
	 */
	public void setNaviTailLinkClass( String naviLinkCellClass ) {
		this.naviTailLinkClass = naviLinkCellClass;
	}
	/**
	 *  末端をリンクで出力するか否かの設定します。
	 * @param tailLinked リンクとする場合、true。リンクとしない場合、false。
	 */
	public void setTailLinked( Object tailLinked ) {
		// null であればリンクにしない
		if (tailLinked == null) {
			this.tailLinked = false;
			return;
		}
		
		// Booleanであれば、値を取得
		if (Boolean.class.isInstance( tailLinked) ){
			this.tailLinked = ((Boolean)tailLinked).booleanValue();
			return;
		}
		
		// String であれば、parseする
		if (String.class.isInstance( tailLinked )) {
			this.tailLinked = Boolean.parseBoolean( (String)tailLinked );
			return;
		}
		// その他は例外
		throw new IllegalArgumentException(
				"the attribute \"tailLinked\" cannot be parsed boolean." 
				 + " value=[" + tailLinked +"] class=[" + tailLinked.getClass() + "]");
	}
	/**
	 * テーブル全体のborder属性を設定します。
	 * @param border テーブル全体のborder属性。
	 */
	public void setBorder( String border ) {
		this.border = border;
	}
	/**
	 * テーブル全体のcellpadding属性を設定します。
	 * @param cellpadding テーブル全体のcellpadding属性。
	 */
	public void setCellpadding( String cellpadding ) {
		this.cellpadding = cellpadding;
	}
	/**
	 * テーブル全体のcellspacing属性を設定します。
	 * @param cellspacing テーブル全体のcellspacing属性。
	 */
	public void setCellspacing( String cellspacing ) {
		this.cellspacing = cellspacing;
	}
	/**
	 * テーブル全体のCSSクラス名称を設定します。
	 * @param styleClass テーブル全体のCSSクラス名称。
	 */
	public void setStyleClass( String styleClass ) {
		this.styleClass = styleClass;
	}
	/**
	 * <tr>タグに対するCSSクラス名称を設定します。
	 * @param rowClass <tr>タグに対するCSSクラス名称。
	 */
	public void setRowClass( String rowClass ) {
		this.rowClass = rowClass;
	}
}
最終更新:2011年08月28日 23:14