View Javadoc

1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.myfaces.renderkit.html;
17  
18  import org.apache.myfaces.renderkit.JSFAttr;
19  import org.apache.myfaces.renderkit.RendererUtils;
20  import org.apache.myfaces.util.ArrayUtils;
21  import org.apache.myfaces.util.StringUtils;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  import javax.faces.component.UIColumn;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIData;
29  import javax.faces.component.html.HtmlDataTable;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import java.io.IOException;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.NoSuchElementException;
36  
37  /***
38   * Common methods for renderers for components that subclass the standard
39   * JSF HtmlDataTable component.
40   * 
41   * @author Thomas Spiegl (latest modification by $Author: skitching $)
42   * @version $Revision: 358448 $ $Date: 2005-12-22 02:52:45 +0000 (Thu, 22 Dec 2005) $
43   */
44  public class HtmlTableRendererBase extends HtmlRenderer
45  {
46      /*** Header facet name. */
47      protected static final String HEADER_FACET_NAME = "header";
48  
49      /*** Footer facet name. */
50      protected static final String FOOTER_FACET_NAME = "footer";
51  
52      /*** The logger. */
53      private static final Log log = LogFactory.getLog(HtmlTableRendererBase.class);
54  
55      /***
56       * @see javax.faces.render.Renderer#getRendersChildren()
57       */
58      public boolean getRendersChildren()
59      {
60          return true;
61      }
62  
63      /***
64       * Render the necessary bits that come before any actual <i>rows</i> in the table.
65       * 
66       * @see javax.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
67       */
68      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
69      {
70          RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
71  
72          ResponseWriter writer = facesContext.getResponseWriter();
73  
74          beforeTable(facesContext, (UIData) uiComponent);
75  
76          HtmlRendererUtils.writePrettyLineSeparator(facesContext);
77          writer.startElement(HTML.TABLE_ELEM, uiComponent);
78          HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
79          HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
80      }
81  
82      /***
83       * Render the TBODY section of the html table. See also method encodeInnerHtml.
84       * 
85       * @see javax.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
86       */
87      public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
88      {
89          RendererUtils.checkParamValidity(facesContext, component, UIData.class);
90  
91          ResponseWriter writer = facesContext.getResponseWriter();
92  
93          beforeBody(facesContext, (UIData) component);
94  
95          HtmlRendererUtils.writePrettyLineSeparator(facesContext);
96          writer.startElement(HTML.TBODY_ELEM, component);
97          writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext)+":tbody_element", null);
98  
99          encodeInnerHtml(facesContext, component);
100 
101         writer.endElement(HTML.TBODY_ELEM);
102 
103         afterBody(facesContext, (UIData) component);
104     }
105 
106     /***
107      * Renders everything inside the TBODY tag by iterating over the row objects
108      * between offsets first and first+rows and applying the UIColumn components
109      * to those objects. 
110      * <p>
111      * This method is separated from the encodeChildren so that it can be overridden by
112      * subclasses. One class that uses this functionality is autoUpdateDataTable.
113      */
114      public void encodeInnerHtml(FacesContext facesContext, UIComponent component)throws IOException{
115 
116         UIData uiData = (UIData) component;
117         ResponseWriter writer = facesContext.getResponseWriter();
118 
119         String rowClasses;
120         String columnClasses;
121         if (component instanceof HtmlDataTable)
122         {
123             rowClasses = ((HtmlDataTable) component).getRowClasses();
124             columnClasses = ((HtmlDataTable) component).getColumnClasses();
125         }
126         else
127         {
128             rowClasses = (String) component.getAttributes().get(JSFAttr.ROW_CLASSES_ATTR);
129             columnClasses = (String) component.getAttributes().get(JSFAttr.COLUMN_CLASSES_ATTR);
130         }
131         Iterator rowStyleIterator = new StyleIterator(rowClasses);
132         StyleIterator columnStyleIterator = new StyleIterator(columnClasses);
133 
134         int first = uiData.getFirst();
135         int rows = uiData.getRows();
136         int rowCount = uiData.getRowCount();
137         if (rows <= 0)
138         {
139             rows = rowCount - first;
140         }
141         int last = first + rows;
142         if (last > rowCount)
143             last = rowCount;
144 
145         for (int i = first; i < last; i++)
146         {            
147             uiData.setRowIndex(i);
148             if (!uiData.isRowAvailable())
149             {
150                 log.error("Row is not available. Rowindex = " + i);
151                 return;
152             }
153             columnStyleIterator.reset();
154             
155             beforeRow(facesContext, uiData);
156 
157             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
158             renderRowStart(facesContext, writer, uiData, rowStyleIterator);
159 
160             List children = getChildren(component);
161             for (int j = 0, size = getChildCount(component); j < size; j++)
162             {
163                 UIComponent child = (UIComponent) children.get(j);
164                 if(child.isRendered())
165                 {
166                     encodeColumnChild(facesContext, writer, uiData, child, columnStyleIterator);
167                 }
168             }
169             renderRowEnd(facesContext, writer, uiData);
170 
171             afterRow(facesContext, uiData);
172         }
173     }
174 
175     protected void encodeColumnChild(FacesContext facesContext, ResponseWriter writer,
176         UIData uiData, UIComponent component, Iterator columnStyleIterator) throws IOException
177     {
178         if (component instanceof UIColumn)
179         {
180             renderColumnBody(facesContext, writer, uiData, component, columnStyleIterator);
181         }
182     }
183 
184     /***
185      * Renders the body of a given <code>UIColumn</code> (everything but
186      * the header and footer facets). This emits a TD cell, whose contents
187      * are the result of calling encodeBegin, encodeChildren and
188      * encodeEnd methods on the component (or its associated renderer).
189      * 
190      * @param facesContext the <code>FacesContext</code>.
191      * @param writer the <code>ResponseWriter</code>.
192      * @param uiData the <code>UIData</code> being rendered.
193      * @param component the <code>UIComponent</code> to render.
194      * @param columnStyleIterator the styleClass of the <code>UIColumn</code> or <code>null</code> if
195      * there is none.
196      * @throws IOException if an exception occurs.
197      */
198     protected void renderColumnBody(
199             FacesContext facesContext,
200             ResponseWriter writer,
201             UIData uiData,
202             UIComponent component,
203             Iterator columnStyleIterator) throws IOException
204     {
205         writer.startElement(HTML.TD_ELEM, uiData);
206         if (columnStyleIterator.hasNext())
207         {
208             writer.writeAttribute(HTML.CLASS_ATTR, columnStyleIterator.next(), null);
209         }
210     
211         RendererUtils.renderChild(facesContext, component);
212         writer.endElement(HTML.TD_ELEM);
213     }
214 
215     /***
216      * Renders the start of a new row of body content.
217      * @param facesContext the <code>FacesContext</code>.
218      * @param writer the <code>ResponseWriter</code>.
219      * @param uiData the <code>UIData</code> being rendered.
220      * @param rowStyleIterator te styleClass of the row or <code>null</code> if there is none.
221      * @throws IOException if an exceptoin occurs.
222      */
223     protected void renderRowStart(
224         FacesContext facesContext,
225         ResponseWriter writer,
226         UIData uiData,
227         Iterator rowStyleIterator) throws IOException
228     {
229         writer.startElement(HTML.TR_ELEM, uiData);
230         
231         renderRowStyle(facesContext, writer, uiData, rowStyleIterator);
232         
233         Object rowId = uiData.getAttributes().get(JSFAttr.ROW_ID);
234 
235         if (rowId != null)
236         {
237             writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
238         }
239     }
240 
241     protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, UIData uiData, Iterator rowStyleIterator) throws IOException
242     {
243       if (rowStyleIterator.hasNext())
244       {
245           writer.writeAttribute(HTML.CLASS_ATTR, rowStyleIterator.next(), null);
246       }
247     }
248 
249     /***
250      * Renders the end of a row of body content.
251      * @param facesContext the <code>FacesContext</code>.
252      * @param writer the <code>ResponseWriter</code>.
253      * @param uiData the <code>UIData</code> being rendered.
254      * @throws IOException if an exceptoin occurs.
255      */
256     protected void renderRowEnd(
257         FacesContext facesContext,
258         ResponseWriter writer,
259         UIData uiData) throws IOException
260     {
261         writer.endElement(HTML.TR_ELEM);
262     }
263 
264     /***
265      * Perform any operations necessary immediately before the TABLE start tag
266      * is output.
267      *
268      * @param facesContext the <code>FacesContext</code>.
269      * @param uiData the <code>UIData</code> being rendered.
270      */
271     protected void beforeTable(FacesContext facesContext, UIData uiData) throws IOException
272     {
273     }
274 
275     /***
276      * Perform any operations necessary after TABLE start tag is output
277      * but before the TBODY start tag.
278      * <p>
279      * This method generates the THEAD/TFOOT sections of a table if there
280      * are any header or footer facets defined on the table or on any child
281      * UIColumn component.
282      *
283      * @param facesContext the <code>FacesContext</code>.
284      * @param uiData the <code>UIData</code> being rendered.
285      */
286     protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException
287     {
288         ResponseWriter writer = facesContext.getResponseWriter();
289         renderFacet(facesContext, writer, uiData, true);
290         renderFacet(facesContext, writer, uiData, false);
291     }
292 
293     /***
294      * Perform any operations necessary immediately before each TR start tag
295      * is output.
296      *
297      * @param facesContext the <code>FacesContext</code>.
298      * @param uiData the <code>UIData</code> being rendered.
299      */
300     protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException
301     {
302     }
303 
304     /***
305      * Perform any operations necessary immediately after each TR end tag
306      * is output.
307      *
308      * @param facesContext the <code>FacesContext</code>.
309      * @param uiData the <code>UIData</code> being rendered.
310      */
311     protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException
312     {
313     }
314 
315     /***
316      * Perform any operations necessary immediately after the TBODY end tag
317      * is output.
318      *
319      * @param facesContext the <code>FacesContext</code>.
320      * @param uiData the <code>UIData</code> being rendered.
321      */
322     protected void afterBody(FacesContext facesContext, UIData uiData) throws IOException
323     {
324     }
325 
326     /***
327      * Perform any operations necessary immediately after the TABLE end tag
328      * is output.
329      *
330      * @param facesContext the <code>FacesContext</code>.
331      * @param uiData the <code>UIData</code> being rendered.
332      */
333     protected void afterTable(FacesContext facesContext, UIData uiData) throws IOException
334     {
335     }
336 
337     /***
338      * @see javax.faces.render.Renderer#encodeEnd(FacesContext, UIComponent)
339      */
340     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
341     {
342         RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
343 
344         ResponseWriter writer = facesContext.getResponseWriter();
345         writer.endElement(HTML.TABLE_ELEM);
346         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
347 
348         afterTable(facesContext, (UIData) uiComponent);
349     }
350 
351     /***
352      * Renders either the header or the footer facets for the UIData component
353      * and all the child UIColumn components, as a THEAD or TFOOT element
354      * containing TR (row) elements.
355      * <p>
356      * If there is a header or footer attached to the UIData then that is
357      * rendered as a TR element whose COLSPAN is the sum of all rendered
358      * columns in the table. This allows that header/footer to take up the
359      * entire width of the table.
360      * <p>
361      * If any child column has a header or footer then a TR is rendered
362      * with a TH cell for each column child. 
363      * 
364      * @param facesContext the <code>FacesContext</code>.
365      * @param writer the <code>ResponseWriter</code>.
366      * @param component the UIData component
367      * @param header whether this is the header facet (if not, then the footer facet).
368      * @throws IOException if an exception occurs.
369      */
370     protected void renderFacet(FacesContext facesContext, ResponseWriter writer,
371             UIComponent component, boolean header)
372             throws IOException
373     {
374         int colspan = 0;
375         boolean hasColumnFacet = false;
376         for (Iterator it = getChildren(component).iterator(); it.hasNext();)
377         {
378             UIComponent uiComponent = (UIComponent) it.next();
379             if(uiComponent.isRendered())
380             {
381                 // a UIColumn has a span of 1, anything else has a span of 0
382                 colspan += determineChildColSpan(uiComponent);
383 
384                 // hasColumnFacet is true if *any* child column has a facet of
385                 // the specified type.
386                 if (!hasColumnFacet)
387                 {
388                      hasColumnFacet = hasFacet(header, uiComponent);
389                 }
390             }
391         }
392 
393         UIComponent facet = header ? (UIComponent) component.getFacets().get(HEADER_FACET_NAME)
394                 : (UIComponent) component.getFacets().get(FOOTER_FACET_NAME);
395         if (facet != null || hasColumnFacet)
396         {
397             // Header or Footer present on either the UIData or a column, so we
398             // definitely need to render the THEAD or TFOOT section.
399             String elemName = header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM;
400 
401             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
402             writer.startElement(elemName, component);
403             if (header)
404             {
405                 String headerStyleClass = getHeaderClass(component);
406                 if (facet != null)
407                     renderTableHeaderRow(facesContext, writer, component, facet, headerStyleClass, colspan);
408                 if (hasColumnFacet)
409                     renderColumnHeaderRow(facesContext, writer, component, headerStyleClass);
410             }
411             else
412             {
413                 String footerStyleClass = getFooterClass(component);
414                 if (hasColumnFacet)
415                     renderColumnFooterRow(facesContext, writer, component, footerStyleClass);
416                 if (facet != null)
417                     renderTableFooterRow(facesContext, writer, component, facet, footerStyleClass, colspan);
418             }
419             writer.endElement(elemName);
420         }
421     }
422 
423     /***
424      * @param header
425      * @param uiComponent
426      * @return boolean
427      */
428     protected boolean hasFacet(boolean header, UIComponent uiComponent)
429     {
430         if (uiComponent instanceof UIColumn)
431         {
432             UIColumn uiColumn = (UIColumn) uiComponent;
433             return header ? uiColumn.getHeader() != null : uiColumn.getFooter() != null;
434         }
435         return false;
436     }
437 
438     /***
439      * Calculate the number of columns the specified child component will span
440      * when rendered.
441      * <p>
442      * Normally, this is a fairly simple calculation: a UIColumn component
443      * is rendered as one column, every other child type is not rendered
444      * (ie spans zero columns). However custom subclasses of this renderer may
445      * override this method to handle cases where a single component renders
446      * as multiple columns. 
447      */
448     protected int determineChildColSpan(UIComponent uiComponent)
449     {
450         if (uiComponent instanceof UIColumn)
451         {
452             return 1;
453         }
454         return 0;
455     }
456 
457     /***
458      * Renders the header row of the table being rendered.
459      * @param facesContext the <code>FacesContext</code>.
460      * @param writer the <code>ResponseWriter</code>.
461      * @param component the <code>UIComponent</code> for whom a table is being rendered.
462      * @param headerFacet the facet for the header.
463      * @param headerStyleClass the styleClass of the header.
464      * @param colspan the number of columns the header should span.  Typically, this is
465      * the number of columns in the table.
466      * @throws IOException if an exception occurs.
467      */
468     protected void renderTableHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
469             UIComponent headerFacet, String headerStyleClass, int colspan) throws IOException
470     {
471         renderTableHeaderOrFooterRow(facesContext, writer, component, headerFacet, headerStyleClass, HTML.TH_ELEM,
472                 colspan);
473     }
474 
475     /***
476      * Renders the footer row of the table being rendered.
477      * @param facesContext the <code>FacesContext</code>.
478      * @param writer the <code>ResponseWriter</code>.
479      * @param component the <code>UIComponent</code> for whom a table is being rendered.
480      * @param footerFacet the facet for the footer.
481      * @param footerStyleClass the styleClass of the footer.
482      * @param colspan the number of columns the header should span.  Typically, this is
483      * the number of columns in the table.
484      * @throws IOException if an exception occurs.
485      */
486     protected void renderTableFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
487             UIComponent footerFacet, String footerStyleClass, int colspan) throws IOException
488     {
489         renderTableHeaderOrFooterRow(facesContext, writer, component, footerFacet, footerStyleClass, HTML.TD_ELEM,
490                 colspan);
491     }
492 
493     /***
494      * Renders the header row for the columns, which is a separate row from the header row for the
495      * <code>UIData</code> header facet.
496      * 
497      * @param facesContext the <code>FacesContext</code>.
498      * @param writer the <code>ResponseWriter</code>.
499      * @param component the UIData component for whom a table is being rendered.
500      * @param headerStyleClass the styleClass of the header
501      * @throws IOException if an exception occurs.
502      */
503     protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
504             String headerStyleClass) throws IOException
505     {
506         renderColumnHeaderOrFooterRow(facesContext, writer, component, headerStyleClass, true);
507     }
508 
509     /***
510      * Renders the footer row for the columns, which is a separate row from the footer row for the
511      * <code>UIData</code> footer facet.
512      * @param facesContext the <code>FacesContext</code>.
513      * @param writer the <code>ResponseWriter</code>.
514      * @param component the <code>UIComponent</code> for whom a table is being rendered.
515      * @param footerStyleClass the styleClass of the footerStyleClass
516      * @throws IOException if an exception occurs.
517      */
518     protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
519             String footerStyleClass) throws IOException
520     {
521         renderColumnHeaderOrFooterRow(facesContext, writer, component, footerStyleClass, false);
522     }
523 
524     private void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
525             UIComponent facet, String styleClass, String colElementName, int colspan) throws IOException
526     {
527         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
528         writer.startElement(HTML.TR_ELEM, component);
529         writer.startElement(colElementName, component);
530         if (colElementName.equals(HTML.TH_ELEM))
531         {
532             writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null);
533         }
534         writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
535         if (styleClass != null)
536         {
537             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
538         }
539         if (facet != null)
540         {
541             RendererUtils.renderChild(facesContext, facet);
542         }
543         writer.endElement(colElementName);
544         writer.endElement(HTML.TR_ELEM);
545     }
546 
547     /***
548      * @param component the UIData component for whom a table is being rendered.
549      */
550     private void renderColumnHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer,
551             UIComponent component, String styleClass, boolean header) throws IOException
552     {
553         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
554 
555         writer.startElement(HTML.TR_ELEM, component);
556         for (Iterator it = getChildren(component).iterator(); it.hasNext();)
557         {
558             UIComponent uiComponent = (UIComponent) it.next();
559             if(uiComponent.isRendered())
560             {
561                 renderColumnChildHeaderOrFooterRow(facesContext, writer, uiComponent, styleClass, header);
562             }
563         }
564         writer.endElement(HTML.TR_ELEM);
565     }
566 
567     protected void renderColumnChildHeaderOrFooterRow(FacesContext facesContext,
568         ResponseWriter writer, UIComponent uiComponent, String styleClass, boolean header) throws IOException
569     {
570         if (uiComponent instanceof UIColumn)
571         {
572             if (header)
573             {
574                 renderColumnHeaderCell(facesContext, writer, uiComponent,
575                     ((UIColumn) uiComponent).getHeader(), styleClass, 0);
576             }
577             else
578             {
579                 renderColumnFooterCell(facesContext, writer, uiComponent,
580                     ((UIColumn) uiComponent).getFooter(), styleClass, 0);
581             }
582         }
583     }
584 
585     /***
586      * Renders the header facet for the given <code>UIColumn</code>.
587      * @param facesContext the <code>FacesContext</code>.
588      * @param writer the <code>ResponseWriter</code>.
589      * @param uiColumn the <code>UIColumn</code>.
590      * @param headerStyleClass the styleClass of the header facet.
591      * @param colspan the colspan for the tableData element in which the header facet
592      * will be wrapped.
593      * @throws IOException
594      */
595     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
596         String headerStyleClass, int colspan) throws IOException
597     {
598         renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn.getHeader(), headerStyleClass, colspan);
599     }
600 
601     /***
602      * Renders a TH cell within a TR within a THEAD section. If the specified
603      * UIColumn object does have a header facet, then that facet is rendered
604      * within the cell, otherwise the cell is left blank (though any specified
605      * style class is still applied to empty cells).
606      * 
607      * @param facesContext the <code>FacesContext</code>.
608      * @param writer the <code>ResponseWriter</code>.
609      * @param uiComponent the <code>UIComponent</code> to render the facet for.
610      * @param facet the <code>UIComponent</code> to render as facet.
611      * @param headerStyleClass the styleClass of the header facet.
612      * @param colspan the colspan for the tableData element in which the header facet
613      * will be wrapped.
614      * @throws IOException
615      */
616     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
617             UIComponent facet, String headerStyleClass, int colspan) throws IOException
618     {
619         writer.startElement(HTML.TH_ELEM, uiComponent);
620         if (colspan > 1)
621         {
622             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
623         }
624         if (headerStyleClass != null)
625         {
626             writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass, null);
627         }
628         if (facet != null)
629         {
630             RendererUtils.renderChild(facesContext, facet);
631         }
632         writer.endElement(HTML.TH_ELEM);
633     }
634 
635     /***
636      * Renders the footer facet for the given <code>UIColumn</code>.
637      * @param facesContext the <code>FacesContext</code>.
638      * @param writer the <code>ResponseWriter</code>.
639      * @param uiColumn the <code>UIComponent</code>.
640      * @param footerStyleClass the styleClass of the footer facet.
641      * @param colspan the colspan for the tableData element in which the footer facet
642      * will be wrapped.
643      * @throws IOException
644      */
645     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
646         String footerStyleClass, int colspan) throws IOException
647     {
648       renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn.getFooter(), footerStyleClass, colspan);
649     }
650 
651     /***
652      * Renders the footer facet for the given <code>UIColumn</code>.
653      * @param facesContext the <code>FacesContext</code>.
654      * @param writer the <code>ResponseWriter</code>.
655      * @param uiComponent the <code>UIComponent</code> to render the facet for.
656      * @param facet the <code>UIComponent</code> to render as facet.
657      * @param footerStyleClass the styleClass of the footer facet.
658      * @param colspan the colspan for the tableData element in which the footer facet
659      * will be wrapped.
660      * @throws IOException
661      */
662     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
663         UIComponent facet, String footerStyleClass, int colspan) throws IOException
664     {
665         writer.startElement(HTML.TD_ELEM, uiComponent);
666         if (colspan > 1)
667         {
668             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
669         }
670         if (footerStyleClass != null)
671         {
672             writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null);
673         }
674         if (facet != null)
675         {
676             RendererUtils.renderChild(facesContext, facet);
677         }
678         writer.endElement(HTML.TD_ELEM);
679     }
680 
681     /***
682      * Gets the headerClass attribute of the given <code>UIComponent</code>.
683      * @param component the <code>UIComponent</code>.
684      * @return the headerClass attribute of the given <code>UIComponent</code>.
685      */
686     protected static String getHeaderClass(UIComponent component)
687     {
688         if (component instanceof HtmlDataTable)
689         {
690             return ((HtmlDataTable) component).getHeaderClass();
691         }
692         else
693         {
694             return (String) component.getAttributes().get(JSFAttr.HEADER_CLASS_ATTR);
695         }
696     }
697 
698     /***
699      * Gets the footerClass attribute of the given <code>UIComponent</code>.
700      * @param component the <code>UIComponent</code>.
701      * @return the footerClass attribute of the given <code>UIComponent</code>.
702      */
703     protected static String getFooterClass(UIComponent component)
704     {
705         if (component instanceof HtmlDataTable)
706         {
707             return ((HtmlDataTable) component).getFooterClass();
708         }
709         else
710         {
711             return (String) component.getAttributes().get(JSFAttr.FOOTER_CLASS_ATTR);
712         }
713     }
714 
715     //-------------------------------------------------------------
716     // Helper class Styles
717     //-------------------------------------------------------------
718     private static class StyleIterator implements Iterator
719     {
720         //~ Instance fields
721         // ------------------------------------------------------------------------
722 
723         private String[] _style;
724         private int _idx = 0;
725 
726         //~ Constructors
727         // ---------------------------------------------------------------------------
728         StyleIterator(String styles)
729         {
730             _style = (styles == null) ? ArrayUtils.EMPTY_STRING_ARRAY : StringUtils.trim(StringUtils
731                     .splitShortString(styles, ','));
732         }
733         
734         /***
735          * @see java.util.Iterator#hasNext()
736          */
737         public boolean hasNext()
738         {
739             return _style.length > 0;
740         }
741         
742         /***
743          * @see java.util.Iterator#next()
744          */
745         public Object next()
746         {
747             if(hasNext())
748             {
749                 return _style[_idx++ % _style.length];
750             }
751             throw new NoSuchElementException("no style defined");
752         }
753 
754         /***
755          * @see java.util.Iterator#remove()
756          */
757         public void remove()
758         {
759             throw new UnsupportedOperationException("remove is not supported");
760         }
761         
762         public void reset()
763         {
764             _idx = 0;
765         }
766     }
767 
768     public void decode(FacesContext context, UIComponent component)
769     {
770         super.decode(context, component);
771     }
772 
773 }