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