001: /*
002: * Copyright 2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.myfaces.shared_impl.renderkit.html;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.apache.myfaces.shared_impl.renderkit.JSFAttr;
021: import org.apache.myfaces.shared_impl.renderkit.RendererUtils;
022: import org.apache.myfaces.shared_impl.util.ArrayUtils;
023: import org.apache.myfaces.shared_impl.util.StringUtils;
024:
025: import javax.faces.component.UIColumn;
026: import javax.faces.component.UIComponent;
027: import javax.faces.component.UIData;
028: import javax.faces.component.html.HtmlColumn;
029: import javax.faces.component.html.HtmlDataTable;
030: import javax.faces.context.FacesContext;
031: import javax.faces.context.ResponseWriter;
032: import java.io.IOException;
033: import java.util.Iterator;
034: import java.util.List;
035:
036: /**
037: * Common methods for renderers for components that subclass the standard
038: * JSF HtmlDataTable component.
039: *
040: * @author Thomas Spiegl (latest modification by $Author: baranda $)
041: * @version $Revision: 544646 $ $Date: 2007-06-05 23:51:27 +0200 (Di, 05 Jun 2007) $
042: */
043: public class HtmlTableRendererBase extends HtmlRenderer {
044: /** Header facet name. */
045: protected static final String HEADER_FACET_NAME = "header";
046:
047: /** Footer facet name. */
048: protected static final String FOOTER_FACET_NAME = "footer";
049:
050: protected static final String CAPTION_FACET_NAME = "caption";
051:
052: /** The logger. */
053: private static final Log log = LogFactory
054: .getLog(HtmlTableRendererBase.class);
055:
056: /**
057: * @param component dataTable
058: * @return number of layout columns
059: */
060: protected int getNewspaperColumns(UIComponent component) {
061: return 1;
062: }
063:
064: /**
065: * @param component dataTable
066: * @return component to display between layout columns
067: */
068: protected UIComponent getNewspaperTableSpacer(UIComponent component) {
069: return null;
070: }
071:
072: /**
073: * @param component dataTable
074: * @return whether dataTable has component to display between layout columns
075: */
076: protected boolean hasNewspaperTableSpacer(UIComponent component) {
077: return false;
078: }
079:
080: /**
081: * @param component dataTable
082: * @return whether dataTable has newspaper columns layed out horizontally
083: */
084: protected boolean isNewspaperHorizontalOrientation(
085: UIComponent component) {
086: return false;
087: }
088:
089: /**
090: * @see javax.faces.render.Renderer#getRendersChildren()
091: */
092: public boolean getRendersChildren() {
093: return true;
094: }
095:
096: /**
097: * Render the necessary bits that come before any actual <i>rows</i> in the table.
098: *
099: * @see javax.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
100: */
101: public void encodeBegin(FacesContext facesContext,
102: UIComponent uiComponent) throws IOException {
103: RendererUtils.checkParamValidity(facesContext, uiComponent,
104: UIData.class);
105:
106: ResponseWriter writer = facesContext.getResponseWriter();
107:
108: beforeTable(facesContext, (UIData) uiComponent);
109:
110: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
111: writer.startElement(HTML.TABLE_ELEM, uiComponent);
112: HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent,
113: facesContext);
114: HtmlRendererUtils
115: .renderHTMLAttributes(
116: writer,
117: uiComponent,
118: org.apache.myfaces.shared_impl.renderkit.html.HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
119: }
120:
121: /**
122: * Render the TBODY section of the html table. See also method encodeInnerHtml.
123: *
124: * @see javax.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
125: */
126: public void encodeChildren(FacesContext facesContext,
127: UIComponent component) throws IOException {
128: RendererUtils.checkParamValidity(facesContext, component,
129: UIData.class);
130:
131: ResponseWriter writer = facesContext.getResponseWriter();
132:
133: beforeBody(facesContext, (UIData) component);
134:
135: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
136: renderCaptionFacet(facesContext, writer, component);
137: writer.startElement(HTML.TBODY_ELEM, component);
138: writer.writeAttribute(HTML.ID_ATTR, component
139: .getClientId(facesContext)
140: + ":tbody_element", null);
141:
142: encodeInnerHtml(facesContext, component);
143:
144: writer.endElement(HTML.TBODY_ELEM);
145:
146: afterBody(facesContext, (UIData) component);
147: }
148:
149: /**
150: * Renders the caption facet.
151: * @param facesContext the <code>FacesContext</code>.
152: * @param writer the <code>ResponseWriter</code>.
153: * @param component the parent <code>UIComponent</code> containing the facets.
154: * @throws IOException if an exception occurs.
155: */
156: protected void renderCaptionFacet(FacesContext facesContext,
157: ResponseWriter writer, UIComponent component)
158: throws IOException {
159: UIComponent facet = (UIComponent) component.getFacets().get(
160: CAPTION_FACET_NAME);
161: if (facet != null) {
162: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
163: writer.startElement(HTML.CAPTION_ELEM, component);
164: RendererUtils.renderChild(facesContext, facet);
165: writer.endElement(HTML.CAPTION_ELEM);
166: }
167: }
168:
169: /**
170: * Gets styles for the specified component.
171: */
172: protected static Styles getStyles(UIData uiData) {
173: String rowClasses;
174: String columnClasses;
175: if (uiData instanceof HtmlDataTable) {
176: rowClasses = ((HtmlDataTable) uiData).getRowClasses();
177: columnClasses = ((HtmlDataTable) uiData).getColumnClasses();
178: } else {
179: rowClasses = (String) uiData.getAttributes().get(
180: JSFAttr.ROW_CLASSES_ATTR);
181: columnClasses = (String) uiData.getAttributes().get(
182: JSFAttr.COLUMN_CLASSES_ATTR);
183: }
184: return new Styles(rowClasses, columnClasses);
185: }
186:
187: /**
188: * Class manages the styles from String lists.
189: */
190: protected static class Styles {
191:
192: private String[] _columnStyle;
193: private String[] _rowStyle;
194:
195: Styles(String rowStyles, String columnStyles) {
196: _rowStyle = (rowStyles == null) ? ArrayUtils.EMPTY_STRING_ARRAY
197: : StringUtils.trim(StringUtils.splitShortString(
198: rowStyles, ','));
199: _columnStyle = (columnStyles == null) ? ArrayUtils.EMPTY_STRING_ARRAY
200: : StringUtils.trim(StringUtils.splitShortString(
201: columnStyles, ','));
202: }
203:
204: public String getRowStyle(int idx) {
205: if (!hasRowStyle()) {
206: return null;
207: }
208: return _rowStyle[idx % _rowStyle.length];
209: }
210:
211: public String getColumnStyle(int idx) {
212: if (!hasColumnStyle()) {
213: return null;
214: }
215: return _columnStyle[idx % _columnStyle.length];
216: }
217:
218: public boolean hasRowStyle() {
219: return _rowStyle.length > 0;
220: }
221:
222: public boolean hasColumnStyle() {
223: return _columnStyle.length > 0;
224: }
225: }
226:
227: /**
228: * Renders everything inside the TBODY tag by iterating over the row objects
229: * between offsets first and first+rows and applying the UIColumn components
230: * to those objects.
231: * <p>
232: * This method is separated from the encodeChildren so that it can be overridden by
233: * subclasses. One class that uses this functionality is autoUpdateDataTable.
234: */
235: public void encodeInnerHtml(FacesContext facesContext,
236: UIComponent component) throws IOException {
237:
238: UIData uiData = (UIData) component;
239: ResponseWriter writer = facesContext.getResponseWriter();
240:
241: // begin the table
242: // get the CSS styles
243: Styles styles = getStyles(uiData);
244:
245: int first = uiData.getFirst();
246: int rows = uiData.getRows();
247: int last;
248: if (rows <= 0) {
249: last = uiData.getRowCount();
250: } else {
251: last = first + rows;
252: if (last > uiData.getRowCount()) {
253: last = uiData.getRowCount();
254: }
255: }
256:
257: int newspaperColumns = getNewspaperColumns(component);
258: int newspaperRows;
259: if ((last - first) % newspaperColumns == 0)
260: newspaperRows = (last - first) / newspaperColumns;
261: else
262: newspaperRows = ((last - first) / newspaperColumns) + 1;
263: boolean newspaperHorizontalOrientation = isNewspaperHorizontalOrientation(component);
264:
265: // walk through the newspaper rows
266: for (int nr = 0; nr < newspaperRows; nr++) {
267: beforeRow(facesContext, uiData);
268:
269: // walk through the newspaper columns
270: for (int nc = 0; nc < newspaperColumns; nc++) {
271:
272: // the current row in the 'real' table
273: int currentRow;
274: if (newspaperHorizontalOrientation)
275: currentRow = nr * newspaperColumns + nc + first;
276: else
277: currentRow = nc * newspaperRows + nr + first;
278:
279: // if this row is not to be rendered
280: if (currentRow >= last)
281: continue;
282:
283: // bail if any row does not exist
284: uiData.setRowIndex(currentRow);
285: if (!uiData.isRowAvailable()) {
286: log.error("Row is not available. Rowindex = "
287: + currentRow);
288: break;
289: }
290:
291: if (nc == 0) {
292: // first column in table, start new row
293: beforeRow(facesContext, uiData);
294:
295: HtmlRendererUtils
296: .writePrettyLineSeparator(facesContext);
297: renderRowStart(facesContext, writer, uiData,
298: styles, nr);
299: }
300:
301: List children = getChildren(component);
302: for (int j = 0, size = getChildCount(component); j < size; j++) {
303: UIComponent child = (UIComponent) children.get(j);
304: if (child.isRendered()) {
305: boolean columnRendering = child instanceof UIColumn;
306:
307: if (columnRendering)
308: beforeColumn(facesContext, uiData, j);
309:
310: encodeColumnChild(facesContext, writer, uiData,
311: child, styles, nc
312: * uiData.getChildCount() + j);
313:
314: if (columnRendering)
315: afterColumn(facesContext, uiData, j);
316: }
317: }
318:
319: if (hasNewspaperTableSpacer(uiData)) {
320: // draw the spacer facet
321: if (nc < newspaperColumns - 1)
322: renderSpacerCell(facesContext, writer, uiData);
323: }
324: }
325: renderRowEnd(facesContext, writer, uiData);
326:
327: afterRow(facesContext, uiData);
328: }
329: }
330:
331: protected void encodeColumnChild(FacesContext facesContext,
332: ResponseWriter writer, UIData uiData,
333: UIComponent component, Styles styles, int columnStyleIndex)
334: throws IOException {
335: if (component instanceof UIColumn) {
336: renderColumnBody(facesContext, writer, uiData, component,
337: styles, columnStyleIndex);
338: }
339: }
340:
341: /**
342: * Renders the body of a given <code>UIColumn</code> (everything but
343: * the header and footer facets). This emits a TD cell, whose contents
344: * are the result of calling encodeBegin, encodeChildren and
345: * encodeEnd methods on the component (or its associated renderer).
346: *
347: * @param facesContext the <code>FacesContext</code>.
348: * @param writer the <code>ResponseWriter</code>.
349: * @param uiData the <code>UIData</code> being rendered.
350: * @param component the <code>UIComponent</code> to render.
351: * @throws IOException if an exception occurs.
352: */
353: protected void renderColumnBody(FacesContext facesContext,
354: ResponseWriter writer, UIData uiData,
355: UIComponent component, Styles styles, int columnStyleIndex)
356: throws IOException {
357: writer.startElement(HTML.TD_ELEM, uiData);
358: if (styles.hasColumnStyle()) {
359: writer.writeAttribute(HTML.CLASS_ATTR, styles
360: .getColumnStyle(columnStyleIndex), null);
361: }
362:
363: RendererUtils.renderChild(facesContext, component);
364: writer.endElement(HTML.TD_ELEM);
365: }
366:
367: /**
368: * Renders the start of a new row of body content.
369: * @param facesContext the <code>FacesContext</code>.
370: * @param writer the <code>ResponseWriter</code>.
371: * @param uiData the <code>UIData</code> being rendered.
372: * @throws IOException if an exceptoin occurs.
373: */
374: protected void renderRowStart(FacesContext facesContext,
375: ResponseWriter writer, UIData uiData, Styles styles,
376: int rowStyleIndex) throws IOException {
377: writer.startElement(HTML.TR_ELEM, uiData);
378:
379: renderRowStyle(facesContext, writer, uiData, styles,
380: rowStyleIndex);
381:
382: Object rowId = uiData
383: .getAttributes()
384: .get(
385: org.apache.myfaces.shared_impl.renderkit.JSFAttr.ROW_ID);
386:
387: if (rowId != null) {
388: writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
389: }
390: }
391:
392: protected void renderRowStyle(FacesContext facesContext,
393: ResponseWriter writer, UIData uiData, Styles styles,
394: int rowStyleIndex) throws IOException {
395: if (styles.hasRowStyle()) {
396: String rowStyle = styles.getRowStyle(rowStyleIndex);
397: writer.writeAttribute(HTML.CLASS_ATTR, rowStyle, null);
398: }
399: }
400:
401: /**
402: * Renders the end of a row of body content.
403: * @param facesContext the <code>FacesContext</code>.
404: * @param writer the <code>ResponseWriter</code>.
405: * @param uiData the <code>UIData</code> being rendered.
406: * @throws IOException if an exceptoin occurs.
407: */
408: protected void renderRowEnd(FacesContext facesContext,
409: ResponseWriter writer, UIData uiData) throws IOException {
410: writer.endElement(HTML.TR_ELEM);
411: }
412:
413: /**
414: * Perform any operations necessary immediately before the TABLE start tag
415: * is output.
416: *
417: * @param facesContext the <code>FacesContext</code>.
418: * @param uiData the <code>UIData</code> being rendered.
419: */
420: protected void beforeTable(FacesContext facesContext, UIData uiData)
421: throws IOException {
422: }
423:
424: /**
425: * Perform any operations necessary after TABLE start tag is output
426: * but before the TBODY start tag.
427: * <p>
428: * This method generates the THEAD/TFOOT sections of a table if there
429: * are any header or footer facets defined on the table or on any child
430: * UIColumn component.
431: *
432: * @param facesContext the <code>FacesContext</code>.
433: * @param uiData the <code>UIData</code> being rendered.
434: */
435: protected void beforeBody(FacesContext facesContext, UIData uiData)
436: throws IOException {
437: ResponseWriter writer = facesContext.getResponseWriter();
438:
439: HtmlRendererUtils.renderTableCaption(facesContext, writer,
440: uiData);
441: renderFacet(facesContext, writer, uiData, true);
442: renderFacet(facesContext, writer, uiData, false);
443: }
444:
445: /**
446: * Perform any operations necessary immediately before each TR start tag
447: * is output.
448: *
449: * @param facesContext the <code>FacesContext</code>.
450: * @param uiData the <code>UIData</code> being rendered.
451: */
452: protected void beforeRow(FacesContext facesContext, UIData uiData)
453: throws IOException {
454: }
455:
456: /**
457: * Perform any operations necessary immediately after each TR end tag
458: * is output.
459: *
460: * @param facesContext the <code>FacesContext</code>.
461: * @param uiData the <code>UIData</code> being rendered.
462: */
463: protected void afterRow(FacesContext facesContext, UIData uiData)
464: throws IOException {
465: }
466:
467: /**
468: * Perform any operations necessary immediately before each column child is rendered
469: *
470: * @param facesContext the <code>FacesContext</code>.
471: * @param uiData the <code>UIData</code> being rendered.
472: * @param columnIndex the index of the currenly rendered column
473: */
474: protected void beforeColumn(FacesContext facesContext,
475: UIData uiData, int columnIndex) throws IOException {
476: }
477:
478: /**
479: * Perform any operations necessary immediately after each column child is rendered
480: *
481: * @param facesContext the <code>FacesContext</code>.
482: * @param uiData the <code>UIData</code> being rendered.
483: * @param columnIndex the index of the currenly rendered column
484: */
485: protected void afterColumn(FacesContext facesContext,
486: UIData uiData, int columnIndex) throws IOException {
487: }
488:
489: /**
490: *Perform any operations necessary immediately before each column child's header or footer is rendered
491: *
492: * @param facesContext the <code>FacesContext</code>.
493: * @param uiData the <code>UIData</code> being rendered.
494: * @param header true if the header of the column child is rendered
495: * @param columnIndex the index of the currenly rendered column
496: */
497: protected void beforeColumnHeaderOrFooter(
498: FacesContext facesContext, UIData uiData, boolean header,
499: int columnIndex) throws IOException {
500: }
501:
502: /**
503: * Perform any operations necessary immediately after each column child's header of footer is rendered
504: *
505: * @param facesContext the <code>FacesContext</code>.
506: * @param uiData the <code>UIData</code> being rendered.
507: * @param header true if the header of the column child is rendered
508: * @param columnIndex the index of the currenly rendered column
509: */
510: protected void afterColumnHeaderOrFooter(FacesContext facesContext,
511: UIData uiData, boolean header, int columnIndex)
512: throws IOException {
513: }
514:
515: /**
516: * Perform any operations necessary in the TBODY start tag.
517: *
518: * @param facesContext the <code>FacesContext</code>.
519: * @param uiData the <code>UIData</code> being rendered.
520: */
521: protected void inBodyStart(FacesContext facesContext, UIData uiData)
522: throws IOException {
523: }
524:
525: /**
526: * Perform any operations necessary immediately after the TBODY end tag
527: * is output.
528: *
529: * @param facesContext the <code>FacesContext</code>.
530: * @param uiData the <code>UIData</code> being rendered.
531: */
532: protected void afterBody(FacesContext facesContext, UIData uiData)
533: throws IOException {
534: }
535:
536: /**
537: * Perform any operations necessary immediately after the TABLE end tag
538: * is output.
539: *
540: * @param facesContext the <code>FacesContext</code>.
541: * @param uiData the <code>UIData</code> being rendered.
542: */
543: protected void afterTable(FacesContext facesContext, UIData uiData)
544: throws IOException {
545: }
546:
547: /**
548: * @see javax.faces.render.Renderer#encodeEnd(FacesContext, UIComponent)
549: */
550: public void encodeEnd(FacesContext facesContext,
551: UIComponent uiComponent) throws IOException {
552: RendererUtils.checkParamValidity(facesContext, uiComponent,
553: UIData.class);
554:
555: ResponseWriter writer = facesContext.getResponseWriter();
556: writer.endElement(HTML.TABLE_ELEM);
557: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
558:
559: afterTable(facesContext, (UIData) uiComponent);
560: }
561:
562: /**
563: * Renders either the header or the footer facets for the UIData component
564: * and all the child UIColumn components, as a THEAD or TFOOT element
565: * containing TR (row) elements.
566: * <p>
567: * If there is a header or footer attached to the UIData then that is
568: * rendered as a TR element whose COLSPAN is the sum of all rendered
569: * columns in the table. This allows that header/footer to take up the
570: * entire width of the table.
571: * <p>
572: * If any child column has a header or footer then a TR is rendered
573: * with a TH cell for each column child.
574: *
575: * @param facesContext the <code>FacesContext</code>.
576: * @param writer the <code>ResponseWriter</code>.
577: * @param component the UIData component
578: * @param header whether this is the header facet (if not, then the footer facet).
579: * @throws IOException if an exception occurs.
580: */
581: protected void renderFacet(FacesContext facesContext,
582: ResponseWriter writer, UIComponent component, boolean header)
583: throws IOException {
584: int colspan = 0;
585: boolean hasColumnFacet = false;
586: for (Iterator it = getChildren(component).iterator(); it
587: .hasNext();) {
588: UIComponent uiComponent = (UIComponent) it.next();
589: if (uiComponent.isRendered()) {
590: // a UIColumn has a span of 1, anything else has a span of 0
591: colspan += determineChildColSpan(uiComponent);
592:
593: // hasColumnFacet is true if *any* child column has a facet of
594: // the specified type.
595: if (!hasColumnFacet) {
596: hasColumnFacet = hasFacet(header, uiComponent);
597: }
598: }
599: }
600:
601: UIComponent facet = header ? (UIComponent) component
602: .getFacets().get(HEADER_FACET_NAME)
603: : (UIComponent) component.getFacets().get(
604: FOOTER_FACET_NAME);
605: if (facet != null || hasColumnFacet) {
606: // Header or Footer present on either the UIData or a column, so we
607: // definitely need to render the THEAD or TFOOT section.
608: String elemName = header ? HTML.THEAD_ELEM
609: : HTML.TFOOT_ELEM;
610:
611: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
612: writer.startElement(elemName, component);
613: if (header) {
614: String headerStyleClass = getHeaderClass(component);
615: if (facet != null)
616: renderTableHeaderRow(facesContext, writer,
617: component, facet, headerStyleClass, colspan);
618: if (hasColumnFacet)
619: renderColumnHeaderRow(facesContext, writer,
620: component, headerStyleClass);
621: } else {
622: String footerStyleClass = getFooterClass(component);
623: if (hasColumnFacet)
624: renderColumnFooterRow(facesContext, writer,
625: component, footerStyleClass);
626: if (facet != null)
627: renderTableFooterRow(facesContext, writer,
628: component, facet, footerStyleClass, colspan);
629: }
630: writer.endElement(elemName);
631: }
632: }
633:
634: /**
635: * @param header
636: * @param uiComponent
637: * @return boolean
638: */
639: protected boolean hasFacet(boolean header, UIComponent uiComponent) {
640: if (uiComponent instanceof UIColumn) {
641: UIColumn uiColumn = (UIColumn) uiComponent;
642: return header ? uiColumn.getHeader() != null : uiColumn
643: .getFooter() != null;
644: }
645: return false;
646: }
647:
648: /**
649: * Calculate the number of columns the specified child component will span
650: * when rendered.
651: * <p>
652: * Normally, this is a fairly simple calculation: a UIColumn component
653: * is rendered as one column, every other child type is not rendered
654: * (ie spans zero columns). However custom subclasses of this renderer may
655: * override this method to handle cases where a single component renders
656: * as multiple columns.
657: */
658: protected int determineChildColSpan(UIComponent uiComponent) {
659: if (uiComponent instanceof UIColumn) {
660: return 1;
661: }
662: return 0;
663: }
664:
665: /**
666: * Renders the header row of the table being rendered.
667: * @param facesContext the <code>FacesContext</code>.
668: * @param writer the <code>ResponseWriter</code>.
669: * @param component the <code>UIComponent</code> for whom a table is being rendered.
670: * @param headerFacet the facet for the header.
671: * @param headerStyleClass the styleClass of the header.
672: * @param colspan the number of columns the header should span. Typically, this is
673: * the number of columns in the table.
674: * @throws IOException if an exception occurs.
675: */
676: protected void renderTableHeaderRow(FacesContext facesContext,
677: ResponseWriter writer, UIComponent component,
678: UIComponent headerFacet, String headerStyleClass,
679: int colspan) throws IOException {
680: renderTableHeaderOrFooterRow(facesContext, writer, component,
681: headerFacet, headerStyleClass, HTML.TH_ELEM, colspan,
682: true);
683: }
684:
685: /**
686: * Renders the footer row of the table being rendered.
687: * @param facesContext the <code>FacesContext</code>.
688: * @param writer the <code>ResponseWriter</code>.
689: * @param component the <code>UIComponent</code> for whom a table is being rendered.
690: * @param footerFacet the facet for the footer.
691: * @param footerStyleClass the styleClass of the footer.
692: * @param colspan the number of columns the header should span. Typically, this is
693: * the number of columns in the table.
694: * @throws IOException if an exception occurs.
695: */
696: protected void renderTableFooterRow(FacesContext facesContext,
697: ResponseWriter writer, UIComponent component,
698: UIComponent footerFacet, String footerStyleClass,
699: int colspan) throws IOException {
700: renderTableHeaderOrFooterRow(facesContext, writer, component,
701: footerFacet, footerStyleClass, HTML.TD_ELEM, colspan,
702: false);
703: }
704:
705: /**
706: * Renders the header row for the columns, which is a separate row from the header row for the
707: * <code>UIData</code> header facet.
708: *
709: * @param facesContext the <code>FacesContext</code>.
710: * @param writer the <code>ResponseWriter</code>.
711: * @param component the UIData component for whom a table is being rendered.
712: * @param headerStyleClass the styleClass of the header
713: * @throws IOException if an exception occurs.
714: */
715: protected void renderColumnHeaderRow(FacesContext facesContext,
716: ResponseWriter writer, UIComponent component,
717: String headerStyleClass) throws IOException {
718: renderColumnHeaderOrFooterRow(facesContext, writer, component,
719: headerStyleClass, true);
720: }
721:
722: /**
723: * Renders the footer row for the columns, which is a separate row from the footer row for the
724: * <code>UIData</code> footer facet.
725: * @param facesContext the <code>FacesContext</code>.
726: * @param writer the <code>ResponseWriter</code>.
727: * @param component the <code>UIComponent</code> for whom a table is being rendered.
728: * @param footerStyleClass the styleClass of the footerStyleClass
729: * @throws IOException if an exception occurs.
730: */
731: protected void renderColumnFooterRow(FacesContext facesContext,
732: ResponseWriter writer, UIComponent component,
733: String footerStyleClass) throws IOException {
734: renderColumnHeaderOrFooterRow(facesContext, writer, component,
735: footerStyleClass, false);
736: }
737:
738: private void renderTableHeaderOrFooterRow(
739: FacesContext facesContext, ResponseWriter writer,
740: UIComponent component, UIComponent facet,
741: String styleClass, String colElementName, int colspan,
742: boolean isHeader) throws IOException {
743: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
744: writer.startElement(HTML.TR_ELEM, component);
745: writer.startElement(colElementName, component);
746: if (colElementName.equals(HTML.TH_ELEM) && isHeader) {
747: writer.writeAttribute(HTML.SCOPE_ATTR,
748: HTML.SCOPE_COLGROUP_VALUE, null);
749: }
750:
751: // span all the table's columns
752: int newsPaperColumns = getNewspaperColumns(component);
753: int totalColumns = colspan * newsPaperColumns;
754: if (hasNewspaperTableSpacer(component)) {
755: totalColumns = totalColumns + newsPaperColumns - 1;
756: }
757: writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(
758: totalColumns), null);
759: if (styleClass != null) {
760: writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
761: }
762: if (facet != null) {
763: RendererUtils.renderChild(facesContext, facet);
764: }
765: writer.endElement(colElementName);
766: writer.endElement(HTML.TR_ELEM);
767: }
768:
769: /**
770: * @param component the UIData component for whom a table is being rendered.
771: */
772: private void renderColumnHeaderOrFooterRow(
773: FacesContext facesContext, ResponseWriter writer,
774: UIComponent component, String styleClass, boolean header)
775: throws IOException {
776: HtmlRendererUtils.writePrettyLineSeparator(facesContext);
777:
778: writer.startElement(HTML.TR_ELEM, component);
779: int columnIndex = 0;
780: int newspaperColumns = getNewspaperColumns(component);
781: for (int nc = 0; nc < newspaperColumns; nc++) {
782: for (Iterator it = getChildren(component).iterator(); it
783: .hasNext();) {
784: UIComponent uiComponent = (UIComponent) it.next();
785: if (uiComponent.isRendered()) {
786: if (component instanceof UIData
787: && uiComponent instanceof UIColumn)
788: beforeColumnHeaderOrFooter(facesContext,
789: (UIData) component, header, columnIndex);
790:
791: renderColumnChildHeaderOrFooterRow(facesContext,
792: writer, uiComponent, styleClass, header);
793:
794: if (component instanceof UIData
795: && uiComponent instanceof UIColumn)
796: afterColumnHeaderOrFooter(facesContext,
797: (UIData) component, header, columnIndex);
798: }
799: columnIndex += 1;
800: }
801:
802: if (hasNewspaperTableSpacer(component)) {
803: // draw the spacer facet
804: if (nc < newspaperColumns - 1)
805: renderSpacerCell(facesContext, writer, component);
806: }
807: }
808: writer.endElement(HTML.TR_ELEM);
809: }
810:
811: /**
812: * Renders a spacer between adjacent newspaper columns.
813: */
814: protected void renderSpacerCell(FacesContext facesContext,
815: ResponseWriter writer, UIComponent component)
816: throws IOException {
817: UIComponent spacer = getNewspaperTableSpacer(component);
818: if (spacer == null)
819: return;
820:
821: writer.startElement(HTML.TD_ELEM, component);
822: RendererUtils.renderChild(facesContext, spacer);
823: writer.endElement(HTML.TD_ELEM);
824: }
825:
826: protected void renderColumnChildHeaderOrFooterRow(
827: FacesContext facesContext, ResponseWriter writer,
828: UIComponent uiComponent, String styleClass, boolean isHeader)
829: throws IOException {
830: if (uiComponent instanceof UIColumn) {
831: // allow column to override style class, new in JSF 1.2
832: if (uiComponent instanceof HtmlColumn) {
833: HtmlColumn column = (HtmlColumn) uiComponent;
834: if (isHeader && column.getHeaderClass() != null)
835: styleClass = column.getHeaderClass();
836: else if (!isHeader && column.getFooterClass() != null)
837: styleClass = column.getFooterClass();
838: }
839:
840: if (isHeader) {
841: renderColumnHeaderCell(facesContext, writer,
842: uiComponent, ((UIColumn) uiComponent)
843: .getHeader(), styleClass, 0);
844: } else {
845: renderColumnFooterCell(facesContext, writer,
846: uiComponent, ((UIColumn) uiComponent)
847: .getFooter(), styleClass, 0);
848: }
849: }
850: }
851:
852: /**
853: * Renders the header facet for the given <code>UIColumn</code>.
854: * @param facesContext the <code>FacesContext</code>.
855: * @param writer the <code>ResponseWriter</code>.
856: * @param uiColumn the <code>UIColumn</code>.
857: * @param headerStyleClass the styleClass of the header facet.
858: * @param colspan the colspan for the tableData element in which the header facet
859: * will be wrapped.
860: * @throws IOException
861: */
862: protected void renderColumnHeaderCell(FacesContext facesContext,
863: ResponseWriter writer, UIColumn uiColumn,
864: String headerStyleClass, int colspan) throws IOException {
865: renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn
866: .getHeader(), headerStyleClass, colspan);
867: }
868:
869: /**
870: * Renders a TH cell within a TR within a THEAD section. If the specified
871: * UIColumn object does have a header facet, then that facet is rendered
872: * within the cell, otherwise the cell is left blank (though any specified
873: * style class is still applied to empty cells).
874: *
875: * @param facesContext the <code>FacesContext</code>.
876: * @param writer the <code>ResponseWriter</code>.
877: * @param uiComponent the <code>UIComponent</code> to render the facet for.
878: * @param facet the <code>UIComponent</code> to render as facet.
879: * @param headerStyleClass the styleClass of the header facet.
880: * @param colspan the colspan for the tableData element in which the header facet
881: * will be wrapped.
882: * @throws IOException
883: */
884: protected void renderColumnHeaderCell(FacesContext facesContext,
885: ResponseWriter writer, UIComponent uiComponent,
886: UIComponent facet, String headerStyleClass, int colspan)
887: throws IOException {
888: writer.startElement(HTML.TH_ELEM, uiComponent);
889: if (colspan > 1) {
890: writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(
891: colspan), null);
892: }
893: if (headerStyleClass != null) {
894: writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass,
895: null);
896: }
897:
898: writer.writeAttribute(HTML.SCOPE_ATTR, "col", null);
899:
900: if (facet != null) {
901: RendererUtils.renderChild(facesContext, facet);
902: }
903:
904: writer.endElement(HTML.TH_ELEM);
905: }
906:
907: /**
908: * Renders the footer facet for the given <code>UIColumn</code>.
909: * @param facesContext the <code>FacesContext</code>.
910: * @param writer the <code>ResponseWriter</code>.
911: * @param uiColumn the <code>UIComponent</code>.
912: * @param footerStyleClass the styleClass of the footer facet.
913: * @param colspan the colspan for the tableData element in which the footer facet
914: * will be wrapped.
915: * @throws IOException
916: */
917: protected void renderColumnFooterCell(FacesContext facesContext,
918: ResponseWriter writer, UIColumn uiColumn,
919: String footerStyleClass, int colspan) throws IOException {
920: renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn
921: .getFooter(), footerStyleClass, colspan);
922: }
923:
924: /**
925: * Renders the footer facet for the given <code>UIColumn</code>.
926: * @param facesContext the <code>FacesContext</code>.
927: * @param writer the <code>ResponseWriter</code>.
928: * @param uiComponent the <code>UIComponent</code> to render the facet for.
929: * @param facet the <code>UIComponent</code> to render as facet.
930: * @param footerStyleClass the styleClass of the footer facet.
931: * @param colspan the colspan for the tableData element in which the footer facet
932: * will be wrapped.
933: * @throws IOException
934: */
935: protected void renderColumnFooterCell(FacesContext facesContext,
936: ResponseWriter writer, UIComponent uiComponent,
937: UIComponent facet, String footerStyleClass, int colspan)
938: throws IOException {
939: writer.startElement(HTML.TD_ELEM, uiComponent);
940: if (colspan > 1) {
941: writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(
942: colspan), null);
943: }
944: if (footerStyleClass != null) {
945: writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass,
946: null);
947: }
948: if (facet != null) {
949: RendererUtils.renderChild(facesContext, facet);
950: }
951: writer.endElement(HTML.TD_ELEM);
952: }
953:
954: /**
955: * Gets the headerClass attribute of the given <code>UIComponent</code>.
956: * @param component the <code>UIComponent</code>.
957: * @return the headerClass attribute of the given <code>UIComponent</code>.
958: */
959: protected static String getHeaderClass(UIComponent component) {
960: if (component instanceof HtmlDataTable) {
961: return ((HtmlDataTable) component).getHeaderClass();
962: } else {
963: return (String) component
964: .getAttributes()
965: .get(
966: org.apache.myfaces.shared_impl.renderkit.JSFAttr.HEADER_CLASS_ATTR);
967: }
968: }
969:
970: /**
971: * Gets the footerClass attribute of the given <code>UIComponent</code>.
972: * @param component the <code>UIComponent</code>.
973: * @return the footerClass attribute of the given <code>UIComponent</code>.
974: */
975: protected static String getFooterClass(UIComponent component) {
976: if (component instanceof HtmlDataTable) {
977: return ((HtmlDataTable) component).getFooterClass();
978: } else {
979: return (String) component
980: .getAttributes()
981: .get(
982: org.apache.myfaces.shared_impl.renderkit.JSFAttr.FOOTER_CLASS_ATTR);
983: }
984: }
985:
986: public void decode(FacesContext context, UIComponent component) {
987: super.decode(context, component);
988: }
989:
990: }
|