com.icesoft.faces.renderkit.dom_html_basic.TableRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icefaces-compat Show documentation
Show all versions of icefaces-compat Show documentation
${icefaces.product.name} Compat Component Library
The newest version!
/*
* Copyright 2004-2013 ICEsoft Technologies Canada Corp.
*
* 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 com.icesoft.faces.renderkit.dom_html_basic;
import com.icesoft.faces.component.AttributeConstants;
import com.icesoft.faces.context.DOMContext;
import com.icesoft.faces.context.effects.JavascriptContext;
import com.icesoft.faces.util.Debug;
import org.w3c.dom.Element;
import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.el.ValueBinding;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
public class TableRenderer extends DomBasicRenderer {
private static final String[] passThruAttributes = AttributeConstants.getAttributes(AttributeConstants.H_DATATABLE);
private static final String SCROLL_POSITION_SUFFIX = "_scrollPosition";
@Override
public void decode(FacesContext facesContext, UIComponent uiComponent) {
super.decode(facesContext, uiComponent);
String scrollPositionParameter = uiComponent.getClientId(facesContext) + SCROLL_POSITION_SUFFIX;
String scrollPosition = facesContext.getExternalContext().getRequestParameterMap().get(scrollPositionParameter);
if (scrollPosition != null) {
ValueBinding vb = uiComponent.getValueBinding("scrollPosition");
if (vb != null) {
vb.setValue(facesContext, scrollPosition);
}
}
}
public boolean getRendersChildren() {
return true;
}
public void encodeBegin(FacesContext facesContext, UIComponent uiComponent)
throws IOException {
validateParameters(facesContext, uiComponent, null);
ResponseWriter responseWriter = facesContext.getResponseWriter();
Debug.assertTrue(responseWriter != null, "ResponseWriter is null");
uiComponent.getAttributes().get("resizableColumnWidths");
DOMContext domContext =
DOMContext.attachDOMContext(facesContext, uiComponent);
if (!domContext.isInitialized()) {
Element root = null;
root = domContext.createRootElement("table");
setRootElementId(facesContext, root, uiComponent);
PassThruAttributeRenderer.renderHtmlAttributes(
facesContext, uiComponent, passThruAttributes);
}
Element root = (Element) domContext.getRootNode();
DOMContext.removeChildren(root);
String styleClass = getComponentStyleClass(uiComponent);
if (styleClass != null && styleClass.length() > 0) {
root.setAttribute("class", styleClass);
}
Object cellspacing = uiComponent.getAttributes().get(HTML.CELLSPACING_ATTR);
if (cellspacing != null) {
root.setAttribute(HTML.CELLSPACING_ATTR, String.valueOf(cellspacing));
} else {
root.setAttribute(HTML.CELLSPACING_ATTR, "0");
}
if (isScrollable(uiComponent)) {
Element tr = domContext.createElement("tr");
root.appendChild(tr);
Element td = domContext.createElement("td");
tr.appendChild(td);
Element mainDiv = domContext.createElement("div");
td.appendChild(mainDiv);
Element headerDiv = domContext.createElement("div");
Element headerTable = domContext.createElement("table");
headerTable.setAttribute("style","width:100%;");
headerDiv.appendChild(headerTable);
mainDiv.appendChild(headerDiv);
Element bodyDiv = domContext.createElement("div");
ValueBinding vb = uiComponent.getValueBinding("scrollPosition");
int scrollPosition = vb == null ? 0 : (Integer) vb.getValue(facesContext);
String height = (String) uiComponent.getAttributes().get("scrollHeight");
String id = uiComponent.getClientId(facesContext) + "_scroll";
bodyDiv.setAttribute("id", id);
bodyDiv.setAttribute("style",
"overflow:auto;width:100%;"+ (height!=null&&height.length()>0?"height:" + height + ";":""));
Element bodytable = domContext.createElement("table");
bodyDiv.appendChild(bodytable);
Element scrollState = (Element) bodyDiv.appendChild(domContext.createElement("input"));
String inputID = uiComponent.getClientId(facesContext) + SCROLL_POSITION_SUFFIX;
scrollState.setAttribute("id", inputID);
scrollState.setAttribute("name", inputID);
scrollState.setAttribute("type", "hidden");
scrollState.setAttribute("value", Integer.toString(scrollPosition));
bodyDiv.setAttribute("onscroll", "var input = document.getElementById('" + inputID + "'); clearTimeout(ice.pid); ice.pid = setTimeout(function() { input.value = document.getElementById('" + id + "').scrollTop; window.iceSubmitPartial(null, input, event); }, 400);");
Element scrollInitializeWrapper = (Element) bodyDiv.appendChild(domContext.createElement("span"));
scrollInitializeWrapper.setAttribute("id", id + "_initialize");
Element scrollInitialize = (Element) scrollInitializeWrapper.appendChild(domContext.createElement("script"));
scrollInitialize.setAttribute("type", "text/javascript");
scrollInitialize.appendChild(domContext.createTextNodeUnescaped("document.getElementById('" + id + "').scrollTop = " + scrollPosition + ";"));
mainDiv.appendChild(bodyDiv);
Object scollFooter = uiComponent.getAttributes().get("scrollFooter");
if (!(scollFooter != null && ((Boolean)scollFooter).booleanValue())) {
Element footerDiv = domContext.createElement("div");
Element footerTable = domContext.createElement("table");
footerDiv.appendChild(footerTable);
mainDiv.appendChild(footerDiv);
}
}
renderFacet(facesContext, uiComponent, domContext, true); //header facet
renderFacet(facesContext, uiComponent, domContext,
false); //footer facet
}
// this method is overridden in the subclass
public String getComponentStyleClass(UIComponent uiComponent) {
String styleClass =
(String) uiComponent.getAttributes().get("styleClass");
return styleClass;
}
protected void renderFacet(FacesContext facesContext,
UIComponent uiComponent,
DOMContext domContext, boolean header)
throws IOException {
String facet, tag, element, facetClass;
if (header) {
facet = "header";
tag = HTML.THEAD_ELEM;
element = HTML.TH_ELEM;
facetClass = getHeaderClass(uiComponent);
} else {
facet = "footer";
tag = HTML.TFOOT_ELEM;
element = HTML.TD_ELEM;
facetClass = getFooterClass(uiComponent);
}
UIData uiData = (UIData) uiComponent;
uiData.setRowIndex(-1);
Element root = (Element) domContext.getRootNode();
if (isScrollable(uiComponent)) {
if (header) {
Element headerDiv = domContext.createElement("div");
Element headerTable = domContext.createElement("table");
headerDiv.appendChild(headerTable);
root.getFirstChild().getFirstChild().appendChild(headerDiv);
root = headerTable;
} else {
// Get the table in the second div
root = (Element) root.getChildNodes().item(1).getFirstChild();
}
}
// detect whether a facet exists on the UIData component or any of the
// child UIColumn components; if so, then render a thead element
UIComponent headerFacet = getFacetByName(uiData, facet);
boolean childHeaderFacetExists =
childColumnHasFacetWithName(uiData, facet);
Element thead = null;
if (headerFacet != null || childHeaderFacetExists) {
thead = domContext.createElement(tag);
root.appendChild(thead);
}
// if the header is associated with the UIData component then encode the
// header inside a tr and th element that span the whole table.
if (headerFacet != null && headerFacet.isRendered()) {
resetFacetChildId(headerFacet);
Element tr = domContext.createElement("tr");
thead.appendChild(tr);
Element th = domContext.createElement(element);
tr.appendChild(th);
if (facetClass != null) {
th.setAttribute("class", facetClass);
}
th.setAttribute("colspan",
String.valueOf(getNumberOfChildColumns(uiData)));
th.setAttribute("scope", "colgroup");
domContext.setCursorParent(th);
encodeParentAndChildren(facesContext, headerFacet);
}
// if one or more of the child columns has a header facet then render a
// row to accommodate the header(s); render an empty th for each column
// that has no header facet
if (childHeaderFacetExists) {
Element tr = domContext.createElement("tr");
thead.appendChild(tr);
StringTokenizer columnWidths =
getColumnWidths(uiData);
Iterator childColumns = getRenderedChildColumnsIterator(uiData);
while (childColumns.hasNext()) {
UIColumn nextColumn = (UIColumn) childColumns.next();
Element th = domContext.createElement(element);
tr.appendChild(th);
if (facetClass != null) {
th.setAttribute("class", facetClass);
}
if (columnWidths != null && columnWidths.hasMoreTokens()) {
String width = columnWidths.nextToken();
th.setAttribute("style",
"width:" + width + ";overflow:hidden;");
}
//th.setAttribute("colgroup", "col");
UIComponent nextFacet = getFacetByName(nextColumn, facet);
if (nextFacet != null) {
resetFacetChildId(nextFacet);
domContext.setCursorParent(th);
encodeParentAndChildren(facesContext, nextFacet);
}
}
if (isScrollable(uiComponent)) {
tr.appendChild(scrollBarSpacer(domContext, facesContext));
}
}
domContext.setCursorParent(root);
}
protected void resetFacetChildId(UIComponent component) {
component.setId(component.getId());
if (component.getChildCount() == 0)return;
Iterator facetChild = component.getChildren().iterator();
while (facetChild.hasNext()) {
UIComponent child = (UIComponent) facetChild.next();
resetFacetChildId(child);
}
}
// this method is overridden in the subclass
public String getHeaderClass(UIComponent component) {
return (String) component.getAttributes().get("headerClass");
}
public String getFooterClass(UIComponent component) {
return (String) component.getAttributes().get("footerClass");
}
protected boolean childColumnHasFacetWithName(UIComponent component,
String facetName) {
Iterator childColumns = getRenderedChildColumnsIterator(component);
while (childColumns.hasNext()) {
UIColumn nextChildColumn = (UIColumn) childColumns.next();
if (getFacetByName(nextChildColumn, facetName) != null) {
return true;
}
}
return false;
}
public void encodeChildren(FacesContext facesContext,
UIComponent uiComponent)
throws IOException {
validateParameters(facesContext, uiComponent, null);
DOMContext domContext =
DOMContext.attachDOMContext(facesContext, uiComponent);
Element root = (Element) domContext.getRootNode();
Element tbody = domContext.createElement("tbody");
root.appendChild(tbody);
// render the appropriate styles for each row and column
String columnStyles[] = getColumnStyleClasses(uiComponent);
String rowStyles[] = getRowStyles(uiComponent);
int columnStyleIndex = 0;
int rowStyleIndex = 0;
int columnStylesMaxIndex = columnStyles.length - 1;
int rowStylesMaxIndex = rowStyles.length - 1;
// keep track of row index on UIData component and how many rows we've displayed
UIData uiData = (UIData) uiComponent;
int rowIndex = uiData.getFirst();
int numberOfRowsToDisplay = uiData.getRows();
int countOfRowsDisplayed = 0;
uiData.setRowIndex(rowIndex);
while (uiData.isRowAvailable()) {
// Have we finished the required number of rows ? Note that
// numberOfRowsToDisplay == 0 means that we display all remaining rows
// of the underlying model and in this case we rely on the
// isRowAvailable method (above) to limit rendering work.
if (numberOfRowsToDisplay > 0
&& countOfRowsDisplayed >= numberOfRowsToDisplay) {
break;
}
// render another row
Element tr = domContext.createElement("tr");
tr.setAttribute("id", uiComponent.getContainerClientId(facesContext));
tbody.appendChild(tr);
// if row styles exist, then render the appropriate one
if (rowStylesMaxIndex >= 0) {
tr.setAttribute("class", rowStyles[rowStyleIndex]);
if (++rowStyleIndex > rowStylesMaxIndex) {
rowStyleIndex = 0;
}
}
// render the child columns; each one in a td
Iterator childColumns;
childColumns = getRenderedChildColumnsIterator(uiData);
StringTokenizer columnWidths =
getColumnWidths(uiComponent);
int colNumber = 1;
while (childColumns.hasNext()) {
// render another td
UIColumn nextColumn = (UIColumn) childColumns.next();
Element td = domContext.createElement("td");
if (columnWidths != null && columnWidths.hasMoreTokens()) {
td.setAttribute("style",
"width:" + columnWidths.nextToken() + ";");
}
tr.appendChild(td);
// if column styles exist, then apply the appropriate one
writeColStyles(columnStyles, columnStylesMaxIndex,
columnStyleIndex, td, colNumber++,
uiComponent);
if (++columnStyleIndex > columnStylesMaxIndex) {
columnStyleIndex = 0;
}
if (nextColumn.getChildCount() > 0) {
// recursively render the components contained within this td (column)
Iterator childrenOfThisColumn =
nextColumn.getChildren().iterator();
domContext.setCursorParent(td);
while (childrenOfThisColumn.hasNext()) {
UIComponent nextChild =
(UIComponent) childrenOfThisColumn.next();
if (nextChild.isRendered()) {
encodeParentAndChildren(facesContext, nextChild);
}
}
}
}
// keep track of rows displayed
countOfRowsDisplayed++;
// maintain the row index property on the underlying UIData component
rowIndex++;
uiData.setRowIndex(rowIndex);
// reset the column style index for the next row
columnStyleIndex = 0;
}
// reset the underlying UIData component
uiData.setRowIndex(-1);
domContext.stepOver();
}
// this method is overridden in the subclass
public void writeColStyles(String[] columnStyles, int columnStylesMaxIndex,
int columnStyleIndex, Element td,
int colNumber,
UIComponent uiComponent) {
if (columnStyles.length > 0) {
if (columnStylesMaxIndex >= 0) {
td.setAttribute("class", columnStyles[columnStyleIndex]);
if (++columnStyleIndex > columnStylesMaxIndex) {
columnStyleIndex = 0;
}
}
}
}
// this method is overridden in the subclass
public String[] getRowStyles(UIComponent uiComponent) {
return getRowStyleClasses(uiComponent);
}
public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
throws IOException {
validateParameters(facesContext, uiComponent, null);
if (!uiComponent.isRendered()) {
return;
}
if (isScrollable(uiComponent)) {
JavascriptContext.addJavascriptCall(facesContext, "Ice.dataTable.onLoad('" +
uiComponent.getClientId(facesContext) + "');");
}
}
protected int getNumberOfChildColumns(UIComponent component) {
return getRenderedChildColumnsList(component).size();
}
protected Iterator getRenderedChildColumnsIterator(UIComponent component) {
return getRenderedChildColumnsList(component).iterator();
}
protected List getRenderedChildColumnsList(UIComponent component) {
List results = new ArrayList();
if (component.getChildCount() > 0) {
Iterator kids = component.getChildren().iterator();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
if ((kid instanceof UIColumn) && kid.isRendered()) {
results.add(kid);
}
}
}
return results;
}
protected boolean isScrollable(UIComponent uiComponent) {
Object o = uiComponent.getAttributes().get("scrollable");
if (o != null && o instanceof Boolean) {
return ((Boolean) o).booleanValue();
}
return false;
}
protected Element scrollBarSpacer(DOMContext domContext, FacesContext facesContext) {
Element spacer = domContext.createElement("th");
spacer.setAttribute(HTML.STYLE_ATTR, "padding:0"); // ICE-2654
spacer.appendChild(domContext.createElement(HTML.DIV_ELEM));
return spacer;
}
protected StringTokenizer getColumnWidths(UIComponent uiComponent) {
Object o = uiComponent.getAttributes().get("columnWidths");
if (o != null && o instanceof String) {
return new StringTokenizer(o.toString(), ",");
}
return null;
}
protected List getColumnWidthsAsList(UIComponent uiComponent) {
StringTokenizer st = getColumnWidths(uiComponent);
if (st == null) {
return null;
}
int count = st.countTokens();
if (count == 0) {
return null;
}
List columnWidths = new ArrayList(count);
while (st.hasMoreTokens()) {
columnWidths.add(st.nextToken());
}
return columnWidths;
}
protected Element getScrollableHeaderTableElement(Element root) {
// First table in first div path : table/tr/td/div/div0/table
return (Element) root.getFirstChild().getFirstChild().getFirstChild().getFirstChild().getFirstChild();
}
protected Element getScrollableBodyTableElement(Element root) {
// First table in second div path table/tr/td/div/div1/table
return (Element) root.getFirstChild().getFirstChild().getFirstChild().getFirstChild().getNextSibling().getFirstChild();
}
protected Element getScrollableFooterTableElement(Element root) {
// First table in second div path table/tr/td/div/div1/table
return (Element) root.getFirstChild().getFirstChild().getFirstChild().getFirstChild().getNextSibling().getNextSibling().getFirstChild();
}
}