001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.renderkit.dom_html_basic;
035:
036: import com.icesoft.faces.context.DOMContext;
037: import com.icesoft.faces.util.Debug;
038: import org.w3c.dom.Element;
039:
040: import javax.faces.component.UIColumn;
041: import javax.faces.component.UIComponent;
042: import javax.faces.component.UIData;
043: import javax.faces.context.FacesContext;
044: import javax.faces.context.ResponseWriter;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Iterator;
048: import java.util.List;
049: import java.util.StringTokenizer;
050:
051: public class TableRenderer extends DomBasicRenderer {
052:
053: public boolean getRendersChildren() {
054: return true;
055: }
056:
057: public void encodeBegin(FacesContext facesContext,
058: UIComponent uiComponent) throws IOException {
059:
060: validateParameters(facesContext, uiComponent, null);
061: ResponseWriter responseWriter = facesContext
062: .getResponseWriter();
063: Debug.assertTrue(responseWriter != null,
064: "ResponseWriter is null");
065: DOMContext domContext = DOMContext.attachDOMContext(
066: facesContext, uiComponent);
067:
068: if (!domContext.isInitialized()) {
069: Element root = null;
070: root = domContext.createRootElement("table");
071: setRootElementId(facesContext, root, uiComponent);
072: PassThruAttributeRenderer.renderAttributes(facesContext,
073: uiComponent, null);
074: }
075: Element root = (Element) domContext.getRootNode();
076: DOMContext.removeChildren(root);
077: String styleClass = getComponentStyleClass(uiComponent);
078: if (styleClass != null && styleClass.length() > 0) {
079: root.setAttribute("class", styleClass);
080: }
081: root.setAttribute(HTML.CELLSPACING_ATTR, "0");
082: if (isScrollable(uiComponent)) {
083: Element tr = domContext.createElement("tr");
084: root.appendChild(tr);
085: Element td = domContext.createElement("td");
086: tr.appendChild(td);
087: Element mainDiv = domContext.createElement("div");
088: td.appendChild(mainDiv);
089: Element headerDiv = domContext.createElement("div");
090: Element headerTable = domContext.createElement("table");
091: headerDiv.appendChild(headerTable);
092:
093: mainDiv.appendChild(headerDiv);
094: Element bodyDiv = domContext.createElement("div");
095: String height = (String) uiComponent.getAttributes().get(
096: "scrollHeight");
097: bodyDiv.setAttribute("style", "overflow:auto;height:"
098: + height + ";");
099:
100: Element bodytable = domContext.createElement("table");
101: bodyDiv.appendChild(bodytable);
102: mainDiv.appendChild(bodyDiv);
103: }
104: renderFacet(facesContext, uiComponent, domContext, true); //header facet
105: renderFacet(facesContext, uiComponent, domContext, false); //footer facet
106: }
107:
108: // this method is overridden in the subclass
109: public String getComponentStyleClass(UIComponent uiComponent) {
110: String styleClass = (String) uiComponent.getAttributes().get(
111: "styleClass");
112: return styleClass;
113: }
114:
115: protected void renderFacet(FacesContext facesContext,
116: UIComponent uiComponent, DOMContext domContext,
117: boolean header) throws IOException {
118: String facet, tag, element, facetClass;
119: if (header) {
120: facet = "header";
121: tag = HTML.THEAD_ELEM;
122: element = HTML.TH_ELEM;
123: facetClass = getHeaderClass(uiComponent);
124: } else {
125: facet = "footer";
126: tag = HTML.TFOOT_ELEM;
127: element = HTML.TD_ELEM;
128: facetClass = getFooterClass(uiComponent);
129: }
130: UIData uiData = (UIData) uiComponent;
131: uiData.setRowIndex(-1);
132: Element root = (Element) domContext.getRootNode();
133: if (isScrollable(uiComponent)) {
134: if (header) {
135: Element headerDiv = domContext.createElement("div");
136: Element headerTable = domContext.createElement("table");
137: headerDiv.appendChild(headerTable);
138: root.getFirstChild().getFirstChild().appendChild(
139: headerDiv);
140: root = headerTable;
141: } else {
142: // Get the table in the second div
143: root = (Element) root.getChildNodes().item(1)
144: .getFirstChild();
145: }
146: }
147:
148: // detect whether a facet exists on the UIData component or any of the
149: // child UIColumn components; if so, then render a thead element
150: UIComponent headerFacet = getFacetByName(uiData, facet);
151: boolean childHeaderFacetExists = childColumnHasFacetWithName(
152: uiData, facet);
153: Element thead = null;
154: if (headerFacet != null || childHeaderFacetExists) {
155: thead = domContext.createElement(tag);
156: root.appendChild(thead);
157: }
158: // if the header is associated with the UIData component then encode the
159: // header inside a tr and th element that span the whole table.
160: if (headerFacet != null && headerFacet.isRendered()) {
161: resetFacetChildId(headerFacet);
162: Element tr = domContext.createElement("tr");
163: thead.appendChild(tr);
164: Element th = domContext.createElement(element);
165: tr.appendChild(th);
166: if (facetClass != null) {
167: th.setAttribute("class", facetClass);
168: }
169: th.setAttribute("colspan", String
170: .valueOf(getNumberOfChildColumns(uiData)));
171: th.setAttribute("scope", "colgroup");
172: domContext.setCursorParent(th);
173: domContext.streamWrite(facesContext, uiComponent,
174: domContext.getRootNode(), th);
175: encodeParentAndChildren(facesContext, headerFacet);
176: }
177:
178: // if one or more of the child columns has a header facet then render a
179: // row to accommodate the header(s); render an empty th for each column
180: // that has no header facet
181: if (childHeaderFacetExists) {
182: Element tr = domContext.createElement("tr");
183: thead.appendChild(tr);
184: StringTokenizer columnWidths = getColumnWidths(uiData);
185:
186: Iterator childColumns = getRenderedChildColumnsIterator(uiData);
187: while (childColumns.hasNext()) {
188:
189: UIColumn nextColumn = (UIColumn) childColumns.next();
190: Element th = domContext.createElement(element);
191: tr.appendChild(th);
192: if (facetClass != null) {
193: th.setAttribute("class", facetClass);
194: }
195: if (columnWidths != null
196: && columnWidths.hasMoreTokens()) {
197: String width = columnWidths.nextToken();
198:
199: th.setAttribute("style", "width:" + width
200: + ";overflow:hidden;");
201: }
202: th.setAttribute("colgroup", "col");
203: UIComponent nextFacet = getFacetByName(nextColumn,
204: facet);
205: if (nextFacet != null) {
206: resetFacetChildId(nextFacet);
207: domContext.setCursorParent(th);
208: domContext.streamWrite(facesContext, uiComponent,
209: domContext.getRootNode(), th);
210: encodeParentAndChildren(facesContext, nextFacet);
211: }
212: }
213: if (isScrollable(uiComponent)) {
214: tr
215: .appendChild(scrollBarSpacer(domContext,
216: facesContext));
217: }
218: }
219: domContext.setCursorParent(root);
220: }
221:
222: protected void resetFacetChildId(UIComponent component) {
223: component.setId(component.getId());
224: Iterator facetChild = component.getChildren().iterator();
225: while (facetChild.hasNext()) {
226: UIComponent child = (UIComponent) facetChild.next();
227: resetFacetChildId(child);
228: }
229: }
230:
231: // this method is overridden in the subclass
232: public String getHeaderClass(UIComponent component) {
233: return (String) component.getAttributes().get("headerClass");
234: }
235:
236: public String getFooterClass(UIComponent component) {
237: return (String) component.getAttributes().get("footerClass");
238: }
239:
240: protected boolean childColumnHasFacetWithName(
241: UIComponent component, String facetName) {
242: Iterator childColumns = getRenderedChildColumnsIterator(component);
243: while (childColumns.hasNext()) {
244: UIColumn nextChildColumn = (UIColumn) childColumns.next();
245: if (getFacetByName(nextChildColumn, facetName) != null) {
246: return true;
247: }
248: }
249: return false;
250: }
251:
252: public void encodeChildren(FacesContext facesContext,
253: UIComponent uiComponent) throws IOException {
254:
255: validateParameters(facesContext, uiComponent, null);
256:
257: DOMContext domContext = DOMContext.attachDOMContext(
258: facesContext, uiComponent);
259: Element root = (Element) domContext.getRootNode();
260:
261: Element tbody = domContext.createElement("tbody");
262: root.appendChild(tbody);
263:
264: // render the appropriate styles for each row and column
265: String columnStyles[] = getColumnStyleClasses(uiComponent);
266:
267: String rowStyles[] = getRowStyles(uiComponent);
268:
269: int columnStyleIndex = 0;
270: int rowStyleIndex = 0;
271: int columnStylesMaxIndex = columnStyles.length - 1;
272: int rowStylesMaxIndex = rowStyles.length - 1;
273: // keep track of row index on UIData component and how many rows we've displayed
274: UIData uiData = (UIData) uiComponent;
275: int rowIndex = uiData.getFirst();
276: int numberOfRowsToDisplay = uiData.getRows();
277: int countOfRowsDisplayed = 0;
278: uiData.setRowIndex(rowIndex);
279:
280: while (uiData.isRowAvailable()) {
281: // Have we finished the required number of rows ? Note that
282: // numberOfRowsToDisplay == 0 means that we display all remaining rows
283: // of the underlying model and in this case we rely on the
284: // isRowAvailable method (above) to limit rendering work.
285: if (numberOfRowsToDisplay > 0
286: && countOfRowsDisplayed >= numberOfRowsToDisplay) {
287: break;
288: }
289: // render another row
290: Element tr = domContext.createElement("tr");
291: tr
292: .setAttribute("id", uiComponent
293: .getClientId(facesContext));
294: tbody.appendChild(tr);
295: // if row styles exist, then render the appropriate one
296: if (rowStylesMaxIndex >= 0) {
297: tr.setAttribute("class", rowStyles[rowStyleIndex]);
298: if (++rowStyleIndex > rowStylesMaxIndex) {
299: rowStyleIndex = 0;
300: }
301: }
302: // render the child columns; each one in a td
303: Iterator childColumns;
304: childColumns = getRenderedChildColumnsIterator(uiData);
305: StringTokenizer columnWidths = getColumnWidths(uiComponent);
306:
307: int colNumber = 1;
308: while (childColumns.hasNext()) {
309: // render another td
310: UIColumn nextColumn = (UIColumn) childColumns.next();
311: Element td = domContext.createElement("td");
312: if (columnWidths != null
313: && columnWidths.hasMoreTokens()) {
314: td.setAttribute("style", "width:"
315: + columnWidths.nextToken() + ";");
316: }
317: tr.appendChild(td);
318:
319: // if column styles exist, then apply the appropriate one
320: writeColStyles(columnStyles, columnStylesMaxIndex,
321: columnStyleIndex, td, colNumber++, uiComponent);
322: if (++columnStyleIndex > columnStylesMaxIndex) {
323: columnStyleIndex = 0;
324: }
325:
326: // recursively render the components contained within this td (column)
327: Iterator childrenOfThisColumn = nextColumn
328: .getChildren().iterator();
329: domContext.setCursorParent(td);
330: domContext.streamWrite(facesContext, uiComponent,
331: domContext.getRootNode(), td);
332: while (childrenOfThisColumn.hasNext()) {
333: UIComponent nextChild = (UIComponent) childrenOfThisColumn
334: .next();
335: if (nextChild.isRendered()) {
336: encodeParentAndChildren(facesContext, nextChild);
337: }
338: }
339: }
340: // keep track of rows displayed
341: countOfRowsDisplayed++;
342: // maintain the row index property on the underlying UIData component
343: rowIndex++;
344: uiData.setRowIndex(rowIndex);
345: // reset the column style index for the next row
346: columnStyleIndex = 0;
347:
348: }
349: // reset the underlying UIData component
350: uiData.setRowIndex(-1);
351: domContext.stepOver();
352: domContext.streamWrite(facesContext, uiComponent);
353: }
354:
355: // this method is overridden in the subclass
356: public void writeColStyles(String[] columnStyles,
357: int columnStylesMaxIndex, int columnStyleIndex, Element td,
358: int colNumber, UIComponent uiComponent) {
359: if (columnStyles.length > 0) {
360: if (columnStylesMaxIndex >= 0) {
361: td
362: .setAttribute("class",
363: columnStyles[columnStyleIndex]);
364: if (++columnStyleIndex > columnStylesMaxIndex) {
365: columnStyleIndex = 0;
366: }
367: }
368: }
369: }
370:
371: // this method is overridden in the subclass
372: public String[] getRowStyles(UIComponent uiComponent) {
373: return getRowStyleClasses(uiComponent);
374: }
375:
376: public void encodeEnd(FacesContext facesContext,
377: UIComponent uiComponent) throws IOException {
378: validateParameters(facesContext, uiComponent, null);
379: if (!uiComponent.isRendered()) {
380: return;
381: }
382: }
383:
384: protected int getNumberOfChildColumns(UIComponent component) {
385: return getRenderedChildColumnsList(component).size();
386: }
387:
388: protected Iterator getRenderedChildColumnsIterator(
389: UIComponent component) {
390: return getRenderedChildColumnsList(component).iterator();
391: }
392:
393: protected List getRenderedChildColumnsList(UIComponent component) {
394: List results = new ArrayList();
395: Iterator kids = component.getChildren().iterator();
396: while (kids.hasNext()) {
397: UIComponent kid = (UIComponent) kids.next();
398: if ((kid instanceof UIColumn) && kid.isRendered()) {
399: results.add(kid);
400: }
401: }
402: return results;
403: }
404:
405: protected boolean isScrollable(UIComponent uiComponent) {
406: Object o = uiComponent.getAttributes().get("scrollable");
407: if (o != null && o instanceof Boolean) {
408: return ((Boolean) o).booleanValue();
409: }
410: return false;
411: }
412:
413: protected Element scrollBarSpacer(DOMContext domContext,
414: FacesContext facesContext) {
415: Element spacer = domContext.createElement("th");
416: //spacer.setAttribute("style", "width:20px;");
417: String url = getResourceURL(facesContext,
418: "/xmlhttp/css/xp/css-images/selection_spacer.gif");
419: Element spacerImg = domContext.createElement(HTML.IMG_ELEM);
420: spacerImg.setAttribute(HTML.SRC_ATTR, url);
421: spacerImg.setAttribute(HTML.BORDER_ATTR, "0");
422: spacer.appendChild(spacerImg);
423: return spacer;
424: }
425:
426: protected StringTokenizer getColumnWidths(UIComponent uiComponent) {
427: Object o = uiComponent.getAttributes().get("columnWidths");
428: if (o != null && o instanceof String) {
429: return new StringTokenizer(o.toString(), ",");
430: }
431: return null;
432: }
433:
434: protected Element getScrollableHeaderTableElement(Element root) {
435: // First table in first div path : table/tr/td/div/div0/table
436: return (Element) root.getFirstChild().getFirstChild()
437: .getFirstChild().getFirstChild().getFirstChild();
438: }
439:
440: protected Element getScrollableBodyTableElement(Element root) {
441: // First table in second div path table/tr/td/div/div1/table
442: return (Element) root.getFirstChild().getFirstChild()
443: .getFirstChild().getFirstChild().getNextSibling()
444: .getFirstChild();
445: }
446: }
|