
org.apache.myfaces.renderkit.html.HtmlTableRendererBase Maven / Gradle / Ivy
Show all versions of myfaces-commons Show documentation
/*
* Copyright 2004 The Apache Software Foundation.
*
* 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.apache.myfaces.renderkit.html;
import org.apache.myfaces.renderkit.JSFAttr;
import org.apache.myfaces.renderkit.RendererUtils;
import org.apache.myfaces.util.ArrayUtils;
import org.apache.myfaces.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Common methods for renderers for components that subclass the standard
* JSF HtmlDataTable component.
*
* @author Thomas Spiegl (latest modification by $Author: skitching $)
* @version $Revision: 358448 $ $Date: 2005-12-22 02:52:45 +0000 (Thu, 22 Dec 2005) $
*/
public class HtmlTableRendererBase extends HtmlRenderer
{
/** Header facet name. */
protected static final String HEADER_FACET_NAME = "header";
/** Footer facet name. */
protected static final String FOOTER_FACET_NAME = "footer";
/** The logger. */
private static final Log log = LogFactory.getLog(HtmlTableRendererBase.class);
/**
* @see javax.faces.render.Renderer#getRendersChildren()
*/
public boolean getRendersChildren()
{
return true;
}
/**
* Render the necessary bits that come before any actual rows in the table.
*
* @see javax.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
*/
public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
{
RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
ResponseWriter writer = facesContext.getResponseWriter();
beforeTable(facesContext, (UIData) uiComponent);
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
writer.startElement(HTML.TABLE_ELEM, uiComponent);
HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
}
/**
* Render the TBODY section of the html table. See also method encodeInnerHtml.
*
* @see javax.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
*/
public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
{
RendererUtils.checkParamValidity(facesContext, component, UIData.class);
ResponseWriter writer = facesContext.getResponseWriter();
beforeBody(facesContext, (UIData) component);
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
writer.startElement(HTML.TBODY_ELEM, component);
writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext)+":tbody_element", null);
encodeInnerHtml(facesContext, component);
writer.endElement(HTML.TBODY_ELEM);
afterBody(facesContext, (UIData) component);
}
/**
* Renders everything inside the TBODY tag by iterating over the row objects
* between offsets first and first+rows and applying the UIColumn components
* to those objects.
*
* This method is separated from the encodeChildren so that it can be overridden by
* subclasses. One class that uses this functionality is autoUpdateDataTable.
*/
public void encodeInnerHtml(FacesContext facesContext, UIComponent component)throws IOException{
UIData uiData = (UIData) component;
ResponseWriter writer = facesContext.getResponseWriter();
String rowClasses;
String columnClasses;
if (component instanceof HtmlDataTable)
{
rowClasses = ((HtmlDataTable) component).getRowClasses();
columnClasses = ((HtmlDataTable) component).getColumnClasses();
}
else
{
rowClasses = (String) component.getAttributes().get(JSFAttr.ROW_CLASSES_ATTR);
columnClasses = (String) component.getAttributes().get(JSFAttr.COLUMN_CLASSES_ATTR);
}
Iterator rowStyleIterator = new StyleIterator(rowClasses);
StyleIterator columnStyleIterator = new StyleIterator(columnClasses);
int first = uiData.getFirst();
int rows = uiData.getRows();
int rowCount = uiData.getRowCount();
if (rows <= 0)
{
rows = rowCount - first;
}
int last = first + rows;
if (last > rowCount)
last = rowCount;
for (int i = first; i < last; i++)
{
uiData.setRowIndex(i);
if (!uiData.isRowAvailable())
{
log.error("Row is not available. Rowindex = " + i);
return;
}
columnStyleIterator.reset();
beforeRow(facesContext, uiData);
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
renderRowStart(facesContext, writer, uiData, rowStyleIterator);
List children = getChildren(component);
for (int j = 0, size = getChildCount(component); j < size; j++)
{
UIComponent child = (UIComponent) children.get(j);
if(child.isRendered())
{
encodeColumnChild(facesContext, writer, uiData, child, columnStyleIterator);
}
}
renderRowEnd(facesContext, writer, uiData);
afterRow(facesContext, uiData);
}
}
protected void encodeColumnChild(FacesContext facesContext, ResponseWriter writer,
UIData uiData, UIComponent component, Iterator columnStyleIterator) throws IOException
{
if (component instanceof UIColumn)
{
renderColumnBody(facesContext, writer, uiData, component, columnStyleIterator);
}
}
/**
* Renders the body of a given UIColumn
(everything but
* the header and footer facets). This emits a TD cell, whose contents
* are the result of calling encodeBegin, encodeChildren and
* encodeEnd methods on the component (or its associated renderer).
*
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiData the UIData
being rendered.
* @param component the UIComponent
to render.
* @param columnStyleIterator the styleClass of the UIColumn
or null
if
* there is none.
* @throws IOException if an exception occurs.
*/
protected void renderColumnBody(
FacesContext facesContext,
ResponseWriter writer,
UIData uiData,
UIComponent component,
Iterator columnStyleIterator) throws IOException
{
writer.startElement(HTML.TD_ELEM, uiData);
if (columnStyleIterator.hasNext())
{
writer.writeAttribute(HTML.CLASS_ATTR, columnStyleIterator.next(), null);
}
RendererUtils.renderChild(facesContext, component);
writer.endElement(HTML.TD_ELEM);
}
/**
* Renders the start of a new row of body content.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiData the UIData
being rendered.
* @param rowStyleIterator te styleClass of the row or null
if there is none.
* @throws IOException if an exceptoin occurs.
*/
protected void renderRowStart(
FacesContext facesContext,
ResponseWriter writer,
UIData uiData,
Iterator rowStyleIterator) throws IOException
{
writer.startElement(HTML.TR_ELEM, uiData);
renderRowStyle(facesContext, writer, uiData, rowStyleIterator);
Object rowId = uiData.getAttributes().get(JSFAttr.ROW_ID);
if (rowId != null)
{
writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
}
}
protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, UIData uiData, Iterator rowStyleIterator) throws IOException
{
if (rowStyleIterator.hasNext())
{
writer.writeAttribute(HTML.CLASS_ATTR, rowStyleIterator.next(), null);
}
}
/**
* Renders the end of a row of body content.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiData the UIData
being rendered.
* @throws IOException if an exceptoin occurs.
*/
protected void renderRowEnd(
FacesContext facesContext,
ResponseWriter writer,
UIData uiData) throws IOException
{
writer.endElement(HTML.TR_ELEM);
}
/**
* Perform any operations necessary immediately before the TABLE start tag
* is output.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void beforeTable(FacesContext facesContext, UIData uiData) throws IOException
{
}
/**
* Perform any operations necessary after TABLE start tag is output
* but before the TBODY start tag.
*
* This method generates the THEAD/TFOOT sections of a table if there
* are any header or footer facets defined on the table or on any child
* UIColumn component.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException
{
ResponseWriter writer = facesContext.getResponseWriter();
renderFacet(facesContext, writer, uiData, true);
renderFacet(facesContext, writer, uiData, false);
}
/**
* Perform any operations necessary immediately before each TR start tag
* is output.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException
{
}
/**
* Perform any operations necessary immediately after each TR end tag
* is output.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException
{
}
/**
* Perform any operations necessary immediately after the TBODY end tag
* is output.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void afterBody(FacesContext facesContext, UIData uiData) throws IOException
{
}
/**
* Perform any operations necessary immediately after the TABLE end tag
* is output.
*
* @param facesContext the FacesContext
.
* @param uiData the UIData
being rendered.
*/
protected void afterTable(FacesContext facesContext, UIData uiData) throws IOException
{
}
/**
* @see javax.faces.render.Renderer#encodeEnd(FacesContext, UIComponent)
*/
public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
{
RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
ResponseWriter writer = facesContext.getResponseWriter();
writer.endElement(HTML.TABLE_ELEM);
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
afterTable(facesContext, (UIData) uiComponent);
}
/**
* Renders either the header or the footer facets for the UIData component
* and all the child UIColumn components, as a THEAD or TFOOT element
* containing TR (row) elements.
*
* If there is a header or footer attached to the UIData then that is
* rendered as a TR element whose COLSPAN is the sum of all rendered
* columns in the table. This allows that header/footer to take up the
* entire width of the table.
*
* If any child column has a header or footer then a TR is rendered
* with a TH cell for each column child.
*
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param component the UIData component
* @param header whether this is the header facet (if not, then the footer facet).
* @throws IOException if an exception occurs.
*/
protected void renderFacet(FacesContext facesContext, ResponseWriter writer,
UIComponent component, boolean header)
throws IOException
{
int colspan = 0;
boolean hasColumnFacet = false;
for (Iterator it = getChildren(component).iterator(); it.hasNext();)
{
UIComponent uiComponent = (UIComponent) it.next();
if(uiComponent.isRendered())
{
// a UIColumn has a span of 1, anything else has a span of 0
colspan += determineChildColSpan(uiComponent);
// hasColumnFacet is true if *any* child column has a facet of
// the specified type.
if (!hasColumnFacet)
{
hasColumnFacet = hasFacet(header, uiComponent);
}
}
}
UIComponent facet = header ? (UIComponent) component.getFacets().get(HEADER_FACET_NAME)
: (UIComponent) component.getFacets().get(FOOTER_FACET_NAME);
if (facet != null || hasColumnFacet)
{
// Header or Footer present on either the UIData or a column, so we
// definitely need to render the THEAD or TFOOT section.
String elemName = header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM;
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
writer.startElement(elemName, component);
if (header)
{
String headerStyleClass = getHeaderClass(component);
if (facet != null)
renderTableHeaderRow(facesContext, writer, component, facet, headerStyleClass, colspan);
if (hasColumnFacet)
renderColumnHeaderRow(facesContext, writer, component, headerStyleClass);
}
else
{
String footerStyleClass = getFooterClass(component);
if (hasColumnFacet)
renderColumnFooterRow(facesContext, writer, component, footerStyleClass);
if (facet != null)
renderTableFooterRow(facesContext, writer, component, facet, footerStyleClass, colspan);
}
writer.endElement(elemName);
}
}
/**
* @param header
* @param uiComponent
* @return boolean
*/
protected boolean hasFacet(boolean header, UIComponent uiComponent)
{
if (uiComponent instanceof UIColumn)
{
UIColumn uiColumn = (UIColumn) uiComponent;
return header ? uiColumn.getHeader() != null : uiColumn.getFooter() != null;
}
return false;
}
/**
* Calculate the number of columns the specified child component will span
* when rendered.
*
* Normally, this is a fairly simple calculation: a UIColumn component
* is rendered as one column, every other child type is not rendered
* (ie spans zero columns). However custom subclasses of this renderer may
* override this method to handle cases where a single component renders
* as multiple columns.
*/
protected int determineChildColSpan(UIComponent uiComponent)
{
if (uiComponent instanceof UIColumn)
{
return 1;
}
return 0;
}
/**
* Renders the header row of the table being rendered.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param component the UIComponent
for whom a table is being rendered.
* @param headerFacet the facet for the header.
* @param headerStyleClass the styleClass of the header.
* @param colspan the number of columns the header should span. Typically, this is
* the number of columns in the table.
* @throws IOException if an exception occurs.
*/
protected void renderTableHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
UIComponent headerFacet, String headerStyleClass, int colspan) throws IOException
{
renderTableHeaderOrFooterRow(facesContext, writer, component, headerFacet, headerStyleClass, HTML.TH_ELEM,
colspan);
}
/**
* Renders the footer row of the table being rendered.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param component the UIComponent
for whom a table is being rendered.
* @param footerFacet the facet for the footer.
* @param footerStyleClass the styleClass of the footer.
* @param colspan the number of columns the header should span. Typically, this is
* the number of columns in the table.
* @throws IOException if an exception occurs.
*/
protected void renderTableFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
UIComponent footerFacet, String footerStyleClass, int colspan) throws IOException
{
renderTableHeaderOrFooterRow(facesContext, writer, component, footerFacet, footerStyleClass, HTML.TD_ELEM,
colspan);
}
/**
* Renders the header row for the columns, which is a separate row from the header row for the
* UIData
header facet.
*
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param component the UIData component for whom a table is being rendered.
* @param headerStyleClass the styleClass of the header
* @throws IOException if an exception occurs.
*/
protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
String headerStyleClass) throws IOException
{
renderColumnHeaderOrFooterRow(facesContext, writer, component, headerStyleClass, true);
}
/**
* Renders the footer row for the columns, which is a separate row from the footer row for the
* UIData
footer facet.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param component the UIComponent
for whom a table is being rendered.
* @param footerStyleClass the styleClass of the footerStyleClass
* @throws IOException if an exception occurs.
*/
protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
String footerStyleClass) throws IOException
{
renderColumnHeaderOrFooterRow(facesContext, writer, component, footerStyleClass, false);
}
private void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
UIComponent facet, String styleClass, String colElementName, int colspan) throws IOException
{
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
writer.startElement(HTML.TR_ELEM, component);
writer.startElement(colElementName, component);
if (colElementName.equals(HTML.TH_ELEM))
{
writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null);
}
writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
if (styleClass != null)
{
writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
}
if (facet != null)
{
RendererUtils.renderChild(facesContext, facet);
}
writer.endElement(colElementName);
writer.endElement(HTML.TR_ELEM);
}
/**
* @param component the UIData component for whom a table is being rendered.
*/
private void renderColumnHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer,
UIComponent component, String styleClass, boolean header) throws IOException
{
HtmlRendererUtils.writePrettyLineSeparator(facesContext);
writer.startElement(HTML.TR_ELEM, component);
for (Iterator it = getChildren(component).iterator(); it.hasNext();)
{
UIComponent uiComponent = (UIComponent) it.next();
if(uiComponent.isRendered())
{
renderColumnChildHeaderOrFooterRow(facesContext, writer, uiComponent, styleClass, header);
}
}
writer.endElement(HTML.TR_ELEM);
}
protected void renderColumnChildHeaderOrFooterRow(FacesContext facesContext,
ResponseWriter writer, UIComponent uiComponent, String styleClass, boolean header) throws IOException
{
if (uiComponent instanceof UIColumn)
{
if (header)
{
renderColumnHeaderCell(facesContext, writer, uiComponent,
((UIColumn) uiComponent).getHeader(), styleClass, 0);
}
else
{
renderColumnFooterCell(facesContext, writer, uiComponent,
((UIColumn) uiComponent).getFooter(), styleClass, 0);
}
}
}
/**
* Renders the header facet for the given UIColumn
.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiColumn the UIColumn
.
* @param headerStyleClass the styleClass of the header facet.
* @param colspan the colspan for the tableData element in which the header facet
* will be wrapped.
* @throws IOException
*/
protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
String headerStyleClass, int colspan) throws IOException
{
renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn.getHeader(), headerStyleClass, colspan);
}
/**
* Renders a TH cell within a TR within a THEAD section. If the specified
* UIColumn object does have a header facet, then that facet is rendered
* within the cell, otherwise the cell is left blank (though any specified
* style class is still applied to empty cells).
*
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiComponent the UIComponent
to render the facet for.
* @param facet the UIComponent
to render as facet.
* @param headerStyleClass the styleClass of the header facet.
* @param colspan the colspan for the tableData element in which the header facet
* will be wrapped.
* @throws IOException
*/
protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
UIComponent facet, String headerStyleClass, int colspan) throws IOException
{
writer.startElement(HTML.TH_ELEM, uiComponent);
if (colspan > 1)
{
writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
}
if (headerStyleClass != null)
{
writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass, null);
}
if (facet != null)
{
RendererUtils.renderChild(facesContext, facet);
}
writer.endElement(HTML.TH_ELEM);
}
/**
* Renders the footer facet for the given UIColumn
.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiColumn the UIComponent
.
* @param footerStyleClass the styleClass of the footer facet.
* @param colspan the colspan for the tableData element in which the footer facet
* will be wrapped.
* @throws IOException
*/
protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
String footerStyleClass, int colspan) throws IOException
{
renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn.getFooter(), footerStyleClass, colspan);
}
/**
* Renders the footer facet for the given UIColumn
.
* @param facesContext the FacesContext
.
* @param writer the ResponseWriter
.
* @param uiComponent the UIComponent
to render the facet for.
* @param facet the UIComponent
to render as facet.
* @param footerStyleClass the styleClass of the footer facet.
* @param colspan the colspan for the tableData element in which the footer facet
* will be wrapped.
* @throws IOException
*/
protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
UIComponent facet, String footerStyleClass, int colspan) throws IOException
{
writer.startElement(HTML.TD_ELEM, uiComponent);
if (colspan > 1)
{
writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
}
if (footerStyleClass != null)
{
writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null);
}
if (facet != null)
{
RendererUtils.renderChild(facesContext, facet);
}
writer.endElement(HTML.TD_ELEM);
}
/**
* Gets the headerClass attribute of the given UIComponent
.
* @param component the UIComponent
.
* @return the headerClass attribute of the given UIComponent
.
*/
protected static String getHeaderClass(UIComponent component)
{
if (component instanceof HtmlDataTable)
{
return ((HtmlDataTable) component).getHeaderClass();
}
else
{
return (String) component.getAttributes().get(JSFAttr.HEADER_CLASS_ATTR);
}
}
/**
* Gets the footerClass attribute of the given UIComponent
.
* @param component the UIComponent
.
* @return the footerClass attribute of the given UIComponent
.
*/
protected static String getFooterClass(UIComponent component)
{
if (component instanceof HtmlDataTable)
{
return ((HtmlDataTable) component).getFooterClass();
}
else
{
return (String) component.getAttributes().get(JSFAttr.FOOTER_CLASS_ATTR);
}
}
//-------------------------------------------------------------
// Helper class Styles
//-------------------------------------------------------------
private static class StyleIterator implements Iterator
{
//~ Instance fields
// ------------------------------------------------------------------------
private String[] _style;
private int _idx = 0;
//~ Constructors
// ---------------------------------------------------------------------------
StyleIterator(String styles)
{
_style = (styles == null) ? ArrayUtils.EMPTY_STRING_ARRAY : StringUtils.trim(StringUtils
.splitShortString(styles, ','));
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext()
{
return _style.length > 0;
}
/**
* @see java.util.Iterator#next()
*/
public Object next()
{
if(hasNext())
{
return _style[_idx++ % _style.length];
}
throw new NoSuchElementException("no style defined");
}
/**
* @see java.util.Iterator#remove()
*/
public void remove()
{
throw new UnsupportedOperationException("remove is not supported");
}
public void reset()
{
_idx = 0;
}
}
public void decode(FacesContext context, UIComponent component)
{
super.decode(context, component);
}
}