/**
* Licensed under the Artistic License; you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://displaytag.sourceforge.net/license.html
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.displaytag.render;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.displaytag.exception.DecoratorException;
import org.displaytag.exception.ObjectLookupException;
import org.displaytag.exception.WrappedRuntimeException;
import org.displaytag.model.Column;
import org.displaytag.model.HeaderCell;
import org.displaytag.model.Row;
import org.displaytag.model.TableModel;
import org.displaytag.pagination.PaginatedList;
import org.displaytag.pagination.SmartListHelper;
import org.displaytag.properties.MediaTypeEnum;
import org.displaytag.properties.SortOrderEnum;
import org.displaytag.properties.TableProperties;
import org.displaytag.tags.CaptionTag;
import org.displaytag.tags.TableTagParameters;
import org.displaytag.util.Anchor;
import org.displaytag.util.Href;
import org.displaytag.util.HtmlAttributeMap;
import org.displaytag.util.ParamEncoder;
import org.displaytag.util.TagConstants;
/**
* A table writer that formats a table in HTML and writes it to a JSP page.
* @author Fabrizio Giustina
* @author Jorge L. Barroso
* @version $Id: HtmlTableWriter.java 8904 2006-05-01 18:02:06 -0400 (Mon, 01 May 2006) charles $
* @see org.displaytag.render.TableWriterTemplate
* @since 1.1
*/
public class HtmlTableWriter extends TableWriterAdapter
{
/**
* Logger.
*/
private static Log log = LogFactory.getLog(HtmlTableWriter.class);
/**
* <code>TableModel</code>
*/
private TableModel tableModel;
/**
* <code>TableProperties</code>
*/
private TableProperties properties;
/**
* Output destination.
*/
private JspWriter out;
/**
* The param encoder used to generate unique parameter names. Initialized at the first use of encodeParameter().
*/
private ParamEncoder paramEncoder;
/**
* base href used for links.
*/
private Href baseHref;
/**
* add export links.
*/
private boolean export;
private CaptionTag captionTag;
/**
* The paginated list containing the external pagination and sort parameters The presence of this paginated list is
* what determines if external pagination and sorting is used or not.
*/
private PaginatedList paginatedList;
/**
* Used by various functions when the person wants to do paging.
*/
private SmartListHelper listHelper;
/**
* page size.
*/
private int pagesize;
private HtmlAttributeMap attributeMap;
/**
* Unique table id.
*/
private String uid;
/**
* This table writer uses a <code>TableTag</code> and a <code>JspWriter</code> to do its work.
* @param tableTag <code>TableTag</code> instance called back by this writer.
* @param out The output destination.
*/
public HtmlTableWriter(
TableModel tableModel,
TableProperties tableProperties,
Href baseHref,
boolean export,
JspWriter out,
CaptionTag captionTag,
PaginatedList paginatedList,
SmartListHelper listHelper,
int pagesize,
HtmlAttributeMap attributeMap,
String uid)
{
this.tableModel = tableModel;
this.properties = tableProperties;
this.baseHref = baseHref;
this.export = export;
this.out = out;
this.captionTag = captionTag;
this.paginatedList = paginatedList;
this.listHelper = listHelper;
this.pagesize = pagesize;
this.attributeMap = attributeMap;
this.uid = uid;
}
/**
* Writes a banner containing search result and paging navigation above an HTML table to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTopBanner(org.displaytag.model.TableModel)
*/
protected void writeTopBanner(TableModel model)
{
writeSearchResultAndNavigation();
}
/**
* Writes an HTML table's opening tags to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel)
*/
protected void writeTableOpener(TableModel model)
{
this.write(getOpenTag());
}
/**
* Writes an HTML table's caption to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel)
*/
protected void writeCaption(TableModel model)
{
this.write(captionTag.getOpenTag() + model.getCaption() + captionTag.getCloseTag());
}
/**
* Writes an HTML table's footer to a JSP page; HTML requires tfoot to appear before tbody.
* @see org.displaytag.render.TableWriterTemplate#writeFooter(org.displaytag.model.TableModel)
*/
protected void writePreBodyFooter(TableModel model)
{
this.write(TagConstants.TAG_TFOOTER_OPEN);
this.write(model.getFooter());
this.write(TagConstants.TAG_TFOOTER_CLOSE);
}
/**
* Writes the start of an HTML table's body to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTableBodyOpener(org.displaytag.model.TableModel)
*/
protected void writeTableBodyOpener(TableModel model)
{
this.write(TagConstants.TAG_TBODY_OPEN);
}
/**
* Writes the end of an HTML table's body to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTableBodyCloser(org.displaytag.model.TableModel)
*/
protected void writeTableBodyCloser(TableModel model)
{
this.write(TagConstants.TAG_TBODY_CLOSE);
}
/**
* Writes the closing structure of an HTML table to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTableCloser(org.displaytag.model.TableModel)
*/
protected void writeTableCloser(TableModel model)
{
this.write(TagConstants.TAG_OPENCLOSING);
this.write(TagConstants.TABLE_TAG_NAME);
this.write(TagConstants.TAG_CLOSE);
}
/**
* Writes a banner containing search result, paging navigation, and export links below an HTML table to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeBottomBanner(org.displaytag.model.TableModel)
*/
protected void writeBottomBanner(TableModel model)
{
writeNavigationAndExportLinks();
}
/**
* @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel)
*/
protected void writeDecoratedTableFinish(TableModel model)
{
model.getTableDecorator().finish();
}
/**
* @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel)
*/
protected void writeDecoratedRowStart(TableModel model)
{
this.write(model.getTableDecorator().startRow());
}
/**
* Writes an HTML table's row-opening tag to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeRowOpener(org.displaytag.model.Row)
*/
protected void writeRowOpener(Row row)
{
this.write(row.getOpenTag());
}
/**
* Writes an HTML table's column-opening tag to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column)
*/
protected void writeColumnOpener(Column column) throws ObjectLookupException, DecoratorException
{
this.write(column.getOpenTag());
}
/**
* Writes an HTML table's column-closing tag to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeColumnCloser(org.displaytag.model.Column)
*/
protected void writeColumnCloser(Column column)
{
this.write(column.getCloseTag());
}
/**
* Writes to a JSP page an HTML table row that has no columns.
* @see org.displaytag.render.TableWriterTemplate#writeRowWithNoColumns(java.lang.String)
*/
protected void writeRowWithNoColumns(String rowValue)
{
this.write(TagConstants.TAG_TD_OPEN);
this.write(rowValue);
this.write(TagConstants.TAG_TD_CLOSE);
}
/**
* Writes an HTML table's row-closing tag to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeRowCloser(org.displaytag.model.Row)
*/
protected void writeRowCloser(Row row)
{
this.write(row.getCloseTag());
}
/**
* @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel)
*/
protected void writeDecoratedRowFinish(TableModel model)
{
this.write(model.getTableDecorator().finishRow());
}
/**
* Writes an HTML message to a JSP page explaining that the table model contains no data.
* @see org.displaytag.render.TableWriterTemplate#writeEmptyListMessage(java.lang.String)
*/
protected void writeEmptyListMessage(String emptyListMessage)
{
this.write(emptyListMessage);
}
/**
* Writes a HTML table column value to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeColumnValue(java.lang.String,org.displaytag.model.Column)
*/
protected void writeColumnValue(Object value, Column column)
{
this.write(value);
}
/**
* Writes an HTML message to a JSP page explaining that the row contains no data.
* @see org.displaytag.render.TableWriterTemplate#writeEmptyListRowMessage(java.lang.String)
*/
protected void writeEmptyListRowMessage(String message)
{
this.write(message);
}
/**
* Writes an HTML table's column header to a JSP page.
* @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel)
*/
protected void writeTableHeader(TableModel model)
{
if (log.isDebugEnabled())
{
log.debug("[" + tableModel.getId() + "] getTableHeader called");
}
// open thead
write(TagConstants.TAG_THEAD_OPEN);
// open tr
write(TagConstants.TAG_TR_OPEN);
// no columns?
if (this.tableModel.isEmpty())
{
write(TagConstants.TAG_TH_OPEN);
write(TagConstants.TAG_TH_CLOSE);
}
// iterator on columns for header
Iterator iterator = this.tableModel.getHeaderCellList().iterator();
while (iterator.hasNext())
{
// get the header cell
HeaderCell headerCell = (HeaderCell) iterator.next();
if (headerCell.getSortable())
{
String cssSortable = this.properties.getCssSortable();
headerCell.addHeaderClass(cssSortable);
}
// if sorted add styles
if (headerCell.isAlreadySorted())
{
// sorted css class
headerCell.addHeaderClass(this.properties.getCssSorted());
// sort order css class
headerCell.addHeaderClass(this.properties.getCssOrder(this.tableModel.isSortOrderAscending()));
}
// append th with html attributes
write(headerCell.getHeaderOpenTag());
// title
String header = headerCell.getTitle();
// column is sortable, create link
if (headerCell.getSortable())
{
// creates the link for sorting
Anchor anchor = new Anchor(getSortingHref(headerCell), header);
// append to buffer
header = anchor.toString();
}
write(header);
write(headerCell.getHeaderCloseTag());
}
// close tr
write(TagConstants.TAG_TR_CLOSE);
// close thead
write(TagConstants.TAG_THEAD_CLOSE);
if (log.isDebugEnabled())
{
log.debug("[" + tableModel.getId() + "] getTableHeader end");
}
}
/**
* Generates the link to be added to a column header for sorting.
* @param headerCell header cell the link should be added to
* @return Href for sorting
*/
private Href getSortingHref(HeaderCell headerCell)
{
// costruct Href from base href, preserving parameters
Href href = (Href) this.baseHref.clone();
if (this.paginatedList == null)
{
// add column number as link parameter
if (!this.tableModel.isLocalSort() && (headerCell.getSortName() != null))
{
href.addParameter(encodeParameter(TableTagParameters.PARAMETER_SORT), headerCell.getSortName());
href.addParameter(encodeParameter(TableTagParameters.PARAMETER_SORTUSINGNAME), "1");
}
else
{
href.addParameter(encodeParameter(TableTagParameters.PARAMETER_SORT), headerCell.getColumnNumber());
}
boolean nowOrderAscending = true;
if (headerCell.getDefaultSortOrder() != null)
{
boolean sortAscending = SortOrderEnum.ASCENDING.equals(headerCell.getDefaultSortOrder());
nowOrderAscending = headerCell.isAlreadySorted()
? !this.tableModel.isSortOrderAscending()
: sortAscending;
}
else
{
nowOrderAscending = !(headerCell.isAlreadySorted() && this.tableModel.isSortOrderAscending());
}
int sortOrderParam = nowOrderAscending ? SortOrderEnum.ASCENDING.getCode() : SortOrderEnum.DESCENDING
.getCode();
href.addParameter(encodeParameter(TableTagParameters.PARAMETER_ORDER), sortOrderParam);
// If user want to sort the full table I need to reset the page number.
// or if we aren't sorting locally we need to reset the page as well.
if (this.tableModel.isSortFullTable() || !this.tableModel.isLocalSort())
{
href.addParameter(encodeParameter(TableTagParameters.PARAMETER_PAGE), 1);
}
}
else
{
if (properties.getPaginationSkipPageNumberInSort())
{
href.removeParameter(properties.getPaginationPageNumberParam());
}
String sortProperty = headerCell.getSortProperty();
if (sortProperty == null)
{
sortProperty = headerCell.getBeanPropertyName();
}
href.addParameter(properties.getPaginationSortParam(), sortProperty);
String dirParam;
if (headerCell.isAlreadySorted())
{
dirParam = tableModel.isSortOrderAscending() ? properties.getPaginationDescValue() : properties
.getPaginationAscValue();
}
else
{
dirParam = properties.getPaginationAscValue();
}
href.addParameter(properties.getPaginationSortDirectionParam(), dirParam);
if (paginatedList.getSearchId() != null)
{
href.addParameter(properties.getPaginationSearchIdParam(), paginatedList.getSearchId());
}
}
return href;
}
/**
* encode a parameter name to be unique in the page using ParamEncoder.
* @param parameterName parameter name to encode
* @return String encoded parameter name
*/
private String encodeParameter(String parameterName)
{
// paramEncoder has been already instantiated?
if (this.paramEncoder == null)
{
// use the id attribute to get the unique identifier
this.paramEncoder = new ParamEncoder(this.tableModel.getId());
}
return this.paramEncoder.encodeParameterName(parameterName);
}
/**
* Generates table footer with links for export commands.
*/
public void writeNavigationAndExportLinks()
{
// Put the page stuff there if it needs to be there...
if (this.properties.getAddPagingBannerBottom())
{
writeSearchResultAndNavigation();
}
// add export links (only if the table is not empty)
if (this.export && this.tableModel.getRowListPage().size() != 0)
{
writeExportLinks();
}
}
/**
* generates the search result and navigation bar.
*/
public void writeSearchResultAndNavigation()
{
if ((this.paginatedList == null && this.pagesize != 0 && this.listHelper != null)
|| (this.paginatedList != null))
{
// create a new href
Href navigationHref = (Href) this.baseHref.clone();
write(this.listHelper.getSearchResultsSummary());
String pageParameter;
if (paginatedList == null)
{
pageParameter = encodeParameter(TableTagParameters.PARAMETER_PAGE);
}
else
{
pageParameter = properties.getPaginationPageNumberParam();
if ((paginatedList.getSearchId() != null)
&& (!navigationHref.getParameterMap().containsKey(properties.getPaginationSearchIdParam())))
{
navigationHref.addParameter(properties.getPaginationSearchIdParam(), paginatedList.getSearchId());
}
}
write(this.listHelper.getPageNavigationBar(navigationHref, pageParameter));
}
}
/**
* Writes the formatted export links section.
*/
private void writeExportLinks()
{
// Figure out what formats they want to export, make up a little string
Href exportHref = (Href) this.baseHref.clone();
StringBuffer buffer = new StringBuffer(200);
Iterator iterator = MediaTypeEnum.iterator();
while (iterator.hasNext())
{
MediaTypeEnum currentExportType = (MediaTypeEnum) iterator.next();
if (this.properties.getAddExport(currentExportType))
{
if (buffer.length() > 0)
{
buffer.append(this.properties.getExportBannerSeparator());
}
exportHref.addParameter(encodeParameter(TableTagParameters.PARAMETER_EXPORTTYPE), currentExportType
.getCode());
// export marker
exportHref.addParameter(TableTagParameters.PARAMETER_EXPORTING, "1");
Anchor anchor = new Anchor(exportHref, this.properties.getExportLabel(currentExportType));
buffer.append(anchor.toString());
}
}
String[] exportOptions = {buffer.toString()};
write(MessageFormat.format(this.properties.getExportBanner(), exportOptions));
}
/**
* create the open tag containing all the attributes.
* @return open tag string: <code>%lt;table attribute="value" ... ></code>
*/
public String getOpenTag()
{
if (this.uid != null && attributeMap.get(TagConstants.ATTRIBUTE_ID) == null)
{
// we need to clone the attribute map in order to "fix" the html id when using only the "uid" attribute
Map localAttributeMap = (Map) attributeMap.clone();
localAttributeMap.put(TagConstants.ATTRIBUTE_ID, this.uid);
StringBuffer buffer = new StringBuffer();
buffer.append(TagConstants.TAG_OPEN).append(TagConstants.TABLE_TAG_NAME);
buffer.append(localAttributeMap);
buffer.append(TagConstants.TAG_CLOSE);
return buffer.toString();
}
// fast, no clone
StringBuffer buffer = new StringBuffer();
buffer.append(TagConstants.TAG_OPEN).append(TagConstants.TABLE_TAG_NAME);
buffer.append(attributeMap);
buffer.append(TagConstants.TAG_CLOSE);
return buffer.toString();
}
/**
* Utility method.
* @param string String
*/
public void write(String string)
{
if (string != null)
{
try
{
out.write(string);
}
catch (IOException e)
{
throw new WrappedRuntimeException(getClass(), e);
}
}
}
public void writeTable(TableModel model, String id) throws JspException
{
super.writeTable(model, id);
}
/**
* Utility method.
* @param string String
*/
public void write(Object string)
{
if (string != null)
{
try
{
out.write(string.toString());
}
catch (IOException e)
{
throw new WrappedRuntimeException(getClass(), e);
}
}
}
}