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  
20  package org.apache.myfaces.renderkit.html.ext;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIData;
32  import javax.faces.component.ValueHolder;
33  import javax.faces.component.behavior.ClientBehavior;
34  import javax.faces.component.behavior.ClientBehaviorHolder;
35  import javax.faces.context.FacesContext;
36  import javax.faces.context.ResponseWriter;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
41  import org.apache.myfaces.component.NewspaperTable;
42  import org.apache.myfaces.component.html.ext.AbstractHtmlDataTable;
43  import org.apache.myfaces.component.html.ext.HtmlDataTable;
44  import org.apache.myfaces.custom.column.HtmlColumn;
45  import org.apache.myfaces.custom.column.HtmlSimpleColumn;
46  import org.apache.myfaces.custom.crosstable.UIColumns;
47  import org.apache.myfaces.renderkit.html.util.ColumnInfo;
48  import org.apache.myfaces.renderkit.html.util.RowInfo;
49  import org.apache.myfaces.renderkit.html.util.TableContext;
50  import org.apache.myfaces.shared_tomahawk.renderkit.ClientBehaviorEvents;
51  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
52  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
53  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
54  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
55  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase;
56  import org.apache.myfaces.shared_tomahawk.util.ArrayUtils;
57  
58  /**
59   * Renderer for the Tomahawk extended HtmlDataTable component.
60   *
61   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
62   * @version $Revision: 691079 $ $Date: 2008-09-01 17:55:44 -0500 (lun, 01 sep 2008) $
63   */
64  @JSFRenderer(
65     renderKitId = "HTML_BASIC",
66     family = "javax.faces.Data",
67     type = "org.apache.myfaces.Table")
68  public class HtmlTableRenderer extends HtmlTableRendererBase {
69      private static final Log log = LogFactory.getLog(HtmlTableRenderer.class);
70  
71      /**
72       * DetailStamp facet name.
73       */
74      public static final String DETAIL_STAMP_FACET_NAME = "detailStamp";
75      private static final String BODY_STYLE_CLASS = "bodyStyleClass";
76      private static final String BODY_STYLE = "bodyStyle";
77      
78      //Client events
79      public static final String ROW_CLICK = "rowClick";
80      public static final String ROW_DBLCLICK = "rowDblClick";
81      
82      public static final String ROW_KEYDOWN = "rowKeyDown";
83      public static final String ROW_KEYPRESS = "rowKeyPress";
84      public static final String ROW_KEYUP = "rowKeyUp";
85      
86      public static final String ROW_MOUSEDOWN = "rowMouseDown";
87      public static final String ROW_MOUSEMOVE = "rowMouseMove";
88      public static final String ROW_MOUSEOUT = "rowMouseOut";
89      public static final String ROW_MOUSEOVER = "rowMouseOver";
90      public static final String ROW_MOUSEUP = "rowMouseUp";
91      
92      //Attributes
93      public static final String ROW_ONCLICK_ATTR = "rowOnClick";
94      public static final String ROW_ONDBLCLICK_ATTR = "rowOnDblClick";
95      
96      public static final String ROW_ONKEYDOWN_ATTR = "rowOnKeyDown";
97      public static final String ROW_ONKEYPRESS_ATTR = "rowOnKeyPress";
98      public static final String ROW_ONKEYUP_ATTR = "rowOnKeyUp";
99      
100     public static final String ROW_ONMOUSEDOWN_ATTR = "rowOnMouseDown";
101     public static final String ROW_ONMOUSEMOVE_ATTR = "rowOnMouseMove";
102     public static final String ROW_ONMOUSEOUT_ATTR = "rowOnMouseOut";
103     public static final String ROW_ONMOUSEOVER_ATTR = "rowOnMouseOver";
104     public static final String ROW_ONMOUSEUP_ATTR = "rowOnMouseUp";
105     
106     private static final String[] EVENTS = {
107         ClientBehaviorEvents.CLICK,
108         ClientBehaviorEvents.DBLCLICK,
109         ClientBehaviorEvents.KEYDOWN,
110         ClientBehaviorEvents.KEYPRESS,
111         ClientBehaviorEvents.KEYUP,
112         ClientBehaviorEvents.MOUSEDOWN,
113         ClientBehaviorEvents.MOUSEMOVE,
114         ClientBehaviorEvents.MOUSEOUT,
115         ClientBehaviorEvents.MOUSEOVER,
116         ClientBehaviorEvents.MOUSEUP
117     };
118     
119     private static final String[] EVENTS_ATTRIBUTES =
120     {
121         HTML.ONCLICK_ATTR,
122         HTML.ONDBLCLICK_ATTR,
123         HTML.ONKEYDOWN_ATTR,
124         HTML.ONKEYPRESS_ATTR,
125         HTML.ONKEYUP_ATTR,
126         HTML.ONMOUSEDOWN_ATTR,
127         HTML.ONMOUSEMOVE_ATTR,
128         HTML.ONMOUSEOUT_ATTR,
129         HTML.ONMOUSEOVER_ATTR,
130         HTML.ONMOUSEUP_ATTR,
131     };
132     
133     private static final String[] COLUMN_ATTRIBUTES_WITHOUT_EVENTS = (String [])
134         ArrayUtils.concat(HTML.UNIVERSAL_ATTRIBUTES_WITHOUT_STYLE, new String[]{HTML.COLSPAN_ATTR});
135 
136     private static final String[] COLUMN_ATTRIBUTES = (String [])
137         (String[]) ArrayUtils.concat(HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE, new String[]{HTML.COLSPAN_ATTR});
138 
139     @Override
140     protected boolean isCommonPropertiesOptimizationEnabled(FacesContext facesContext)
141     {
142         return true;
143     }
144 
145     @Override
146     protected boolean isCommonEventsOptimizationEnabled(FacesContext facesContext)
147     {
148         return true;
149     }
150 
151     /**
152      * @param component dataTable
153      * @return number of layout columns
154      */
155     protected int getNewspaperColumns(UIComponent component) {
156         if (component instanceof NewspaperTable) {
157             // the number of slices to break the table up into */
158             NewspaperTable newspaperTable = (NewspaperTable) component;
159             return newspaperTable.getNewspaperColumns();
160         }
161         return super.getNewspaperColumns(component);
162     }
163 
164     /**
165      * @param component dataTable
166      * @return component to display between layout columns
167      */
168     protected UIComponent getNewspaperTableSpacer(UIComponent component) {
169         if (component instanceof NewspaperTable) {
170             // the number of slices to break the table up into */
171             NewspaperTable newspaperTable = (NewspaperTable) component;
172             return newspaperTable.getSpacer();
173         }
174         return super.getNewspaperTableSpacer(component);
175     }
176 
177     /**
178      * @param component dataTable
179      * @return whether dataTable has component to display between layout columns
180      */
181     protected boolean hasNewspaperTableSpacer(UIComponent component) {
182         if (null != getNewspaperTableSpacer(component)) {
183             return true;
184         }
185         return super.hasNewspaperTableSpacer(component);
186     }
187 
188     /**
189      * @param component dataTable
190      * @return if the orientation of the has newspaper columns is horizontal
191      */
192     protected boolean isNewspaperHorizontalOrientation(UIComponent component) {
193         if (component instanceof NewspaperTable) {
194             // get the value of the newspaperOrientation attribute, any value besides horizontal
195             // means vertical, the default
196             NewspaperTable newspaperTable = (NewspaperTable) component;
197             return NewspaperTable.NEWSPAPER_HORIZONTAL_ORIENTATION.equals(newspaperTable.getNewspaperOrientation());
198         }
199         return super.isNewspaperHorizontalOrientation(component);
200     }
201 
202     protected void startTable(FacesContext facesContext, UIComponent uiComponent) throws IOException {
203         boolean embedded = isEmbeddedTable(uiComponent);
204 
205         if (!embedded) {
206             super.startTable(facesContext, uiComponent);
207         }
208     }
209 
210     protected String determineHeaderFooterTag(FacesContext facesContext, UIComponent component, boolean header) {
211         if (isEmbeddedTable(component)) {
212             // we are embedded, so do not render the tfoot/thead stuff
213             return null;
214         }
215 
216         return super.determineHeaderFooterTag(facesContext, component, header);
217     }
218 
219     protected String determineHeaderCellTag(FacesContext facesContext, UIComponent component) {
220         if (isEmbeddedTable(component)) {
221             return HTML.TD_ELEM;
222         }
223 
224         return HTML.TH_ELEM;
225     }
226 
227     protected void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, UIComponent facet, String styleClass, String colElementName, int colspan, boolean isHeader) throws IOException {
228         if (isEmbeddedTable(component)) {
229             // embedded tables render the header/footer stuff using TD only
230             colElementName = HTML.TD_ELEM;
231         }
232 
233         super.renderTableHeaderOrFooterRow(facesContext, writer, component, facet, styleClass, colElementName, colspan, isHeader);
234     }
235 
236     protected boolean isEmbeddedTable(UIComponent uiComponent) {
237         boolean embedded = false;
238         if (uiComponent instanceof HtmlDataTable) {
239             HtmlDataTable table = (HtmlDataTable) uiComponent;
240             embedded = table.isEmbedded();
241         }
242         return embedded;
243     }
244 
245     protected boolean isDetailStampAfterRow(UIComponent uiComponent) {
246         if (uiComponent instanceof HtmlDataTable) {
247             return "after".equals(((HtmlDataTable) uiComponent).getDetailStampLocation());
248         }
249 
250         return true;
251     }
252 
253     protected void endTable(FacesContext facesContext, UIComponent uiComponent) throws IOException {
254         boolean embedded = isEmbeddedTable(uiComponent);
255 
256         if (!embedded) {
257             super.endTable(facesContext, uiComponent);
258         }
259     }
260 
261     protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException {
262         super.beforeRow(facesContext, uiData);
263 
264         if (!isDetailStampAfterRow(uiData)) {
265             renderDetailRow(facesContext, uiData);
266         }
267     }
268 
269     protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException {
270         super.afterRow(facesContext, uiData);
271 
272         if (isDetailStampAfterRow(uiData)) {
273             renderDetailRow(facesContext, uiData);
274         }
275     }
276 
277     /**
278      * @param facesContext
279      * @param uiData
280      * @throws IOException
281      */
282     private void renderDetailRow(FacesContext facesContext, UIData uiData) throws IOException {
283         UIComponent detailStampRow = uiData.getFacet(AbstractHtmlDataTable.DETAIL_STAMP_ROW_FACET_NAME);
284         UIComponent detailStampFacet = uiData.getFacet(DETAIL_STAMP_FACET_NAME);
285         
286         if (detailStampRow != null && detailStampFacet != null)
287         {
288             detailStampRow.encodeAll(facesContext);
289         }
290         else
291         {
292             if (uiData instanceof HtmlDataTable) {
293                 HtmlDataTable htmlDataTable = (HtmlDataTable) uiData;
294     
295                 if (htmlDataTable.isCurrentDetailExpanded()) {
296     
297                     boolean embedded = false;
298                     if (detailStampFacet != null) {
299                         embedded = isEmbeddedTable(detailStampFacet);
300                     }
301     
302                     ResponseWriter writer = facesContext.getResponseWriter();
303     
304                     if (!embedded) {
305                         writer.startElement(HTML.TR_ELEM, uiData);
306                         writer.startElement(HTML.TD_ELEM, uiData);
307                         //TOMAHAWK-1087 datatable dont renders a detail correct 
308                         //if a UIColumns is used we have to count UIColumns 
309                         //elements as component.getRowCount()
310                         //instead of just get the number of children available,
311                         //so the colspan could be assigned correctly.
312                         int childCount = 0;
313                         for (Iterator childIter = uiData.getChildren().iterator();
314                             childIter.hasNext();)
315                         {
316                             UIComponent childComp = (UIComponent) childIter.next();
317                             if (childComp instanceof UIColumns)
318                             {
319                                 UIColumns v = (UIColumns) childComp;
320                                 childCount += v.getRowCount();
321                             }
322                             else
323                             {
324                                 childCount++;
325                             }
326                         }
327                         writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(
328                                 childCount), null);
329                     }
330     
331                     if (detailStampFacet != null) {
332                         RendererUtils.renderChild(facesContext, detailStampFacet);
333                     }
334     
335                     if (!embedded) {
336                         writer.endElement(HTML.TD_ELEM);
337                         writer.endElement(HTML.TR_ELEM);
338                     }
339                 }
340             }
341         }
342     }
343 
344     /**
345      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
346      */
347     public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
348         if (uiComponent instanceof HtmlDataTable) {
349             HtmlDataTable htmlDataTable = (HtmlDataTable) uiComponent;
350             if (htmlDataTable.isRenderedIfEmpty() || htmlDataTable.getRowCount() > 0) {
351                 super.encodeBegin(facesContext, uiComponent);
352             }
353         }
354         else {
355             super.encodeBegin(facesContext, uiComponent);
356         }
357     }
358 
359     /**
360      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#encodeChildren(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
361      */
362     public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException {
363         if (component instanceof HtmlDataTable) {
364             HtmlDataTable htmlDataTable = (HtmlDataTable) component;
365             if (htmlDataTable.isRenderedIfEmpty() || htmlDataTable.getRowCount() > 0) {
366                 super.encodeChildren(facesContext, component);
367             }
368         }
369         else {
370             super.encodeChildren(facesContext, component);
371         }
372     }
373 
374     private boolean isGroupedTable(UIData uiData) {
375         if (uiData instanceof HtmlDataTable) {
376             List children = getChildren(uiData);
377             for (int j = 0, size = getChildCount(uiData); j < size; j++) {
378                 UIComponent child = (UIComponent) children.get(j);
379                 if (child instanceof HtmlSimpleColumn) {
380                     HtmlSimpleColumn column = (HtmlSimpleColumn) child;
381                     if (column.isGroupBy()) {
382                         return true;
383                     }
384                 }
385             }
386         }
387 
388         return false;  //To change body of created methods use File | Settings | File Templates.
389     }
390 
391     protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException {
392         if (isGroupedTable(uiData)) {
393             createColumnInfos((HtmlDataTable) uiData, facesContext);
394         }
395         super.beforeBody(facesContext, uiData);
396     }
397 
398     private void createColumnInfos(HtmlDataTable htmlDataTable, FacesContext facesContext)
399         throws IOException {
400         int first = htmlDataTable.getFirst();
401         int rows = htmlDataTable.getRows();
402         int last;
403         int currentRowSpan = -1;
404         int currentRowInfoIndex = -1;
405 
406         TableContext tableContext = htmlDataTable.getTableContext();
407         RowInfo rowInfo = null;
408         ColumnInfo columnInfo = null;
409         HtmlSimpleColumn currentColumn = null;
410         Map groupHashTable = new HashMap();
411 
412         if (rows <= 0) {
413             last = htmlDataTable.getRowCount();
414         }
415         else {
416             last = first + rows;
417         }
418 
419         //Loop over the Children Columns to find the Columns with groupBy Attribute true
420         List children = getChildren(htmlDataTable);
421         int nChildren = getChildCount(htmlDataTable);
422 
423         for (int j = 0, size = nChildren; j < size; j++) {
424             UIComponent child = (UIComponent) children.get(j);
425             if (child instanceof HtmlSimpleColumn) {
426                 currentColumn = (HtmlSimpleColumn) child;
427                 if (currentColumn.isGroupBy()) {
428                     groupHashTable.put(new Integer(j), null);
429                 }
430             }
431         }
432 
433         boolean groupEndReached = false;
434 
435         for (int rowIndex = first; last == -1 || rowIndex < last; rowIndex++) {
436             htmlDataTable.setRowIndex(rowIndex);
437             rowInfo = new RowInfo();
438             //scrolled past the last row
439             if (!htmlDataTable.isRowAvailable()) {
440                 break;
441             }
442 
443             Set groupIndexList = groupHashTable.keySet();
444             List currentColumnContent = null;
445             for (Iterator it = groupIndexList.iterator(); it.hasNext();) {
446                 currentColumnContent = new ArrayList();
447                 Integer currentIndex = (Integer) it.next();
448                 currentColumn = (HtmlSimpleColumn) children.get(currentIndex.intValue());
449 
450                 if (currentColumn.isGroupByValueSet()) {
451                     currentColumnContent.add(currentColumn.getGroupByValue());
452                 }
453                 else {
454                     // iterate the children - this avoids to add the column facet too
455                     List currentColumnChildren = currentColumn.getChildren();
456                     if (currentColumnChildren != null) {
457                         collectChildrenValues(currentColumnContent, currentColumnChildren.iterator());
458                     }
459                 }
460 
461                 if (!isListEqual(currentColumnContent, (List) groupHashTable.get(currentIndex)) &&
462                     currentRowInfoIndex > -1) {
463                     groupEndReached = true;
464                     groupHashTable.put(currentIndex, currentColumnContent);
465                 }
466                 else if (currentRowInfoIndex == -1) {
467                     groupHashTable.put(currentIndex, currentColumnContent);
468                 }
469             }
470             currentRowSpan++;
471 
472 
473             for (int j = 0, size = nChildren; j < size; j++) {
474                 columnInfo = new ColumnInfo();
475                 if (groupHashTable.containsKey(new Integer(j)))  // Column is groupBy
476                 {
477                     if (currentRowSpan > 0) {
478                         if (groupEndReached) {
479                             ((ColumnInfo)
480                                 ((RowInfo)
481                                     tableContext.getRowInfos().get(currentRowInfoIndex - currentRowSpan + 1)).
482                                     getColumnInfos().get(j)).
483                                 setRowSpan(currentRowSpan);
484                             columnInfo.setStyle(htmlDataTable.getRowGroupStyle());
485                             columnInfo.setStyleClass(htmlDataTable.getRowGroupStyleClass());
486                         }
487                         else {
488                             columnInfo.setRendered(false);
489                         }
490                     }
491                     else {
492                         columnInfo.setStyle(htmlDataTable.getRowGroupStyle());
493                         columnInfo.setStyleClass(htmlDataTable.getRowGroupStyleClass());
494                     }
495 
496                 }
497                 else    // Column  is not group by
498                 {
499                     if (groupEndReached) {
500                         ((ColumnInfo)
501                             ((RowInfo)
502                                 tableContext.getRowInfos().get(currentRowInfoIndex)).
503                                 getColumnInfos().get(j)).
504                             setStyle(htmlDataTable.getRowGroupStyle());
505                         ((ColumnInfo)
506                             ((RowInfo)
507                                 tableContext.getRowInfos().get(currentRowInfoIndex)).
508                                 getColumnInfos().get(j)).
509                             setStyleClass(htmlDataTable.getRowGroupStyleClass());
510                     }
511                 }
512                 rowInfo.getColumnInfos().add(columnInfo);
513             }
514             if (groupEndReached) {
515                 currentRowSpan = 0;
516                 groupEndReached = false;
517             }
518             tableContext.getRowInfos().add(rowInfo);
519             currentRowInfoIndex++;
520         }
521 
522         // do further processing if we've found at least one row
523         if (currentRowInfoIndex > -1) {
524             for (int j = 0, size = nChildren; j < size; j++) {
525                 if (groupHashTable.containsKey(new Integer(j)))  // Column is groupBy
526                 {
527                     ((ColumnInfo)
528                         ((RowInfo)
529                             tableContext.getRowInfos().get(currentRowInfoIndex - currentRowSpan)).
530                             getColumnInfos().get(j)).
531                         setRowSpan(currentRowSpan + 1);
532                 }
533                 else    // Column  is not group by
534                 {
535                     ((ColumnInfo)
536                         ((RowInfo)
537                             tableContext.getRowInfos().get(currentRowInfoIndex)).
538                             getColumnInfos().get(j)).
539                         setStyle(htmlDataTable.getRowGroupStyle());
540                     ((ColumnInfo)
541                         ((RowInfo)
542                             tableContext.getRowInfos().get(currentRowInfoIndex)).
543                             getColumnInfos().get(j)).
544                         setStyleClass(htmlDataTable.getRowGroupStyleClass());
545                 }
546             }
547         }
548 
549         htmlDataTable.setRowIndex(-1);
550     }
551 
552     protected void collectChildrenValues(List container, Iterator iterChildren) {
553         while (iterChildren.hasNext()) {
554             UIComponent child = (UIComponent) iterChildren.next();
555             if (child.isRendered()) {
556                 if (child instanceof ValueHolder) {
557                     Object value = ((ValueHolder) child).getValue();
558                     container.add(value);
559                 }
560 
561                 // we lave the column ... now iterate the facets too
562                 collectChildrenValues(container, child.getFacetsAndChildren());
563             }
564         }
565     }
566 
567     /**
568      * checks if the contenta of both lists are the same.<br />
569      * <b>Notice:</b> In case both lists are null or empty they are
570      * considered NOT being equal
571      */
572     protected boolean isListEqual(List list1, List list2) {
573         if (list1 == list2) {
574             return list1 != null && list1.size() > 0;
575         }
576         if (list1 == null || list2 == null || list1.size() != list2.size()) {
577             return false;
578         }
579 
580         for (int i = 0; i < list1.size(); i++) {
581             Object o1 = list1.get(i);
582             Object o2 = list2.get(i);
583 
584             if (o1 != null && !o1.equals(o2)) {
585                 return false;
586             }
587             if (o2 != null && !o2.equals(o1)) {
588                 return false;
589             }
590         }
591 
592         return true;
593     }
594 
595 
596     /**
597      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
598      */
599     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
600         if (uiComponent instanceof HtmlDataTable) {
601             HtmlDataTable htmlDataTable = (HtmlDataTable) uiComponent;
602             if (htmlDataTable.isRenderedIfEmpty() || htmlDataTable.getRowCount() > 0) {
603                 super.encodeEnd(facesContext, uiComponent);
604             }
605         }
606         else {
607             super.encodeEnd(facesContext, uiComponent);
608         }
609     }
610 
611     protected void renderRowStart(FacesContext facesContext,
612                                   ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex)
613         throws IOException {
614         super.renderRowStart(facesContext, writer, uiData, styles, rowStyleIndex);
615 
616         // get event handlers from component
617         HtmlDataTable table = (HtmlDataTable) uiData;
618         
619         if (table.isAjaxRowRender())
620         {
621             UIComponent row = table.getFacet("row");
622             if (row != null)
623             {
624                 writer.writeAttribute(HTML.ID_ATTR, row.getClientId(facesContext), null);
625             }
626         }
627         
628         Map<String, List<ClientBehavior>> clientBehaviors = ((ClientBehaviorHolder) uiData).getClientBehaviors();
629 
630         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONCLICK_ATTR, uiData,
631                 ROW_CLICK, clientBehaviors, HTML.ONCLICK_ATTR);
632         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONDBLCLICK_ATTR, uiData,
633                 ROW_DBLCLICK, clientBehaviors, HTML.ONDBLCLICK_ATTR);
634         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONKEYDOWN_ATTR, uiData,
635                 ROW_KEYDOWN, clientBehaviors, HTML.ONKEYDOWN_ATTR);
636         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONKEYPRESS_ATTR, uiData,
637                 ROW_KEYPRESS, clientBehaviors, HTML.ONKEYPRESS_ATTR);
638         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONKEYUP_ATTR, uiData,
639                 ROW_KEYUP, clientBehaviors, HTML.ONKEYUP_ATTR);
640         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONMOUSEDOWN_ATTR, uiData,
641                 ROW_MOUSEDOWN, clientBehaviors, HTML.ONMOUSEDOWN_ATTR);
642         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONMOUSEMOVE_ATTR, uiData,
643                 ROW_MOUSEMOVE, clientBehaviors, HTML.ONMOUSEMOVE_ATTR);
644         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONMOUSEOUT_ATTR, uiData,
645                 ROW_MOUSEOUT, clientBehaviors, HTML.ONMOUSEOUT_ATTR);
646         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONMOUSEOVER_ATTR, uiData,
647                 ROW_MOUSEOVER, clientBehaviors, HTML.ONMOUSEOVER_ATTR);
648         HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, ROW_ONMOUSEUP_ATTR, uiData,
649                 ROW_MOUSEUP, clientBehaviors, HTML.ONMOUSEUP_ATTR);
650         
651         /*
652         renderRowAttribute(writer, HTML.ONCLICK_ATTR, table.getRowOnClick());
653         renderRowAttribute(writer, HTML.ONDBLCLICK_ATTR, table.getRowOnDblClick());
654         renderRowAttribute(writer, HTML.ONKEYDOWN_ATTR, table.getRowOnKeyDown());
655         renderRowAttribute(writer, HTML.ONKEYPRESS_ATTR, table.getRowOnKeyPress());
656         renderRowAttribute(writer, HTML.ONKEYUP_ATTR, table.getRowOnKeyUp());
657         renderRowAttribute(writer, HTML.ONMOUSEDOWN_ATTR, table.getRowOnMouseDown());
658         renderRowAttribute(writer, HTML.ONMOUSEMOVE_ATTR, table.getRowOnMouseMove());
659         renderRowAttribute(writer, HTML.ONMOUSEOUT_ATTR, table.getRowOnMouseOut());
660         renderRowAttribute(writer, HTML.ONMOUSEOVER_ATTR, table.getRowOnMouseOver());
661         renderRowAttribute(writer, HTML.ONMOUSEUP_ATTR, table.getRowOnMouseUp());
662         */
663     }
664 
665     /**
666      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#renderRowStyle(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIData, org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase.Styles, int)
667      */
668     protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex) throws IOException {
669         String rowStyleClass;
670         String rowStyle;
671         if (uiData instanceof HtmlDataTable) {
672             HtmlDataTable datatable = (HtmlDataTable) uiData;
673             rowStyleClass = datatable.getRowStyleClass();
674             rowStyle = datatable.getRowStyle();
675         }
676         else {
677             rowStyleClass = (String) uiData.getAttributes().get(JSFAttr.ROW_STYLECLASS_ATTR);
678             rowStyle = (String) uiData.getAttributes().get(JSFAttr.ROW_STYLE_ATTR);
679         }
680         if (rowStyleClass == null) {
681             super.renderRowStyle(facesContext, writer, uiData, styles, rowStyleIndex);
682         }
683         else {
684             writer.writeAttribute(HTML.CLASS_ATTR, rowStyleClass, null);
685         }
686         if (rowStyle != null) {
687             writer.writeAttribute(HTML.STYLE_ATTR, rowStyle, null);
688         }
689     }
690 
691     protected void renderRowAttribute(ResponseWriter writer,
692                                       String htmlAttribute, Object value) throws IOException {
693         if (value != null) {
694             writer.writeAttribute(htmlAttribute, value, null);
695         }
696     }
697 
698     /**
699      * Render the specified column object using the current row data.
700      * <p/>
701      * When the component is a UIColumn object, the inherited method is
702      * invoked to render a single table cell.
703      * <p/>
704      * In addition to the inherited functionality, support is implemented
705      * here for UIColumns children. When a UIColumns child is encountered:
706      * <pre>
707      * For each dynamic column in that UIColumns child:
708      *   * Select the column (which sets variable named by the var attribute
709      *     to refer to the current column object)
710      *   * Call this.renderColumnBody passing the UIColumns object.
711      * </pre>
712      * The renderColumnBody method eventually:
713      * <ul>
714      * <li>emits TD
715      * <li>calls encodeBegin on the UIColumns (which does nothing)
716      * <li>calls rendering methods on all children of the UIColumns
717      * <li>calls encodeEnd on the UIColumns (which does nothing)
718      * <li> emits /TD
719      * </ul>
720      * If the children of the UIColumns access the variable named by the var
721      * attribute on the UIColumns object, then they end up rendering content
722      * that is extracted from the current column object.
723      *
724      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#encodeColumnChild(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIData, javax.faces.component.UIComponent, org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase.Styles, int)
725      */
726     protected void encodeColumnChild(FacesContext facesContext,
727                                      ResponseWriter writer, UIData uiData,
728                                      UIComponent component, Styles styles, int columnStyleIndex)
729         throws IOException {        
730         //columnStyleIndex param does not takes into
731         //consideration UIColumns, since it is called from HtmlTableRendererBase.
732         //So we need to recalculate its index taking into account that
733         //UIColumns component count as ((UIColumns) child).getRowCount() instead 1 
734         columnStyleIndex = getColumnStyleIndex(uiData, columnStyleIndex);
735         super.encodeColumnChild(facesContext, writer, uiData, component,
736             styles, columnStyleIndex);
737         if (component instanceof UIColumns)
738         {
739             UIColumns columns = (UIColumns) component;
740             for (int k = 0, colSize = columns.getRowCount(); k < colSize; k++)
741             {
742                 columns.setRowIndex(k);
743                 renderColumnBody(facesContext, writer, uiData, component,
744                     styles, columnStyleIndex + k);
745             }
746             columns.setRowIndex(-1);
747         }
748     }
749     
750     /**
751       * This method calculates the correct columnStyleIndex taking the dynamic columns
752       * of <b>UIColumns</b> into consideration
753       * 
754       * @param uiData
755       * @param columnStyleIndex
756       * @return the correct columnStyleIndex 
757       */
758     private int getColumnStyleIndex(UIData uiData, int columnStyleIndex)
759     {
760         int colStyleIndex = 0;
761         for (int i = 0; i < columnStyleIndex; i++)
762         {
763             UIComponent child = (UIComponent) uiData.getChildren().get(i % uiData.getChildCount());
764             if (child instanceof UIColumns)
765             {
766                 colStyleIndex += ((UIColumns) child).getRowCount();
767                 continue;
768             }
769             colStyleIndex++;
770         }
771         return colStyleIndex;
772     }
773     
774 
775     /**
776      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#renderColumnBody(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIData, javax.faces.component.UIComponent, org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase.Styles, int)
777      */
778     protected void renderColumnBody(FacesContext facesContext,
779                                     ResponseWriter writer, UIData uiData,
780                                     UIComponent component, Styles styles, int columnStyleIndex)
781         throws IOException {
782         if (isGroupedTable(uiData)) {
783 
784             HtmlDataTable htmlDataTable = (HtmlDataTable) uiData;
785             List tableChildren = htmlDataTable.getChildren();
786 
787             int first = htmlDataTable.getFirst();
788             int rowInfoIndex = htmlDataTable.getRowIndex() - first;
789             int columnInfoIndex = tableChildren.indexOf(component);
790 
791             RowInfo rowInfo = (RowInfo) htmlDataTable.getTableContext().getRowInfos().get(rowInfoIndex);
792             ColumnInfo columnInfo = (ColumnInfo) rowInfo.getColumnInfos().get(columnInfoIndex);
793 
794             if (!columnInfo.isRendered()) {
795                 return;
796             }
797 
798             if (component instanceof HtmlColumn && amISpannedOver(null, component)) {
799                 return;
800             }
801             writer.startElement(HTML.TD_ELEM, uiData);
802             String styleClass = null;
803             if (component instanceof HtmlColumn) {
804                 styleClass = ((HtmlColumn) component).getStyleClass();
805             }
806             if (columnInfo.getStyleClass() != null) {
807                 styleClass = columnInfo.getStyleClass();
808             }
809 
810             if (styles.hasColumnStyle()) {
811                 if (styleClass == null) {
812                     styleClass = styles.getColumnStyle(columnStyleIndex);
813                 }
814             }
815             if (styleClass != null) {
816                 writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
817             }
818 
819             if (columnInfo.getStyle() != null) {
820                 writer.writeAttribute(HTML.STYLE_ATTR, columnInfo.getStyle(), null);
821             }
822 
823             if (columnInfo.getRowSpan() > 1) {
824                 writer.writeAttribute("rowspan", new Integer(columnInfo.getRowSpan()).toString(), null);
825                 if (columnInfo.getStyle() == null) {
826                     writer.writeAttribute(HTML.STYLE_ATTR, "vertical-align:top", null);
827                 }
828             }
829 
830             renderHtmlColumnAttributes(facesContext, writer, component, null);
831 
832             RendererUtils.renderChild(facesContext, component);
833             writer.endElement(HTML.TD_ELEM);
834         }
835         else if (component instanceof HtmlColumn) {
836             if (amISpannedOver(null, component)) {
837                 return;
838             }
839             writer.startElement(HTML.TD_ELEM, uiData);
840             String styleClass = ((HtmlColumn) component).getStyleClass();
841             if (styles.hasColumnStyle()) {
842                 if (styleClass == null) {
843                     styleClass = styles.getColumnStyle(columnStyleIndex);
844                 }
845             }
846             if (styleClass != null) {
847                 writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
848             }
849             renderHtmlColumnAttributes(facesContext, writer, component, null);
850 
851             RendererUtils.renderChild(facesContext, component);
852             writer.endElement(HTML.TD_ELEM);
853         }
854         else {
855             super.renderColumnBody(facesContext, writer, uiData, component,
856                 styles, columnStyleIndex);
857         }
858     }
859 
860     /**
861      * Render the header or footer of the specified column object.
862      * <p/>
863      * When the component is a UIColumn object, the inherited method is
864      * invoked to render a single header cell.
865      * <p/>
866      * In addition to the inherited functionality, support is implemented
867      * here for UIColumns children. When a UIColumns child is encountered:
868      * <pre>
869      * For each dynamic column in that UIColumns child:
870      *   * Select the column (which sets variable named by the var attribute
871      *     to refer to the current column object)
872      *   * Call this.renderColumnHeaderCell or this.renderColumnFooterCell
873      *     passing the header or footer facet of the UIColumns object.
874      * </pre>
875      * If the facet of the UIColumns accesses the variable named by the var
876      * attribute on the UIColumns object, then it ends up rendering content
877      * that is extracted from the current column object.
878      *
879      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#renderColumnChildHeaderOrFooterRow(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIComponent, java.lang.String, boolean)
880      */
881     protected void renderColumnChildHeaderOrFooterRow(
882         FacesContext facesContext, ResponseWriter writer,
883         UIComponent uiComponent, String styleClass, boolean header)
884         throws IOException {
885         super.renderColumnChildHeaderOrFooterRow(facesContext, writer,
886             uiComponent, styleClass, header);
887         if (uiComponent instanceof UIColumns) {
888             UIColumns columns = (UIColumns) uiComponent;
889             for (int i = 0, size = columns.getRowCount(); i < size; i++) {
890                 columns.setRowIndex(i);
891                 if (header) {
892                     renderColumnHeaderCell(facesContext, writer, columns,
893                         columns.getHeader(), styleClass, 0);
894                 }
895                 else {
896                     renderColumnFooterCell(facesContext, writer, columns,
897                         columns.getFooter(), styleClass, 0);
898                 }
899             }
900             columns.setRowIndex(-1);
901         }
902     }
903 
904     /**
905      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#renderColumnHeaderCell(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIComponent, javax.faces.component.UIComponent, java.lang.String, int)
906      */
907     protected void renderColumnHeaderCell(FacesContext facesContext,
908                                           ResponseWriter writer, UIComponent uiComponent,
909                                           UIComponent facet, String headerStyleClass, int colspan)
910         throws IOException {
911         if (uiComponent instanceof HtmlColumn) {
912 
913             HtmlColumn column = (HtmlColumn) uiComponent;
914 
915             if (amISpannedOver("header", uiComponent)) {
916                 return;
917             }
918 
919             writer.startElement(determineHeaderCellTag(facesContext, uiComponent.getParent()), uiComponent);
920 
921             String columnId = column.getColumnId();
922             if (columnId != null)
923             {
924                 writer.writeAttribute(HTML.ID_ATTR, columnId, null);
925             }
926 
927             if (colspan > 1) {
928                 writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan),
929                     null);
930             }
931             String styleClass = ((HtmlColumn) uiComponent)
932                 .getHeaderstyleClass();
933             if (styleClass == null) {
934                 styleClass = headerStyleClass;
935             }
936             if (styleClass != null) {
937                 writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
938             }
939             renderHtmlColumnAttributes(facesContext, writer, uiComponent, "header");
940             if (facet != null) {
941                 RendererUtils.renderChild(facesContext, facet);
942             }
943             writer.endElement(determineHeaderCellTag(facesContext, uiComponent.getParent()));
944         }
945         else {
946             super.renderColumnHeaderCell(facesContext, writer, uiComponent,
947                 facet, headerStyleClass, colspan);
948         }
949     }
950 
951     /**
952      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#renderColumnFooterCell(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, javax.faces.component.UIComponent, javax.faces.component.UIComponent, java.lang.String, int)
953      */
954     protected void renderColumnFooterCell(FacesContext facesContext,
955                                           ResponseWriter writer, UIComponent uiComponent,
956                                           UIComponent facet, String footerStyleClass, int colspan)
957         throws IOException {
958         if (uiComponent instanceof HtmlColumn) {
959             if (amISpannedOver("footer", uiComponent)) {
960                 return;
961             }
962             writer.startElement(HTML.TD_ELEM, uiComponent);
963             if (colspan > 1) {
964                 writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan),
965                     null);
966             }
967             String styleClass = ((HtmlColumn) uiComponent)
968                 .getFooterstyleClass();
969             if (styleClass == null) {
970                 styleClass = footerStyleClass;
971             }
972             if (styleClass != null) {
973                 writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
974             }
975             renderHtmlColumnAttributes(facesContext, writer, uiComponent, "footer");
976             if (facet != null) {
977                 RendererUtils.renderChild(facesContext, facet);
978             }
979             writer.endElement(HTML.TD_ELEM);
980         }
981         else {
982             super.renderColumnFooterCell(facesContext, writer, uiComponent,
983                 facet, footerStyleClass, colspan);
984         }
985     }
986 
987     protected void renderHtmlColumnAttributes(FacesContext facesContext, ResponseWriter writer,
988                                               UIComponent uiComponent, String prefix) throws IOException {
989         
990         Map<String, List<ClientBehavior>> behaviors = null;
991         if (uiComponent instanceof ClientBehaviorHolder)
992         {
993             behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
994         }
995         
996         if (behaviors != null && !behaviors.isEmpty())
997         {
998             for (int i = 0, size = COLUMN_ATTRIBUTES_WITHOUT_EVENTS.length; i < size; i++) {
999                 String attributeName = COLUMN_ATTRIBUTES_WITHOUT_EVENTS[i];
1000                 String compAttrName = prefix != null ? prefix + attributeName : attributeName;
1001                 HtmlRendererUtils.renderHTMLAttribute(writer, uiComponent,
1002                     compAttrName, attributeName);
1003             }
1004             
1005             for (int i = 0, size = EVENTS.length; i < size; i++)
1006             {
1007                 String attributeName = EVENTS_ATTRIBUTES[i];
1008                 String eventName = prefix != null ? prefix + EVENTS[i] : EVENTS[i];
1009                 String compAttrName = prefix != null ? prefix + attributeName : attributeName;
1010                 HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer,
1011                         compAttrName, uiComponent, eventName, behaviors, attributeName);
1012             }
1013         }
1014         else
1015         {
1016             for (int i = 0, size = COLUMN_ATTRIBUTES.length; i < size; i++) {
1017                 String attributeName = COLUMN_ATTRIBUTES[i];
1018                 String compAttrName = prefix != null ? prefix + attributeName : attributeName;
1019                 HtmlRendererUtils.renderHTMLAttribute(writer, uiComponent,
1020                     compAttrName, attributeName);
1021             }
1022         }
1023         String compAttrName = prefix != null ? prefix + HTML.STYLE_ATTR : HTML.STYLE_ATTR;
1024         HtmlRendererUtils.renderHTMLAttribute(writer, uiComponent,
1025             compAttrName, HTML.STYLE_ATTR);
1026 
1027         HtmlRendererUtils.renderHTMLAttribute(writer, uiComponent,
1028             HTML.WIDTH_ATTR, HTML.WIDTH_ATTR);
1029     }
1030 
1031     /**
1032      * Return the number of columns spanned by the specified component.
1033      * <p/>
1034      * For normal components, use the inherited implementation.
1035      * For UIColumns children, return the number of dynamic columns rendered
1036      * by that child.
1037      *
1038      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase
1039      *      #determineChildColSpan(javax.faces.component.UIComponent)
1040      */
1041     protected int determineChildColSpan(UIComponent uiComponent) {
1042         int result = super.determineChildColSpan(uiComponent);
1043         if (uiComponent instanceof UIColumns) {
1044             result += ((UIColumns) uiComponent).getRowCount();
1045         }
1046         return result;
1047     }
1048 
1049     /**
1050      * Return true if the specified component has a facet that needs to be
1051      * rendered in a THEAD or TFOOT section.
1052      *
1053      * @see org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlTableRendererBase#hasFacet(boolean, javax.faces.component.UIComponent)
1054      */
1055     protected boolean hasFacet(boolean header, UIComponent uiComponent) {
1056         boolean result = super.hasFacet(header, uiComponent);
1057         if (!result && uiComponent instanceof UIColumns) {
1058             // Why is this necessary? It seems to me that the inherited
1059             // implementation will work fine with a UIColumns component...
1060             UIColumns columns = (UIColumns) uiComponent;
1061             result = header ? columns.getHeader() != null : columns.getFooter() != null;
1062         }
1063         return result;
1064     }
1065 
1066     /**
1067      * Renders the column footer.
1068      * Rendering will be supressed if all of the facets have rendered="false"
1069      */
1070     protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, String footerStyleClass) throws IOException {
1071         if (determineRenderFacet(component, false)) {
1072             super.renderColumnFooterRow(facesContext, writer, component, footerStyleClass);
1073         }
1074     }
1075 
1076     /**
1077      * Renders the column header.
1078      * Rendering will be supressed if all of the facets have rendered="false"
1079      */
1080     protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, String headerStyleClass) throws IOException {
1081         if (determineRenderFacet(component, true)) {
1082             super.renderColumnHeaderRow(facesContext, writer, component, headerStyleClass);
1083         }
1084     }
1085 
1086     /**
1087      * determine if the header or footer should be rendered.
1088      */
1089     protected boolean determineRenderFacet(UIComponent component, boolean header) {
1090         for (Iterator it = getChildren(component).iterator(); it.hasNext();) {
1091             UIComponent uiComponent = (UIComponent) it.next();
1092             if (uiComponent.isRendered() && determineChildColSpan(uiComponent) > 0) {
1093                 UIComponent facet = header ? (UIComponent) uiComponent.getFacets().get(HEADER_FACET_NAME)
1094                     : (UIComponent) uiComponent.getFacets().get(FOOTER_FACET_NAME);
1095 
1096                 if (facet != null && facet.isRendered()) {
1097                     return true;
1098                 }
1099             }
1100         }
1101 
1102         return false;
1103     }
1104 
1105     protected void beforeColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException {
1106         super.beforeColumn(facesContext, uiData, columnIndex);
1107 
1108         if (uiData instanceof HtmlDataTable) {
1109             HtmlDataTable dataTable = (HtmlDataTable) uiData;
1110 
1111             putSortedReqScopeParam(facesContext, dataTable, columnIndex);
1112         }
1113     }
1114 
1115     protected void beforeColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header, int columnIndex) throws IOException {
1116         super.beforeColumnHeaderOrFooter(facesContext, uiData, header, columnIndex);
1117 
1118         if (header && uiData instanceof HtmlDataTable) {
1119             HtmlDataTable dataTable = (HtmlDataTable) uiData;
1120 
1121             putSortedReqScopeParam(facesContext, dataTable, columnIndex);
1122         }
1123     }
1124 
1125     protected void putSortedReqScopeParam(FacesContext facesContext, HtmlDataTable dataTable, int columnIndex) {
1126         String sortedColumnVar = dataTable.getSortedColumnVar();
1127         Map requestMap = facesContext.getExternalContext().getRequestMap();
1128 
1129         if (columnIndex == dataTable.getSortColumnIndex()) {
1130             if (sortedColumnVar != null) {
1131                 requestMap.put(sortedColumnVar, Boolean.TRUE);
1132             }
1133         }
1134         else if (sortedColumnVar != null) {
1135             requestMap.remove(sortedColumnVar);
1136         }
1137     }
1138 
1139     /**
1140      * specify if the header, footer or <td> is spanned over (not shown) because
1141      * of a colspan in a cell in a previous column
1142      *
1143      * @param prefix    header, footer or null
1144      * @param component
1145      */
1146     protected boolean amISpannedOver(String prefix, UIComponent component) {
1147         UIComponent table = component.getParent();
1148         int span = 0;
1149         for (Iterator it = table.getChildren().iterator(); it.hasNext();) {
1150             UIComponent columnComponent = (UIComponent) it.next();
1151             if (!(columnComponent instanceof HtmlColumn)) {
1152                 continue;
1153             }
1154             if (span > 0) {
1155                 span--;
1156             }
1157             if (columnComponent == component) {
1158                 return span > 0;
1159             }
1160             if (span == 0) {
1161                 try {
1162                     if (prefix == null && ((HtmlColumn) columnComponent).getColspan() != null) {
1163                         span = Integer.parseInt(((HtmlColumn) columnComponent).getColspan());
1164                     }
1165                     if ("header".equals(prefix) && ((HtmlColumn) columnComponent).getHeadercolspan() != null) {
1166                         span = Integer.parseInt(((HtmlColumn) columnComponent).getHeadercolspan());
1167                     }
1168                     if ("footer".equals(prefix) && ((HtmlColumn) columnComponent).getFootercolspan() != null) {
1169                         span = Integer.parseInt(((HtmlColumn) columnComponent).getFootercolspan());
1170                     }
1171                 }
1172                 catch (NumberFormatException ex) {
1173                     log.warn("Invalid " + ((prefix == null) ? "" : prefix) + "colspan attribute ignored");
1174                 }
1175             }
1176         }
1177         return false;
1178     }
1179 
1180 
1181     /**
1182      * Perform any operations necessary in the TBODY start tag.
1183      *
1184      * @param facesContext the <code>FacesContext</code>.
1185      * @param uiData       the <code>UIData</code> being rendered.
1186      */
1187     protected void inBodyStart(FacesContext facesContext, UIData uiData) throws IOException {
1188         String bodyStyleClass;
1189         String bodyStyle;
1190 
1191         if (uiData instanceof HtmlDataTable) {
1192             bodyStyleClass = ((HtmlDataTable) uiData).getBodyStyleClass();
1193             bodyStyle = ((HtmlDataTable) uiData).getBodyStyle();
1194         }
1195         else {
1196             bodyStyleClass = (String) uiData.getAttributes().get(BODY_STYLE_CLASS);
1197             bodyStyle = (String) uiData.getAttributes().get(BODY_STYLE);
1198         }
1199 
1200         ResponseWriter writer = facesContext.getResponseWriter();
1201         if (bodyStyleClass != null) {
1202             writer.writeAttribute(HTML.CLASS_ATTR, bodyStyleClass,
1203                 BODY_STYLE_CLASS);
1204         }
1205         if (bodyStyle != null) {
1206             writer.writeAttribute(HTML.STYLE_ATTR, bodyStyle, BODY_STYLE);
1207         }
1208     }
1209 
1210 
1211 }