View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.logging.Logger;
25  
26  import javax.faces.component.UIColumn;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIData;
29  import javax.faces.component.behavior.ClientBehavior;
30  import javax.faces.component.behavior.ClientBehaviorHolder;
31  import javax.faces.component.html.HtmlColumn;
32  import javax.faces.component.html.HtmlDataTable;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  
36  import org.apache.myfaces.shared.renderkit.JSFAttr;
37  import org.apache.myfaces.shared.renderkit.RendererUtils;
38  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
39  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
40  import org.apache.myfaces.shared.util.ArrayUtils;
41  import org.apache.myfaces.shared.util.StringUtils;
42  
43  /**
44   * Common methods for renderers for components that subclass the standard
45   * JSF HtmlDataTable component.
46   * 
47   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
48   * @version $Revision: 1533230 $ $Date: 2013-10-17 14:51:28 -0500 (Thu, 17 Oct 2013) $
49   */
50  public class HtmlTableRendererBase extends HtmlRenderer
51  {
52      /** Header facet name. */
53      protected static final String HEADER_FACET_NAME = "header";
54  
55      /** Footer facet name. */
56      protected static final String FOOTER_FACET_NAME = "footer";
57  
58      protected static final String CAPTION_FACET_NAME = "caption";
59      
60      /** The logger. */
61      //private static final Log log = LogFactory.getLog(HtmlTableRendererBase.class);
62      private static final Logger log = Logger.getLogger(HtmlTableRendererBase.class.getName());
63      
64      private static final Integer[] ZERO_INT_ARRAY = new Integer[]{0};
65  
66      /**
67       * @param component dataTable
68       * @return number of layout columns
69       */
70      protected int getNewspaperColumns(UIComponent component)
71      {
72          return 1;
73      }
74  
75      /**
76       * @param component dataTable
77       * @return component to display between layout columns
78       */
79      protected UIComponent getNewspaperTableSpacer(UIComponent component)
80      {
81          return null;
82      }
83  
84      /**
85       * @param component dataTable
86       * @return whether dataTable has component to display between layout columns
87       */
88      protected boolean hasNewspaperTableSpacer(UIComponent component)
89      {
90          return false;
91      }
92  
93      /**
94       * @param component dataTable
95       * @return whether dataTable has newspaper columns layed out horizontally
96       */
97      protected boolean isNewspaperHorizontalOrientation(UIComponent component)
98      {
99          return false;
100     }
101 
102     /**
103      * @see javax.faces.render.Renderer#getRendersChildren()
104      */
105     public boolean getRendersChildren()
106     {
107         return true;
108     }
109 
110     /**
111      * Render the necessary bits that come before any actual <i>rows</i> in the table.
112      * 
113      * @see javax.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
114      */
115     public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
116     {
117         RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
118 
119         Map<String, List<ClientBehavior>> behaviors = null;
120         if (uiComponent instanceof ClientBehaviorHolder)
121         {
122             behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
123             if (!behaviors.isEmpty())
124             {
125                 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
126             }
127         }
128         
129         beforeTable(facesContext, (UIData) uiComponent);
130 
131         startTable(facesContext, uiComponent);
132     }
133 
134     /**
135      * actually render the start of the table
136      */
137     protected void startTable(FacesContext facesContext, UIComponent uiComponent) throws IOException
138     {
139         ResponseWriter writer = facesContext.getResponseWriter();
140         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
141         writer.startElement(HTML.TABLE_ELEM, uiComponent);
142         
143         Map<String, List<ClientBehavior>> behaviors = null;
144         if (uiComponent instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
145                 facesContext.getExternalContext()))
146         {
147             behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
148             if (!behaviors.isEmpty())
149             {
150                 HtmlRendererUtils.writeIdAndName(writer, uiComponent, facesContext);
151             }
152             else
153             {
154                 HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
155             }
156             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
157             {
158                 CommonPropertyUtils.renderEventProperties(writer, 
159                         CommonPropertyUtils.getCommonPropertiesMarked(uiComponent), uiComponent);
160             }
161             else
162             {
163                 if (isCommonEventsOptimizationEnabled(facesContext))
164                 {
165                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
166                            CommonPropertyUtils.getCommonPropertiesMarked(uiComponent),
167                            CommonEventUtils.getCommonEventsMarked(uiComponent), uiComponent, behaviors);
168                 }
169                 else
170                 {
171                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
172                 }
173             }
174             if (isCommonPropertiesOptimizationEnabled(facesContext))
175             {
176                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_ATTRIBUTES);
177                 CommonPropertyUtils.renderCommonPassthroughPropertiesWithoutEvents(writer, 
178                         CommonPropertyUtils.getCommonPropertiesMarked(uiComponent), uiComponent);
179             }
180             else
181             {
182                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
183                         HTML.TABLE_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
184             }
185         }
186         else
187         {
188             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
189             if (isCommonPropertiesOptimizationEnabled(facesContext))
190             {
191                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_ATTRIBUTES);
192                 CommonPropertyUtils.renderCommonPassthroughProperties(writer, 
193                         CommonPropertyUtils.getCommonPropertiesMarked(uiComponent), uiComponent);
194             }
195             else
196             {
197                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
198                         HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
199             }
200         }
201     }
202 
203     /**
204      * Render the TBODY section of the html table. See also method encodeInnerHtml.
205      * 
206      * @see javax.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
207      */
208     public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
209     {
210         RendererUtils.checkParamValidity(facesContext, component, UIData.class);
211 
212         beforeBody(facesContext, (UIData) component);
213 
214         encodeInnerHtml(facesContext, component);
215 
216         afterBody(facesContext, (UIData) component);
217     }
218 
219     /**
220      * Renders the caption facet.
221      * @param facesContext the <code>FacesContext</code>.
222      * @param writer the <code>ResponseWriter</code>.
223      * @param component the parent <code>UIComponent</code> containing the facets.
224      * @throws IOException if an exception occurs.
225      */
226     protected void renderCaptionFacet(FacesContext facesContext, ResponseWriter writer, UIComponent component)
227             throws IOException
228     {
229         HtmlRendererUtils.renderTableCaption(facesContext, writer, component);
230     }  
231     
232     /**
233      * Renders the colgroups facet.
234      * @param facesContext the <code>FacesContext</code>.
235      * @param writer the <code>ResponseWriter</code>.
236      * @param component the parent <code>UIComponent</code> containing the facets.
237      * @throws IOException if an exception occurs.
238      * @since 2.0
239      */
240     protected void renderColgroupsFacet(FacesContext facesContext, ResponseWriter writer, UIComponent component)
241             throws IOException
242     {
243         UIComponent colgroupsFacet = component.getFacet("colgroups");
244         if (colgroupsFacet == null)
245         {
246             // no facet to be rendered
247             return;
248         }
249         // render the facet
250         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
251         //RendererUtils.renderChild(facesContext, colgroupsFacet);
252         colgroupsFacet.encodeAll(facesContext);
253     } 
254     
255     /**
256      * Gets styles for the specified component.
257      */
258     protected static Styles getStyles(UIData uiData)
259     {
260         String rowClasses;
261         String columnClasses;
262         if(uiData instanceof HtmlDataTable) 
263         {
264             rowClasses = ((HtmlDataTable)uiData).getRowClasses();
265             columnClasses = ((HtmlDataTable)uiData).getColumnClasses();
266         }
267         else
268         {
269             rowClasses = (String)uiData.getAttributes().get(JSFAttr.ROW_CLASSES_ATTR);
270             columnClasses = (String)uiData.getAttributes().get(JSFAttr.COLUMN_CLASSES_ATTR);
271         }
272         return new Styles(rowClasses, columnClasses);
273     }
274 
275     /**
276      * Class manages the styles from String lists.
277      */
278     protected static class Styles
279     {
280 
281         private String[] _columnStyle;
282         private String[] _rowStyle;
283 
284         Styles(String rowStyles, String columnStyles)
285         {
286             _rowStyle = (rowStyles == null)
287                 ? ArrayUtils.EMPTY_STRING_ARRAY
288                 : StringUtils.trim(
289                     StringUtils.splitShortString(rowStyles, ','));
290             _columnStyle = (columnStyles == null)
291                 ? ArrayUtils.EMPTY_STRING_ARRAY
292                 : StringUtils.trim(
293                     StringUtils.splitShortString(columnStyles, ','));
294         }
295 
296         public String getRowStyle(int idx)
297         {
298             if(!hasRowStyle())
299             {
300                 return null;
301             }
302             return _rowStyle[idx % _rowStyle.length];
303         }
304 
305         public String getColumnStyle(int idx)
306         {
307             if(!hasColumnStyle())
308             {
309                 return null;
310             }
311             //return _columnStyle[idx % _columnStyle.length];
312             if (idx < _columnStyle.length)
313             {
314                 return _columnStyle[idx];
315             }
316             return null;   
317         }
318 
319         public boolean hasRowStyle()
320         {
321             return _rowStyle.length > 0;
322         }
323 
324         public boolean hasColumnStyle()
325         {
326             return _columnStyle.length > 0;
327         }
328     }
329 
330     private Integer[] getBodyRows(FacesContext facesContext, UIComponent component)
331     {
332         Integer[] bodyrows = null;
333         String bodyrowsAttr = (String) component.getAttributes().get(JSFAttr.BODYROWS_ATTR);
334         if(bodyrowsAttr != null && !"".equals(bodyrowsAttr)) 
335         {   
336             String[] bodyrowsString = StringUtils.trim(StringUtils.splitShortString(bodyrowsAttr, ','));
337             // parsing with no exception handling, because of JSF-spec: 
338             // "If present, this must be a comma separated list of integers."
339             bodyrows = new Integer[bodyrowsString.length];
340             for(int i = 0; i < bodyrowsString.length; i++) 
341             {
342                 bodyrows[i] = new Integer(bodyrowsString[i]);
343             }
344             
345         }
346         else
347         {
348             bodyrows = ZERO_INT_ARRAY;
349         }
350         return bodyrows;
351     }
352 
353     /**
354      * Renders everything inside the TBODY tag by iterating over the row objects
355      * between offsets first and first+rows and applying the UIColumn components
356      * to those objects. 
357      * <p>
358      * This method is separated from the encodeChildren so that it can be overridden by
359      * subclasses. One class that uses this functionality is autoUpdateDataTable.
360      */
361      public void encodeInnerHtml(FacesContext facesContext, UIComponent component)throws IOException
362      {
363         UIData uiData = (UIData) component;
364         ResponseWriter writer = facesContext.getResponseWriter();
365 
366         int rowCount = uiData.getRowCount();
367 
368         int newspaperColumns = getNewspaperColumns(component);
369 
370         if (rowCount == -1 && newspaperColumns == 1)
371         {
372             encodeInnerHtmlUnknownRowCount(facesContext, component);
373             return;
374         }
375         
376         if (rowCount == 0)
377         {
378             //nothing to render, to get valid xhtml we render an empty dummy row
379             writer.startElement(HTML.TBODY_ELEM, uiData);
380             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null);
381             writer.startElement(HTML.TR_ELEM, uiData);
382             writer.startElement(HTML.TD_ELEM, uiData);
383             writer.endElement(HTML.TD_ELEM);
384             writer.endElement(HTML.TR_ELEM);
385             writer.endElement(HTML.TBODY_ELEM);
386             return;
387         }
388 
389         // begin the table
390         // get the CSS styles
391         Styles styles = getStyles(uiData);
392 
393         int first = uiData.getFirst();
394         int rows = uiData.getRows();
395         int last;
396 
397         if (rows <= 0)
398         {
399            last = rowCount;
400         }
401         else
402         {
403            last = first + rows;
404            if (last > rowCount)
405            {
406                last = rowCount;
407            }
408         }
409 
410         int newspaperRows;
411         if((last - first) % newspaperColumns == 0)
412         {
413             newspaperRows = (last - first) / newspaperColumns;
414         }
415         else
416         {
417             newspaperRows = ((last - first) / newspaperColumns) + 1;
418         }
419         boolean newspaperHorizontalOrientation = isNewspaperHorizontalOrientation(component);
420         
421         // get the row indizes for which a new TBODY element should be created
422         Integer[] bodyrows = getBodyRows(facesContext, component);
423         int bodyrowsCount = 0;
424 
425         // walk through the newspaper rows
426         for(int nr = 0; nr < newspaperRows; nr++)
427         {
428             boolean rowStartRendered = false;
429             // walk through the newspaper columns
430             for(int nc = 0; nc < newspaperColumns; nc++)
431             {
432 
433                 // the current row in the 'real' table
434                 int currentRow;
435                 if (newspaperHorizontalOrientation)
436                 {
437                     currentRow = nr * newspaperColumns + nc + first;
438                 }
439                 else
440                 {
441                     currentRow = nc * newspaperRows + nr + first;
442                 }
443                 
444                 // if this row is not to be rendered
445                 if(currentRow >= last)
446                 {
447                     continue;
448                 }
449 
450                 // bail if any row does not exist
451                 uiData.setRowIndex(currentRow);
452                 if(!uiData.isRowAvailable())
453                 {
454                     log.severe("Row is not available. Rowindex = " + currentRow);
455                     break;
456                 }
457     
458                 if (nc == 0)
459                 {
460                     // first column in table, start new row
461                     beforeRow(facesContext, uiData);
462 
463                     // is the current row listed in the bodyrows attribute
464                     if(ArrayUtils.contains(bodyrows, currentRow))  
465                     {
466                         // close any preopened TBODY element first
467                         if(bodyrowsCount != 0) 
468                         {
469                             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
470                             writer.endElement(HTML.TBODY_ELEM);
471                         }
472                         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
473                         writer.startElement(HTML.TBODY_ELEM, uiData); 
474                         // Do not attach bodyrowsCount to the first TBODY element, because of backward compatibility
475                         writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + 
476                             (bodyrowsCount == 0 ? "" : bodyrowsCount), null);
477                         bodyrowsCount++;
478                     }
479                     
480                     HtmlRendererUtils.writePrettyLineSeparator(facesContext);
481                     renderRowStart(facesContext, writer, uiData, styles, nr);
482                     rowStartRendered = true;
483                 }
484 
485                 List children = null;
486                 int columnStyleIndex = 0;
487                 for (int j = 0, size = getChildCount(component); j < size; j++)
488                 {
489                     if (children == null)
490                     {
491                         children = getChildren(component);
492                     }
493                     UIComponent child = (UIComponent) children.get(j);
494                     if (child.isRendered())
495                     {
496                         boolean columnRendering = child instanceof UIColumn;
497                         
498                         if (columnRendering)
499                         {
500                             beforeColumn(facesContext, uiData, columnStyleIndex);
501                         }
502                            
503                         encodeColumnChild(facesContext, writer, uiData, child, 
504                                 styles, nc * uiData.getChildCount() + columnStyleIndex);
505                        
506                         if (columnRendering)
507                         {
508                             afterColumn(facesContext, uiData, columnStyleIndex);
509                         }
510                         columnStyleIndex = columnStyleIndex + 
511                             getColumnCountForComponent(facesContext, uiData, child);
512                     }
513                 }
514 
515                 if (hasNewspaperTableSpacer(uiData))
516                 {
517                     // draw the spacer facet
518                     if(nc < newspaperColumns - 1)
519                     {
520                         renderSpacerCell(facesContext, writer, uiData);
521                     }
522                 }
523             }
524             if (rowStartRendered)
525             {
526                 renderRowEnd(facesContext, writer, uiData);
527                 afterRow(facesContext, uiData);
528             }
529         }
530         
531         if(bodyrowsCount != 0)
532         {
533             // close the last TBODY element
534             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
535             writer.endElement(HTML.TBODY_ELEM);
536         }
537     }
538      
539     private void encodeInnerHtmlUnknownRowCount(FacesContext facesContext, UIComponent component)throws IOException
540     {
541         UIData uiData = (UIData) component;
542         ResponseWriter writer = facesContext.getResponseWriter();
543 
544         Styles styles = getStyles(uiData);
545         
546         Integer[] bodyrows = getBodyRows(facesContext, component);
547         int bodyrowsCount = 0;
548         
549         int first = uiData.getFirst();
550         int rows = uiData.getRows();
551         int currentRow = first;
552         boolean isRowRendered = false;
553         
554         while(true)
555         {
556             uiData.setRowIndex(currentRow);
557             if (!uiData.isRowAvailable())
558             {
559                 break;
560             }
561             
562             isRowRendered = true;
563             
564             // first column in table, start new row
565             beforeRow(facesContext, uiData);
566 
567             // is the current row listed in the bodyrows attribute
568             if(ArrayUtils.contains(bodyrows, currentRow))  
569             {
570                 // close any preopened TBODY element first
571                 if(bodyrowsCount != 0) 
572                 {
573                     HtmlRendererUtils.writePrettyLineSeparator(facesContext);
574                     writer.endElement(HTML.TBODY_ELEM);
575                 }
576                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
577                 writer.startElement(HTML.TBODY_ELEM, uiData); 
578                 // Do not attach bodyrowsCount to the first TBODY element, because of backward compatibility
579                 writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + 
580                     (bodyrowsCount == 0 ? "" : bodyrowsCount), null);
581                 bodyrowsCount++;
582             }
583             
584             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
585             renderRowStart(facesContext, writer, uiData, styles, currentRow);
586             
587             List<UIComponent> children = null;
588             int columnStyleIndex = 0;
589             for (int j = 0, size = getChildCount(component); j < size; j++)
590             {
591                 if (children == null)
592                 {
593                     children = getChildren(component);
594                 }
595                 UIComponent child = (UIComponent) children.get(j);
596                 if (child.isRendered())
597                 {
598                     boolean columnRendering = child instanceof UIColumn;
599                     
600                     if (columnRendering)
601                     {
602                         beforeColumn(facesContext, uiData, columnStyleIndex);
603                     }
604                        
605                     encodeColumnChild(facesContext, writer, uiData, child, 
606                             styles, columnStyleIndex);
607                    
608                     if (columnRendering)
609                     {
610                         afterColumn(facesContext, uiData, columnStyleIndex);
611                     }
612                     columnStyleIndex = columnStyleIndex + 
613                             getColumnCountForComponent(facesContext, uiData, child);
614                 }
615             }
616 
617             renderRowEnd(facesContext, writer, uiData);
618             afterRow(facesContext, uiData);
619             
620             currentRow++;
621 
622             if (rows > 0 && currentRow-first > rows )
623             {
624                 break;
625             }
626         }
627         
628         if (!isRowRendered)
629         {
630             //nothing to render, to get valid xhtml we render an empty dummy row
631             writer.startElement(HTML.TBODY_ELEM, uiData);
632             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null);
633             writer.startElement(HTML.TR_ELEM, uiData);
634             writer.startElement(HTML.TD_ELEM, uiData);
635             writer.endElement(HTML.TD_ELEM);
636             writer.endElement(HTML.TR_ELEM);
637             writer.endElement(HTML.TBODY_ELEM);
638             return;
639         }
640 
641         if(bodyrowsCount != 0)
642         {
643             // close the last TBODY element
644             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
645             writer.endElement(HTML.TBODY_ELEM);
646         }
647     }
648 
649     protected void encodeColumnChild(FacesContext facesContext, ResponseWriter writer,
650         UIData uiData, UIComponent component, Styles styles, int columnStyleIndex) throws IOException
651     {
652         if (component instanceof UIColumn)
653         {            
654             renderColumnBody(facesContext, writer, uiData, component, styles, columnStyleIndex);            
655         }
656     }
657 
658     /**
659      * Renders the body of a given <code>UIColumn</code> (everything but
660      * the header and footer facets). This emits a TD cell, whose contents
661      * are the result of calling encodeBegin, encodeChildren and
662      * encodeEnd methods on the component (or its associated renderer).
663      * 
664      * @param facesContext the <code>FacesContext</code>.
665      * @param writer the <code>ResponseWriter</code>.
666      * @param uiData the <code>UIData</code> being rendered.
667      * @param component the <code>UIComponent</code> to render.
668      * @throws IOException if an exception occurs.
669      */
670     protected void renderColumnBody(
671             FacesContext facesContext,
672             ResponseWriter writer,
673             UIData uiData,
674             UIComponent component,
675             Styles styles, int columnStyleIndex) throws IOException
676     {
677         // Get the rowHeader attribute from the attribute map, because of MYFACES-1790
678         Object rowHeaderAttr = component.getAttributes().get(JSFAttr.ROW_HEADER_ATTR);
679         boolean rowHeader = rowHeaderAttr != null && ((Boolean) rowHeaderAttr);
680         
681         if(rowHeader) 
682         {
683             writer.startElement(HTML.TH_ELEM, uiData);   
684             writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_ROW_VALUE, null);
685         }
686         else 
687         {
688             writer.startElement(HTML.TD_ELEM, uiData);
689         }
690         if (styles.hasColumnStyle())
691         {
692             writer.writeAttribute(HTML.CLASS_ATTR, styles.getColumnStyle(columnStyleIndex), null);
693         }
694         //RendererUtils.renderChild(facesContext, component);
695         component.encodeAll(facesContext);
696         if(rowHeader) 
697         {
698             writer.endElement(HTML.TH_ELEM);   
699         }
700         else 
701         {
702             writer.endElement(HTML.TD_ELEM);
703         }
704     }
705 
706     /**
707      * Renders the start of a new row of body content.
708      * @param facesContext the <code>FacesContext</code>.
709      * @param writer the <code>ResponseWriter</code>.
710      * @param uiData the <code>UIData</code> being rendered.
711      * @throws IOException if an exceptoin occurs.
712      */
713     protected void renderRowStart(
714         FacesContext facesContext,
715         ResponseWriter writer,
716         UIData uiData,
717         Styles styles, int rowStyleIndex) throws IOException
718     {
719         writer.startElement(HTML.TR_ELEM, uiData);
720         
721         renderRowStyle(facesContext, writer, uiData, styles, rowStyleIndex);
722         
723         Object rowId = uiData.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.ROW_ID);
724 
725         if (rowId != null)
726         {
727             writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
728         }
729     }
730 
731     protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, 
732             UIData uiData, Styles styles, int rowStyleIndex) throws IOException
733     {
734         if(styles.hasRowStyle())
735         {
736             String rowStyle = styles.getRowStyle(rowStyleIndex);
737             writer.writeAttribute(HTML.CLASS_ATTR, rowStyle, null);
738         }
739     }
740 
741     /**
742      * Renders the end of a row of body content.
743      * @param facesContext the <code>FacesContext</code>.
744      * @param writer the <code>ResponseWriter</code>.
745      * @param uiData the <code>UIData</code> being rendered.
746      * @throws IOException if an exceptoin occurs.
747      */
748     protected void renderRowEnd(
749         FacesContext facesContext,
750         ResponseWriter writer,
751         UIData uiData) throws IOException
752     {
753         writer.endElement(HTML.TR_ELEM);
754     }
755 
756     /**
757      * Perform any operations necessary immediately before the TABLE start tag
758      * is output.
759      *
760      * @param facesContext the <code>FacesContext</code>.
761      * @param uiData the <code>UIData</code> being rendered.
762      */
763     protected void beforeTable(FacesContext facesContext, UIData uiData) throws IOException
764     {
765     }
766 
767     /**
768      * Perform any operations necessary after TABLE start tag is output
769      * but before the TBODY start tag.
770      * <p>
771      * This method generates the THEAD/TFOOT sections of a table if there
772      * are any header or footer facets defined on the table or on any child
773      * UIColumn component.
774      *
775      * @param facesContext the <code>FacesContext</code>.
776      * @param uiData the <code>UIData</code> being rendered.
777      */
778     protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException
779     {
780         ResponseWriter writer = facesContext.getResponseWriter();
781 
782         renderCaptionFacet(facesContext, writer, uiData);
783         renderColgroupsFacet(facesContext, writer, uiData);
784         renderFacet(facesContext, writer, uiData, true);
785         renderFacet(facesContext, writer, uiData, false);
786     }
787 
788     /**
789      * Perform any operations necessary immediately before each TR start tag
790      * is output.
791      *
792      * @param facesContext the <code>FacesContext</code>.
793      * @param uiData the <code>UIData</code> being rendered.
794      */
795     protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException
796     {
797     }
798 
799     /**
800      * Perform any operations necessary immediately after each TR end tag
801      * is output.
802      *
803      * @param facesContext the <code>FacesContext</code>.
804      * @param uiData the <code>UIData</code> being rendered.
805      */
806     protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException
807     {
808     }
809     /**
810      * Perform any operations necessary immediately before each column child is rendered
811      *
812      * @param facesContext the <code>FacesContext</code>.
813      * @param uiData the <code>UIData</code> being rendered.
814      * @param columnIndex the index of the currenly rendered column
815      */
816     protected void beforeColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException
817     {        
818     }
819     /**
820      * Perform any operations necessary immediately after each column child is rendered
821      *
822      * @param facesContext the <code>FacesContext</code>.
823      * @param uiData the <code>UIData</code> being rendered.
824      * @param columnIndex the index of the currenly rendered column
825      */
826     protected void afterColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException
827     {        
828     }
829     /**
830      * Indicates the number of columns the component represents. By default each UIColumn instance
831      * is 1 column
832      * @param facesContext
833      * @param uiData
834      * @param child
835      * @return 
836      */
837     protected int getColumnCountForComponent(FacesContext facesContext, UIData uiData, UIComponent child)
838     {
839         if (child instanceof UIColumn)
840         {
841             return 1;
842         }
843         return 0;
844     }
845     /**
846      *Perform any operations necessary immediately before each column child's header or footer is rendered
847      *
848      * @param facesContext the <code>FacesContext</code>.
849      * @param uiData the <code>UIData</code> being rendered.
850      * @param header true if the header of the column child is rendered
851      * @param columnIndex the index of the currenly rendered column
852      */
853     protected void beforeColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header,
854             int columnIndex) throws IOException
855     {         
856     }
857     /**
858      * Perform any operations necessary immediately after each column child's header of footer is rendered
859      *
860      * @param facesContext the <code>FacesContext</code>.
861      * @param uiData the <code>UIData</code> being rendered.
862      * @param header true if the header of the column child is rendered
863      * @param columnIndex the index of the currenly rendered column
864      */
865     protected void afterColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header,
866             int columnIndex) throws IOException
867     {         
868     }
869 
870     /**
871      * Perform any operations necessary in the TBODY start tag.
872      *
873      * @param facesContext the <code>FacesContext</code>.
874      * @param uiData the <code>UIData</code> being rendered.
875      */
876     protected void inBodyStart(FacesContext facesContext, UIData uiData) throws IOException
877     {
878     }
879 
880     /**
881      * Perform any operations necessary immediately after the TBODY end tag
882      * is output.
883      *
884      * @param facesContext the <code>FacesContext</code>.
885      * @param uiData the <code>UIData</code> being rendered.
886      */
887     protected void afterBody(FacesContext facesContext, UIData uiData) throws IOException
888     {
889     }
890 
891     /**
892      * Perform any operations necessary immediately after the TABLE end tag
893      * is output.
894      *
895      * @param facesContext the <code>FacesContext</code>.
896      * @param uiData the <code>UIData</code> being rendered.
897      */
898     protected void afterTable(FacesContext facesContext, UIData uiData) throws IOException
899     {
900     }
901 
902     /**
903      * @see javax.faces.render.Renderer#encodeEnd(FacesContext, UIComponent)
904      */
905     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
906     {
907         RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
908 
909         endTable(facesContext, uiComponent);
910 
911         afterTable(facesContext, (UIData) uiComponent);
912     }
913 
914     /**
915      * actually render the end of the table
916      */
917     protected void endTable(FacesContext facesContext, UIComponent uiComponent) throws IOException
918     {
919         ResponseWriter writer = facesContext.getResponseWriter();
920         writer.endElement(HTML.TABLE_ELEM);
921         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
922     }
923 
924     /**
925      * Renders either the header or the footer facets for the UIData component
926      * and all the child UIColumn components, as a THEAD or TFOOT element
927      * containing TR (row) elements.
928      * <p>
929      * If there is a header or footer attached to the UIData then that is
930      * rendered as a TR element whose COLSPAN is the sum of all rendered
931      * columns in the table. This allows that header/footer to take up the
932      * entire width of the table.
933      * <p>
934      * If any child column has a header or footer then a TR is rendered
935      * with a TH cell for each column child. 
936      * 
937      * @param facesContext the <code>FacesContext</code>.
938      * @param writer the <code>ResponseWriter</code>.
939      * @param component the UIData component
940      * @param header whether this is the header facet (if not, then the footer facet).
941      * @throws IOException if an exception occurs.
942      */
943     protected void renderFacet(FacesContext facesContext, ResponseWriter writer,
944             UIComponent component, boolean header)
945             throws IOException
946     {
947         int colspan = 0;
948         boolean hasColumnFacet = false;
949         int childCount = component.getChildCount();
950         for (int i = 0; i < childCount; i++)
951         {
952             UIComponent uiComponent = component.getChildren().get(i);
953             if(uiComponent.isRendered())
954             {
955                 // a UIColumn has a span of 1, anything else has a span of 0
956                 colspan += determineChildColSpan(uiComponent);
957 
958                 // hasColumnFacet is true if *any* child column has a facet of
959                 // the specified type.
960                 if (!hasColumnFacet)
961                 {
962                     hasColumnFacet = hasFacet(header, uiComponent);
963                 }
964             }
965         }
966 
967         
968         UIComponent facet = null;
969         if (component.getFacetCount() > 0)
970         {
971             facet = header ? (UIComponent) component.getFacets().get(HEADER_FACET_NAME) 
972                     : (UIComponent) component.getFacets().get(FOOTER_FACET_NAME);
973         }
974         if (facet != null || hasColumnFacet)
975         {
976             // Header or Footer present on either the UIData or a column, so we
977             // definitely need to render the THEAD or TFOOT section.
978             String elemName = determineHeaderFooterTag(facesContext, component, header);
979 
980             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
981             if (elemName != null)
982             {
983                 writer.startElement(elemName, component);
984             }
985             if (header)
986             {
987                 String headerStyleClass = getHeaderClass(component);
988                 if (facet != null)
989                 {
990                     renderTableHeaderRow(facesContext, writer, component, facet, headerStyleClass, colspan);
991                 }
992                 if (hasColumnFacet)
993                 {
994                     renderColumnHeaderRow(facesContext, writer, component, headerStyleClass);
995                 }
996             }
997             else
998             {
999                 String footerStyleClass = getFooterClass(component);
1000                 if (hasColumnFacet)
1001                 {
1002                     renderColumnFooterRow(facesContext, writer, component, footerStyleClass);
1003                 }
1004                 if (facet != null)
1005                 {
1006                     renderTableFooterRow(facesContext, writer, component, facet, footerStyleClass, colspan);
1007                 }
1008             }
1009             if (elemName != null)
1010             {
1011                 writer.endElement(elemName);
1012             }
1013         }
1014     }
1015 
1016     protected String determineHeaderFooterTag(FacesContext facesContext, UIComponent component, boolean header)
1017     {
1018         return header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM;
1019     }
1020 
1021     /**
1022      * @param header
1023      * @param uiComponent
1024      * @return boolean
1025      */
1026     protected boolean hasFacet(boolean header, UIComponent uiComponent)
1027     {
1028         if (uiComponent instanceof UIColumn)
1029         {
1030             UIColumn uiColumn = (UIColumn) uiComponent;
1031             return header ? uiColumn.getHeader() != null : uiColumn.getFooter() != null;
1032         }
1033         return false;
1034     }
1035 
1036     /**
1037      * Calculate the number of columns the specified child component will span
1038      * when rendered.
1039      * <p>
1040      * Normally, this is a fairly simple calculation: a UIColumn component
1041      * is rendered as one column, every other child type is not rendered
1042      * (ie spans zero columns). However custom subclasses of this renderer may
1043      * override this method to handle cases where a single component renders
1044      * as multiple columns. 
1045      */
1046     protected int determineChildColSpan(UIComponent uiComponent)
1047     {
1048         if (uiComponent instanceof UIColumn)
1049         {
1050             return 1;
1051         }
1052         return 0;
1053     }
1054 
1055     /**
1056      * Renders the header row of the table being rendered.
1057      * @param facesContext the <code>FacesContext</code>.
1058      * @param writer the <code>ResponseWriter</code>.
1059      * @param component the <code>UIComponent</code> for whom a table is being rendered.
1060      * @param headerFacet the facet for the header.
1061      * @param headerStyleClass the styleClass of the header.
1062      * @param colspan the number of columns the header should span.  Typically, this is
1063      * the number of columns in the table.
1064      * @throws IOException if an exception occurs.
1065      */
1066     protected void renderTableHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
1067             UIComponent headerFacet, String headerStyleClass, int colspan) throws IOException
1068     {
1069         renderTableHeaderOrFooterRow(facesContext, writer, component, headerFacet, headerStyleClass, 
1070                 determineHeaderCellTag(facesContext, component),
1071                 colspan, true);
1072     }
1073 
1074     /**
1075      * Renders the footer row of the table being rendered.
1076      * @param facesContext the <code>FacesContext</code>.
1077      * @param writer the <code>ResponseWriter</code>.
1078      * @param component the <code>UIComponent</code> for whom a table is being rendered.
1079      * @param footerFacet the facet for the footer.
1080      * @param footerStyleClass the styleClass of the footer.
1081      * @param colspan the number of columns the header should span.  Typically, this is
1082      * the number of columns in the table.
1083      * @throws IOException if an exception occurs.
1084      */
1085     protected void renderTableFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
1086             UIComponent footerFacet, String footerStyleClass, int colspan) throws IOException
1087     {
1088         renderTableHeaderOrFooterRow(facesContext, writer, component, footerFacet, footerStyleClass, HTML.TD_ELEM,
1089                 colspan, false);
1090     }
1091 
1092     /**
1093      * Renders the header row for the columns, which is a separate row from the header row for the
1094      * <code>UIData</code> header facet.
1095      * 
1096      * @param facesContext the <code>FacesContext</code>.
1097      * @param writer the <code>ResponseWriter</code>.
1098      * @param component the UIData component for whom a table is being rendered.
1099      * @param headerStyleClass the styleClass of the header
1100      * @throws IOException if an exception occurs.
1101      */
1102     protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
1103             String headerStyleClass) throws IOException
1104     {
1105         renderColumnHeaderOrFooterRow(facesContext, writer, component, headerStyleClass, true);
1106     }
1107 
1108     /**
1109      * Renders the footer row for the columns, which is a separate row from the footer row for the
1110      * <code>UIData</code> footer facet.
1111      * @param facesContext the <code>FacesContext</code>.
1112      * @param writer the <code>ResponseWriter</code>.
1113      * @param component the <code>UIComponent</code> for whom a table is being rendered.
1114      * @param footerStyleClass the styleClass of the footerStyleClass
1115      * @throws IOException if an exception occurs.
1116      */
1117     protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
1118             String footerStyleClass) throws IOException
1119     {
1120         renderColumnHeaderOrFooterRow(facesContext, writer, component, footerStyleClass, false);
1121     }
1122 
1123     protected void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, 
1124             UIComponent component,
1125             UIComponent facet, String styleClass, String colElementName, int colspan, boolean isHeader)
1126             throws IOException
1127     {
1128         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
1129         writer.startElement(HTML.TR_ELEM, component);
1130         writer.startElement(colElementName, component);
1131         if (colElementName.equals(determineHeaderCellTag(facesContext, component)) && isHeader)
1132         {
1133             writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null);
1134         }
1135 
1136         // span all the table's columns
1137         int newsPaperColumns = getNewspaperColumns(component);
1138         int totalColumns = colspan * newsPaperColumns;
1139         if(hasNewspaperTableSpacer(component))
1140         {
1141             totalColumns = totalColumns + newsPaperColumns - 1;
1142         }
1143         // Only render colspan if is > 0
1144         if (totalColumns > 0)
1145         {
1146             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(totalColumns), null);
1147         }
1148         if (styleClass != null)
1149         {
1150             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
1151         }
1152         if (facet != null)
1153         {
1154             //RendererUtils.renderChild(facesContext, facet);
1155             facet.encodeAll(facesContext);
1156         }
1157         writer.endElement(colElementName);
1158         writer.endElement(HTML.TR_ELEM);
1159     }
1160 
1161     /**
1162      * @param component the UIData component for whom a table is being rendered.
1163      */
1164     private void renderColumnHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer,
1165             UIComponent component, String styleClass, boolean header) throws IOException
1166     {
1167         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
1168 
1169         writer.startElement(HTML.TR_ELEM, component);
1170         int columnIndex = 0;
1171         int newspaperColumns = getNewspaperColumns(component);
1172         for(int nc = 0; nc < newspaperColumns; nc++)
1173         {
1174             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
1175             {
1176                 UIComponent uiComponent = component.getChildren().get(i);
1177                 if (uiComponent.isRendered())
1178                 {
1179                     if (component instanceof UIData && uiComponent instanceof UIColumn)
1180                     {
1181                         beforeColumnHeaderOrFooter(facesContext, (UIData) component, header, columnIndex);
1182                     }
1183                 
1184                     renderColumnChildHeaderOrFooterRow(facesContext, writer, uiComponent, styleClass, header);
1185                     
1186                     if (component instanceof UIData && uiComponent instanceof UIColumn)
1187                     {
1188                         afterColumnHeaderOrFooter(facesContext, (UIData) component, header, columnIndex);
1189                     }
1190                 }
1191                 columnIndex += 1;
1192             }
1193 
1194             if (hasNewspaperTableSpacer(component))
1195             {
1196                 // draw the spacer facet
1197                 if(nc < newspaperColumns - 1)
1198                 {
1199                     renderSpacerCell(facesContext, writer, component);
1200                 }
1201             }
1202         }
1203         writer.endElement(HTML.TR_ELEM);
1204     }
1205 
1206       /**
1207       * Renders a spacer between adjacent newspaper columns.
1208       */
1209     protected void renderSpacerCell(FacesContext facesContext, ResponseWriter writer, UIComponent component)
1210         throws IOException 
1211     {
1212         UIComponent spacer = getNewspaperTableSpacer(component);
1213         if(spacer == null)
1214         {
1215             return;
1216         }
1217          
1218          writer.startElement(HTML.TD_ELEM, component);
1219          //RendererUtils.renderChild(facesContext, spacer);
1220          spacer.encodeAll(facesContext);
1221          writer.endElement(HTML.TD_ELEM);
1222      }
1223 
1224     protected void renderColumnChildHeaderOrFooterRow(FacesContext facesContext,
1225         ResponseWriter writer, UIComponent uiComponent, String styleClass, boolean isHeader) throws IOException
1226     {
1227         if (uiComponent instanceof UIColumn)
1228         {
1229             // allow column to override style class, new in JSF 1.2
1230             if (uiComponent instanceof HtmlColumn)
1231             {
1232                 HtmlColumn column = (HtmlColumn)uiComponent;
1233                 if (isHeader && column.getHeaderClass()!=null)
1234                 {
1235                     styleClass = column.getHeaderClass();
1236                 }
1237                 else if (!isHeader && column.getFooterClass()!=null)
1238                 {
1239                     styleClass = column.getFooterClass();
1240                 }
1241             }
1242             else
1243             {
1244                 //This code corrects MYFACES-1790, because HtmlColumnTag
1245                 //has as component type javax.faces.Column, so as side
1246                 //effect it not create HtmlColumn, it create UIColumn
1247                 //classes.
1248                 UIColumn column = (UIColumn) uiComponent;                
1249                 if (isHeader)
1250                 {
1251                     String headerClass = (String) column.getAttributes().get("headerClass");
1252                     if (headerClass != null)
1253                     {
1254                         styleClass = (String) headerClass;
1255                     }
1256                 }
1257                 else
1258                 {
1259                     String footerClass = (String) column.getAttributes().get("footerClass");
1260                     if (footerClass != null)
1261                     {
1262                         styleClass = (String) footerClass;
1263                     }
1264                 }
1265             }
1266             
1267             if (isHeader)
1268             {
1269                 renderColumnHeaderCell(facesContext, writer, uiComponent,
1270                     ((UIColumn) uiComponent).getHeader(), styleClass, 0);
1271             }
1272             else
1273             {
1274                 renderColumnFooterCell(facesContext, writer, uiComponent,
1275                     ((UIColumn) uiComponent).getFooter(), styleClass, 0);
1276             }
1277         }
1278     }
1279 
1280     /**
1281      * Renders the header facet for the given <code>UIColumn</code>.
1282      * @param facesContext the <code>FacesContext</code>.
1283      * @param writer the <code>ResponseWriter</code>.
1284      * @param uiColumn the <code>UIColumn</code>.
1285      * @param headerStyleClass the styleClass of the header facet.
1286      * @param colspan the colspan for the tableData element in which the header facet
1287      * will be wrapped.
1288      * @throws IOException
1289      */
1290     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
1291         String headerStyleClass, int colspan) throws IOException
1292     {
1293         renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn.getHeader(), headerStyleClass, colspan);
1294     }
1295 
1296     /**
1297      * Renders a TH cell within a TR within a THEAD section. If the specified
1298      * UIColumn object does have a header facet, then that facet is rendered
1299      * within the cell, otherwise the cell is left blank (though any specified
1300      * style class is still applied to empty cells).
1301      * 
1302      * @param facesContext the <code>FacesContext</code>.
1303      * @param writer the <code>ResponseWriter</code>.
1304      * @param uiComponent the <code>UIComponent</code> to render the facet for.
1305      * @param facet the <code>UIComponent</code> to render as facet.
1306      * @param headerStyleClass the styleClass of the header facet.
1307      * @param colspan the colspan for the tableData element in which the header facet
1308      * will be wrapped.
1309      * @throws IOException
1310      */
1311     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
1312             UIComponent facet, String headerStyleClass, int colspan) throws IOException
1313     {
1314         writer.startElement(determineHeaderCellTag(facesContext, uiComponent.getParent()), uiComponent);
1315         if (colspan > 1)
1316         {
1317             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
1318         }
1319         if (headerStyleClass != null)
1320         {
1321             writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass, null);
1322         }
1323 
1324         writer.writeAttribute(HTML.SCOPE_ATTR, "col", null);
1325 
1326         if (facet != null)
1327         {
1328             //RendererUtils.renderChild(facesContext, facet);
1329             facet.encodeAll(facesContext);
1330         }
1331         writer.endElement(determineHeaderCellTag(facesContext, uiComponent.getParent()));
1332     }
1333 
1334     protected String determineHeaderCellTag(FacesContext facesContext, UIComponent uiComponent)
1335     {
1336         return HTML.TH_ELEM;
1337     }
1338 
1339     /**
1340      * Renders the footer facet for the given <code>UIColumn</code>.
1341      * @param facesContext the <code>FacesContext</code>.
1342      * @param writer the <code>ResponseWriter</code>.
1343      * @param uiColumn the <code>UIComponent</code>.
1344      * @param footerStyleClass the styleClass of the footer facet.
1345      * @param colspan the colspan for the tableData element in which the footer facet
1346      * will be wrapped.
1347      * @throws IOException
1348      */
1349     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
1350         String footerStyleClass, int colspan) throws IOException
1351     {
1352       renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn.getFooter(), footerStyleClass, colspan);
1353     }
1354 
1355     /**
1356      * Renders the footer facet for the given <code>UIColumn</code>.
1357      * @param facesContext the <code>FacesContext</code>.
1358      * @param writer the <code>ResponseWriter</code>.
1359      * @param uiComponent the <code>UIComponent</code> to render the facet for.
1360      * @param facet the <code>UIComponent</code> to render as facet.
1361      * @param footerStyleClass the styleClass of the footer facet.
1362      * @param colspan the colspan for the tableData element in which the footer facet
1363      * will be wrapped.
1364      * @throws IOException
1365      */
1366     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
1367         UIComponent facet, String footerStyleClass, int colspan) throws IOException
1368     {
1369         writer.startElement(HTML.TD_ELEM, uiComponent);
1370         if (colspan > 1)
1371         {
1372             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
1373         }
1374         if (footerStyleClass != null)
1375         {
1376             writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null);
1377         }
1378         if (facet != null)
1379         {
1380             //RendererUtils.renderChild(facesContext, facet);
1381             facet.encodeAll(facesContext);
1382         }
1383         writer.endElement(HTML.TD_ELEM);
1384     }
1385 
1386     /**
1387      * Gets the headerClass attribute of the given <code>UIComponent</code>.
1388      * @param component the <code>UIComponent</code>.
1389      * @return the headerClass attribute of the given <code>UIComponent</code>.
1390      */
1391     protected static String getHeaderClass(UIComponent component)
1392     {
1393         if (component instanceof HtmlDataTable)
1394         {
1395             return ((HtmlDataTable) component).getHeaderClass();
1396         }
1397         else
1398         {
1399             return (String) component.getAttributes().get(
1400                     org.apache.myfaces.shared.renderkit.JSFAttr.HEADER_CLASS_ATTR);
1401         }
1402     }
1403 
1404     /**
1405      * Gets the footerClass attribute of the given <code>UIComponent</code>.
1406      * @param component the <code>UIComponent</code>.
1407      * @return the footerClass attribute of the given <code>UIComponent</code>.
1408      */
1409     protected static String getFooterClass(UIComponent component)
1410     {
1411         if (component instanceof HtmlDataTable)
1412         {
1413             return ((HtmlDataTable) component).getFooterClass();
1414         }
1415         else
1416         {
1417             return (String) component.getAttributes().get(
1418                     org.apache.myfaces.shared.renderkit.JSFAttr.FOOTER_CLASS_ATTR);
1419         }
1420     }
1421 
1422     public void decode(FacesContext context, UIComponent component)
1423     {
1424         super.decode(context, component);
1425         
1426         HtmlRendererUtils.decodeClientBehaviors(context, component);
1427     }
1428 
1429 }