org.richfaces.renderkit.AbstractTableRenderer Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright ${year}, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.renderkit;
import java.io.IOException;
import java.util.Iterator;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.richfaces.component.Row;
import org.richfaces.component.UIDataTableBase;
import org.richfaces.component.util.HtmlUtil;
/**
* @author Anton Belevich
*
*/
@ResourceDependencies({ @ResourceDependency(name = "richfaces-event.js") })
public abstract class AbstractTableRenderer extends AbstractTableBaseRenderer implements MetaComponentRenderer {
public static final String HIDDEN_CONTAINER_ID = ":sc";
public abstract EncodeStrategy getHiddenContainerStrategy(UIDataTableBase dataTableBase);
public boolean isColumnAttributeSet(UIDataTableBase table, String attributeName) {
Iterator columns = table.columns();
boolean result = false;
while (columns.hasNext() && !result) {
UIComponent component = columns.next();
result = (component.isRendered() && (null != component.getValueExpression(attributeName)));
}
return result;
}
@Override
public RowHolderBase createRowHolder(FacesContext context, UIComponent component, Object[] options) {
RowHolder rowHolder = null;
if (component instanceof UIDataTableBase) {
rowHolder = new RowHolder(context, (UIDataTableBase) component);
rowHolder.setUpdatePartial((Boolean) options[0]);
rowHolder.setEncodeParentTBody((Boolean) options[1]);
}
return rowHolder;
}
protected class SimpleHeaderEncodeStrategy implements EncodeStrategy {
public void begin(ResponseWriter writer, FacesContext context, UIComponent column, Object[] params) throws IOException {
}
public void end(ResponseWriter writer, FacesContext context, UIComponent column, Object[] params) throws IOException {
}
}
protected void doDecode(FacesContext context, UIComponent component) {
decodeSortingFiltering(context, component);
}
protected void putRowStylesIntoContext(FacesContext facesContext, RowHolderBase rowHolder) {
UIDataTableBase dataTableBase = (UIDataTableBase) rowHolder.getRow();
String rowClass = getRowSkinClass();
String cellClass = getCellSkinClass();
String firstClass = getFirstRowSkinClass();
rowClass = mergeStyleClasses(ROW_CLASS_KEY, rowClass, dataTableBase);
cellClass = mergeStyleClasses(CELL_CLASS_KEY, cellClass, dataTableBase);
firstClass = mergeStyleClasses(FIRST_ROW_CLASS_KEY, firstClass, dataTableBase);
saveRowStyles(facesContext, dataTableBase.getClientId(facesContext), firstClass, rowClass, cellClass);
}
protected boolean isEncodeHeaders(UIDataTableBase table) {
return table.isColumnFacetPresent(UIDataTableBase.HEADER) || isColumnAttributeSet(table, "sortBy")
|| isColumnAttributeSet(table, "comparator") || isColumnAttributeSet(table, "filterBy");
}
public void encodeTableStructure(ResponseWriter writer, FacesContext context, UIDataTableBase dataTable) throws IOException {
// DataTableRenderer override this method
}
public void encodeBeforeRows(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase,
boolean encodeParentTBody, boolean partialUpdate) throws IOException {
}
public void encodeAfterRows(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase,
boolean encodeParentTBody, boolean partialUpdate) throws IOException {
}
public abstract boolean encodeParentTBody(UIDataTableBase dataTableBase);
public void encodeTableFacets(ResponseWriter writer, FacesContext context, UIDataTableBase dataTable) throws IOException {
Object key = dataTable.getRowKey();
dataTable.captureOrigValue(context);
dataTable.setRowKey(context, null);
encodeTableStructure(writer, context, dataTable);
encodeHeaderFacet(writer, context, dataTable, false);
encodeFooterFacet(writer, context, dataTable, false);
dataTable.setRowKey(context, key);
dataTable.restoreOrigValue(context);
}
public void encodeTableRows(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase,
boolean encodePartialUpdate) throws IOException {
int rowCount = dataTableBase.getRowCount();
Object key = dataTableBase.getRowKey();
dataTableBase.captureOrigValue(facesContext);
dataTableBase.setRowKey(facesContext, null);
boolean encodeParentTBody = encodeParentTBody(dataTableBase);
encodeBeforeRows(writer, facesContext, dataTableBase, encodeParentTBody, encodePartialUpdate);
if (rowCount > 0) {
processRows(writer, facesContext, dataTableBase, new Object[] { encodePartialUpdate, encodeParentTBody });
} else {
encodeNoDataFacetOrLabel(writer, facesContext, dataTableBase);
}
encodeAfterRows(writer, facesContext, dataTableBase, encodeParentTBody, encodePartialUpdate);
if (encodePartialUpdate) {
String id = dataTableBase.getClientId(facesContext) + HIDDEN_CONTAINER_ID;
partialStart(facesContext, id);
}
encodeHiddens(writer, facesContext, dataTableBase, new Object[] { encodeParentTBody });
if (encodePartialUpdate) {
partialEnd(facesContext);
}
dataTableBase.setRowKey(facesContext, key);
dataTableBase.restoreOrigValue(facesContext);
}
public void encodeNoDataFacetOrLabel(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase)
throws IOException {
int columns = getColumnsCount(dataTableBase.columns());
UIComponent noDataFacet = dataTableBase.getNoData();
String noDataLabel = dataTableBase.getNoDataLabel();
writer.startElement(HtmlConstants.TR_ELEMENT, dataTableBase);
String styleClass = (String) dataTableBase.getAttributes().get("noDataStyleClass");
styleClass = concatClasses(getNoDataClass(), styleClass);
writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, styleClass, null);
writer.startElement(HtmlConstants.TD_ELEM, dataTableBase);
writer.writeAttribute(HtmlConstants.COLSPAN_ATTRIBUTE, columns, null);
String cellStyleClass = (String) dataTableBase.getAttributes().get("noDataCellStyleClass");
cellStyleClass = concatClasses(getNoDataCellClass(), cellStyleClass);
writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, cellStyleClass, null);
if (noDataFacet != null && noDataFacet.isRendered()) {
noDataFacet.encodeAll(facesContext);
} else if (noDataLabel != null && noDataLabel.length() > 0) {
writer.writeText(noDataLabel, "noDataLabel");
} else {
writer.writeAttribute(HtmlConstants.STYLE_ATTRIBUTE, "display: none;", null);
}
writer.endElement(HtmlConstants.TD_ELEM);
writer.endElement(HtmlConstants.TR_ELEMENT);
}
/**
* Clear the extendedDataModel before the component encode begins. This is to force the extendedDataModel to be
* re-initialized taking into account any model changes that were applied since the model was created in the
* RESTORE_VIEW phase.
*
* @param context
* @param component
* @throws IOException
*/
@Override
protected void preEncodeBegin(FacesContext context, UIComponent component) throws IOException {
super.preEncodeBegin(context, component);
if (component instanceof UIDataTableBase) {
((UIDataTableBase) component).clearExtendedDataModel();
}
}
protected void doEncodeChildren(ResponseWriter writer, FacesContext context, UIComponent component) throws IOException {
if (component instanceof UIDataTableBase) {
encodeTableRows(writer, context, (UIDataTableBase) component, false);
}
}
public void encodeTableStart(ResponseWriter writer, FacesContext context, UIDataTableBase component) throws IOException {
writer.startElement(HtmlConstants.TABLE_ELEMENT, component);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, component.getClientId(context), null);
String styleClass = getTableSkinClass();
encodeStyleClass(writer, context, component, HtmlConstants.STYLE_CLASS_ATTR, styleClass);
}
protected void encodeHiddens(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase,
Object[] params) throws IOException {
EncodeStrategy encodeStrategy = getHiddenContainerStrategy(dataTableBase);
if (encodeStrategy != null) {
encodeStrategy.begin(writer, facesContext, dataTableBase, params);
encodeClientScript(writer, facesContext, dataTableBase);
encodeHiddenInput(writer, facesContext, dataTableBase);
encodeStrategy.end(writer, facesContext, dataTableBase, params);
}
}
public void encodeTableEnd(ResponseWriter writer) throws IOException {
writer.endElement(HtmlConstants.TABLE_ELEMENT);
}
public abstract void encodeClientScript(ResponseWriter writer, FacesContext context, UIDataTableBase component)
throws IOException;
public abstract void encodeHiddenInput(ResponseWriter writer, FacesContext context, UIDataTableBase component)
throws IOException;
public void encodeTableBodyStart(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTableBase)
throws IOException {
writer.startElement(HtmlConstants.TBODY_ELEMENT, dataTableBase);
String clientId = (dataTableBase.getRowKey() == null) ? dataTableBase.getContainerClientId(facesContext)
: dataTableBase.getRelativeClientId(facesContext);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, clientId + ":tb", null);
writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, getTableBodySkinClass(), null);
}
public void encodeTableBodyEnd(ResponseWriter writer) throws IOException {
writer.endElement(HtmlConstants.TBODY_ELEMENT);
}
public void encodeFooterFacet(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTable,
boolean encodePartialUpdate) throws IOException {
UIComponent footer = dataTable.getFooter();
boolean columnFacetPresent = dataTable.isColumnFacetPresent("footer");
if ((footer != null && footer.isRendered()) || columnFacetPresent) {
boolean partialUpdateEncoded = false;
String clientId = dataTable.getClientId(facesContext);
boolean encodeTfoot = containsThead();
if (encodeTfoot) {
String footerClientId = clientId + ":tf";
if (encodePartialUpdate) {
partialUpdateEncoded = true;
partialStart(facesContext, footerClientId);
}
writer.startElement(HtmlConstants.TFOOT_ELEMENT, dataTable);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, footerClientId, null);
writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-dt-tft", null);
}
int columns = getColumnsCount(dataTable.columns());
boolean encodePartialUpdateForChildren = (encodePartialUpdate && !partialUpdateEncoded);
if (columnFacetPresent) {
String rowClass = getColumnFooterSkinClass();
String cellClass = getColumnFooterCellSkinClass();
String firstClass = getColumnFooterFirstSkinClass();
rowClass = mergeStyleClasses("columnFooterClass", rowClass, dataTable);
cellClass = mergeStyleClasses("columnFooterCellClass", cellClass, dataTable);
firstClass = mergeStyleClasses("firstColumnFooterClass", firstClass, dataTable);
saveRowStyles(facesContext, clientId, firstClass, rowClass, cellClass);
String targetId = clientId + ":cf";
if (encodePartialUpdateForChildren) {
partialStart(facesContext, targetId);
}
writer.startElement(HtmlConstants.TR_ELEMENT, dataTable);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, targetId, null);
encodeStyleClass(writer, facesContext, dataTable, null, rowClass);
encodeColumnFacet(facesContext, writer, dataTable, UIDataTableBase.FOOTER, columns, cellClass);
writer.endElement(HtmlConstants.TR_ELEMENT);
if (encodePartialUpdateForChildren) {
partialEnd(facesContext);
}
}
if (footer != null && footer.isRendered()) {
String rowClass = getFooterSkinClass();
String cellClass = getFooterCellSkinClass();
String firstClass = getFooterFirstSkinClass();
rowClass = mergeStyleClasses("footerClass", rowClass, dataTable);
cellClass = mergeStyleClasses("footerCellClass", cellClass, dataTable);
firstClass = mergeStyleClasses("footerFirstClass", firstClass, dataTable);
// TODO nick - rename method "encodeTableHeaderFacet"
saveRowStyles(facesContext, clientId, firstClass, rowClass, cellClass);
encodeTableFacet(facesContext, writer, clientId, columns, footer, UIDataTableBase.FOOTER, rowClass, cellClass,
encodePartialUpdateForChildren);
}
if (encodeTfoot) {
writer.endElement(HtmlConstants.TFOOT_ELEMENT);
if (partialUpdateEncoded) {
partialEnd(facesContext);
}
}
}
}
protected String mergeStyleClasses(String classAttribibute, String skinClass, UIComponent component) {
String resultClass = skinClass;
String styleClass = null;
if (classAttribibute != null && component != null) {
styleClass = (String) component.getAttributes().get(classAttribibute);
}
return HtmlUtil.concatClasses(resultClass, styleClass);
}
public void encodeHeaderFacet(ResponseWriter writer, FacesContext facesContext, UIDataTableBase dataTable,
boolean encodePartialUpdate) throws IOException {
UIComponent header = dataTable.getHeader();
boolean isEncodeHeaders = isEncodeHeaders(dataTable);
boolean encodeThead = containsThead();
if ((header != null && header.isRendered()) || isEncodeHeaders) {
String id = dataTable.getClientId(facesContext);
boolean partialUpdateEncoded = false;
String clientId = dataTable.getClientId(facesContext);
if (encodeThead) {
String headerClientId = clientId + ":th";
if (encodePartialUpdate) {
partialUpdateEncoded = true;
partialStart(facesContext, headerClientId);
}
writer.startElement(HtmlConstants.THEAD_ELEMENT, dataTable);
setCellElement(facesContext, id, HtmlConstants.TH_ELEM);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, headerClientId, null);
writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-dt-thd", null);
}
int columns = getColumnsCount(dataTable.columns());
boolean encodePartialUpdateForChildren = (encodePartialUpdate && !partialUpdateEncoded);
if (header != null && header.isRendered()) {
String rowClass = getHeaderSkinClass();
String cellClass = getHeaderCellSkinClass();
String firstClass = getHeaderFirstSkinClass();
rowClass = mergeStyleClasses("headerClass", rowClass, dataTable);
cellClass = mergeStyleClasses("headerCellClass", cellClass, dataTable);
firstClass = mergeStyleClasses("headerFirstClass", firstClass, dataTable);
saveRowStyles(facesContext, clientId, firstClass, rowClass, cellClass);
encodeTableFacet(facesContext, writer, clientId, columns, header, UIDataTableBase.HEADER, rowClass, cellClass,
encodePartialUpdateForChildren);
}
if (isEncodeHeaders) {
String rowClass = getColumnHeaderSkinClass();
String cellClass = getColumnHeaderCellSkinClass();
String firstClass = getColumnHeaderFirstSkinClass();
rowClass = mergeStyleClasses("columnHeaderClass", rowClass, dataTable);
cellClass = mergeStyleClasses("columnHeaderCellClass", cellClass, dataTable);
firstClass = mergeStyleClasses("columnHeaderFirstClass", firstClass, dataTable);
saveRowStyles(facesContext, clientId, firstClass, rowClass, cellClass);
String targetId = clientId + ":ch";
if (encodePartialUpdateForChildren) {
partialStart(facesContext, targetId);
}
writer.startElement(HtmlConstants.TR_ELEMENT, dataTable);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, targetId, null);
encodeStyleClass(writer, facesContext, dataTable, null, rowClass);
encodeColumnFacet(facesContext, writer, dataTable, UIDataTableBase.HEADER, columns, cellClass);
writer.endElement(HtmlConstants.TR_ELEMENT);
if (encodePartialUpdateForChildren) {
partialEnd(facesContext);
}
}
if (encodeThead) {
setCellElement(facesContext, id, null);
writer.endElement(HtmlConstants.THEAD_ELEMENT);
if (partialUpdateEncoded) {
partialEnd(facesContext);
}
}
}
}
protected void encodeColumnFacet(FacesContext context, ResponseWriter writer, UIDataTableBase dataTableBase,
String facetName, int colCount, String cellClass) throws IOException {
int tColCount = 0;
String id = dataTableBase.getClientId(context);
String element = getCellElement(context, id);
Iterator headers = dataTableBase.columns();
while (headers.hasNext()) {
UIComponent column = headers.next();
if (!column.isRendered() || (column instanceof Row)) {
continue;
}
Integer colspan = (Integer) column.getAttributes().get(HtmlConstants.COLSPAN_ATTRIBUTE);
if (colspan != null && colspan.intValue() > 0) {
tColCount += colspan.intValue();
} else {
tColCount++;
}
if (tColCount > colCount) {
break;
}
writer.startElement(element, column);
encodeStyleClass(writer, context, column, facetName + "Class", cellClass);
if (HtmlConstants.TH_ELEM.equals(element)) { // HTML5 allows scope attr only on th elements
writer.writeAttribute(HtmlConstants.SCOPE_ATTRIBUTE, HtmlConstants.COL_ELEMENT, null);
}
getUtils().encodeAttribute(context, column, HtmlConstants.COLSPAN_ATTRIBUTE);
EncodeStrategy strategy = getHeaderEncodeStrategy(column, facetName);
if (strategy != null) {
strategy.begin(writer, context, column, new String[] { facetName });
UIComponent facet = column.getFacet(facetName);
if (facet != null && facet.isRendered()) {
facet.encodeAll(context);
}
strategy.end(writer, context, column, new String[] { facetName });
}
writer.endElement(element);
}
}
protected void encodeTableFacet(FacesContext facesContext, ResponseWriter writer, String id, int columns,
UIComponent footer, String facetName, String rowClass, String cellClass, boolean encodePartialUpdate)
throws IOException {
boolean isColumnGroup = (footer instanceof Row);
String element = getCellElement(facesContext, id);
boolean partialUpdateEncoded = false;
if (!isColumnGroup) {
String targetId = id + ":" + facetName.charAt(0);
if (encodePartialUpdate) {
partialUpdateEncoded = true;
partialStart(facesContext, targetId);
}
writer.startElement(HtmlConstants.TR_ELEMENT, footer);
writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, targetId, null);
encodeStyleClass(writer, facesContext, footer, null, rowClass);
writer.startElement(element, footer);
encodeStyleClass(writer, facesContext, footer, null, cellClass);
if (columns > 0) {
writer.writeAttribute(HtmlConstants.COLSPAN_ATTRIBUTE, String.valueOf(columns), null);
}
if (HtmlConstants.TH_ELEM.equals(element)) { // HTML5 allows scope attr only on th elements
writer.writeAttribute(HtmlConstants.SCOPE_ATTRIBUTE, HtmlConstants.COLGROUP_ELEMENT, null);
}
}
if (encodePartialUpdate && !partialUpdateEncoded) {
partialStart(facesContext, footer.getClientId(facesContext));
}
footer.encodeAll(facesContext);
if (encodePartialUpdate && !partialUpdateEncoded) {
partialEnd(facesContext);
}
if (isColumnGroup) {
writer.endElement(HtmlConstants.TR_ELEMENT);
} else {
writer.endElement(element);
writer.endElement(HtmlConstants.TR_ELEMENT);
if (partialUpdateEncoded) {
partialEnd(facesContext);
}
}
}
public abstract EncodeStrategy getHeaderEncodeStrategy(UIComponent column, String tableFacetName);
public abstract boolean containsThead();
public abstract String getTableSkinClass();
public abstract String getTableBodySkinClass();
public abstract String getFirstRowSkinClass();
public abstract String getRowSkinClass();
public abstract String getHeaderCellSkinClass();
public abstract String getHeaderSkinClass();
public abstract String getHeaderFirstSkinClass();
public abstract String getColumnHeaderCellSkinClass();
public abstract String getColumnHeaderSkinClass();
public abstract String getColumnHeaderFirstSkinClass();
public abstract String getFooterCellSkinClass();
public abstract String getFooterSkinClass();
public abstract String getFooterFirstSkinClass();
public abstract String getColumnFooterCellSkinClass();
public abstract String getColumnFooterSkinClass();
public abstract String getColumnFooterFirstSkinClass();
public abstract String getCellSkinClass();
public abstract String getNoDataClass();
public abstract String getNoDataCellClass();
public void encodeMetaComponent(FacesContext context, UIComponent component, String metaComponentId) throws IOException {
UIDataTableBase table = (UIDataTableBase) component;
if (UIDataTableBase.HEADER.equals(metaComponentId)) {
encodeHeaderFacet(context.getResponseWriter(), context, table, true);
} else if (UIDataTableBase.FOOTER.equals(metaComponentId)) {
encodeFooterFacet(context.getResponseWriter(), context, table, true);
} else if (UIDataTableBase.BODY.equals(metaComponentId)) {
encodeTableRows(context.getResponseWriter(), context, table, true);
} else {
throw new IllegalArgumentException("Unsupported metaComponentIdentifier: " + metaComponentId);
}
}
public void decodeMetaComponent(FacesContext context, UIComponent component, String metaComponentId) {
throw new UnsupportedOperationException();
}
protected void partialStart(FacesContext facesContext, String id) throws IOException {
facesContext.getPartialViewContext().getPartialResponseWriter().startUpdate(id);
}
protected void partialEnd(FacesContext facesContext) throws IOException {
facesContext.getPartialViewContext().getPartialResponseWriter().endUpdate();
}
}