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