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_orchestra.renderkit.html;
    20
    21  import org.apache.commons.logging.Log;
    22  import org.apache.commons.logging.LogFactory;
    23  import org.apache.myfaces.shared_orchestra.renderkit.JSFAttr;
    24  import org.apache.myfaces.shared_orchestra.renderkit.RendererUtils;
    25  import org.apache.myfaces.shared_orchestra.util.ArrayUtils;
    26  import org.apache.myfaces.shared_orchestra.util.StringUtils;
    27
    28  import javax.faces.component.UIColumn;
    29  import javax.faces.component.UIComponent;
    30  import javax.faces.component.UIData;
    31  import javax.faces.component.html.HtmlDataTable;
    32  import javax.faces.context.FacesContext;
    33  import javax.faces.context.ResponseWriter;
    34  import java.io.IOException;
    35  import java.util.Iterator;
    36  import java.util.List;
    37
    38  /**
    39   * Common methods for renderers for components that subclass the standard
    40   * JSF HtmlDataTable component.
    41   *
    42   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
    43   * @version $Revision: 1307197 $ $Date: 2012-03-29 20:12:16 -0500 (Thu, 29 Mar 2012) $
    44   */
    45  public class HtmlTableRendererBase extends HtmlRenderer
    46  {
    47      /** Header facet name. */
    48      protected static final String HEADER_FACET_NAME = "header";
    49
    50      /** Footer facet name. */
    51      protected static final String FOOTER_FACET_NAME = "footer";
    52
    53      /** The logger. */
    54      private static final Log log = LogFactory.getLog(HtmlTableRendererBase.class);
    55
    56      /**
    57       * @param component dataTable
    58       * @return number of layout columns
    59       */
    60      protected int getNewspaperColumns(UIComponent component) {
    61          return 1;
    62      }
    63
    64      /**
    65       * @param component dataTable
    66       * @return component to display between layout columns
    67       */
    68      protected UIComponent getNewspaperTableSpacer(UIComponent component) {
    69          return null;
    70      }
    71
    72      /**
    73       * @param component dataTable
    74       * @return whether dataTable has component to display between layout columns
    75       */
    76      protected boolean hasNewspaperTableSpacer(UIComponent component) {
    77          return false;
    78      }
    79
    80      /**
    81       * @param component dataTable
    82       * @return whether dataTable has newspaper columns layed out horizontally
    83       */
    84      protected boolean isNewspaperHorizontalOrientation(UIComponent component) {
    85          return false;
    86      }
    87
    88      /**
    89       * @see javax.faces.render.Renderer#getRendersChildren()
    90       */
    91      public boolean getRendersChildren()
    92      {
    93          return true;
    94      }
    95
    96      /**
    97       * Render the necessary bits that come before any actual <i>rows</i> in the table.
    98       *
    99       * @see javax.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
    100      */
    101     public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
    102     {
    103         RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
    104
    105         beforeTable(facesContext, (UIData) uiComponent);
    106
    107         startTable(facesContext, uiComponent);
    108     }
    109
    110     /**
    111      * actually render the start of the table
    112      */
    113     protected void startTable(FacesContext facesContext, UIComponent uiComponent) throws IOException
    114     {
    115         ResponseWriter writer = facesContext.getResponseWriter();
    116         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    117         writer.startElement(HTML.TABLE_ELEM, uiComponent);
    118         HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
    119         HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
    120     }
    121
    122     /**
    123      * Render the TBODY section of the html table. See also method encodeInnerHtml.
    124      *
    125      * @see javax.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
    126      */
    127     public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
    128     {
    129         RendererUtils.checkParamValidity(facesContext, component, UIData.class);
    130
    131         ResponseWriter writer = facesContext.getResponseWriter();
    132
    133         beforeBody(facesContext, (UIData) component);
    134
    135         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    136         writer.startElement(HTML.TBODY_ELEM, component);
    137         writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext)+":tbody_element", null);
    138         inBodyStart(facesContext, (UIData) component);
    139
    140         encodeInnerHtml(facesContext, component);
    141
    142         writer.endElement(HTML.TBODY_ELEM);
    143
    144         afterBody(facesContext, (UIData) component);
    145     }
    146
    147     /**
    148      * Gets styles for the specified component.
    149      */
    150     protected static Styles getStyles(UIData uiData) {
    151         String rowClasses;
    152         String columnClasses;
    153         if(uiData instanceof HtmlDataTable) {
    154             rowClasses = ((HtmlDataTable)uiData).getRowClasses();
    155             columnClasses = ((HtmlDataTable)uiData).getColumnClasses();
    156         } else {
    157             rowClasses = (String)uiData.getAttributes().get(JSFAttr.ROW_CLASSES_ATTR);
    158             columnClasses = (String)uiData.getAttributes().get(JSFAttr.COLUMN_CLASSES_ATTR);
    159         }
    160         return new Styles(rowClasses, columnClasses);
    161     }
    162
    163     /**
    164      * Class manages the styles from String lists.
    165      */
    166     protected static class Styles {
    167
    168         private String[] _columnStyle;
    169         private String[] _rowStyle;
    170
    171         Styles(String rowStyles, String columnStyles) {
    172             _rowStyle = (rowStyles == null)
    173                 ? ArrayUtils.EMPTY_STRING_ARRAY
    174                 : StringUtils.trim(
    175                     StringUtils.splitShortString(rowStyles, ','));
    176             _columnStyle = (columnStyles == null)
    177                 ? ArrayUtils.EMPTY_STRING_ARRAY
    178                 : StringUtils.trim(
    179                     StringUtils.splitShortString(columnStyles, ','));
    180         }
    181
    182         public String getRowStyle(int idx) {
    183             if(!hasRowStyle()) {
    184                 return null;
    185             }
    186             return _rowStyle[idx % _rowStyle.length];
    187         }
    188
    189         public String getColumnStyle(int idx) {
    190             if(!hasColumnStyle()) {
    191                 return null;
    192             }
    193             return _columnStyle[idx % _columnStyle.length];
    194         }
    195
    196         public boolean hasRowStyle() {
    197             return _rowStyle.length > 0;
    198         }
    199
    200         public boolean hasColumnStyle() {
    201             return _columnStyle.length > 0;
    202         }
    203     }
    204
    205
    206     /**
    207      * Renders everything inside the TBODY tag by iterating over the row objects
    208      * between offsets first and first+rows and applying the UIColumn components
    209      * to those objects.
    210      * <p>
    211      * This method is separated from the encodeChildren so that it can be overridden by
    212      * subclasses. One class that uses this functionality is autoUpdateDataTable.
    213      */
    214      public void encodeInnerHtml(FacesContext facesContext, UIComponent component)throws IOException{
    215
    216         UIData uiData = (UIData) component;
    217         ResponseWriter writer = facesContext.getResponseWriter();
    218
    219         int rowCount = uiData.getRowCount();
    220
    221         int newspaperColumns = getNewspaperColumns(component);
    222
    223         if (rowCount == -1 && newspaperColumns == 1)
    224         {
    225             encodeInnerHtmlUnknownRowCount(facesContext, component);
    226             return;
    227         }
    228
    229         if (rowCount == 0)
    230         {
    231             //nothing to render, to get valid xhtml we render an empty dummy row
    232             writer.startElement(HTML.TR_ELEM, uiData);
    233             writer.startElement(HTML.TD_ELEM, uiData);
    234             writer.endElement(HTML.TD_ELEM);
    235             writer.endElement(HTML.TR_ELEM);
    236             return;
    237         }
    238
    239         // begin the table
    240         // get the CSS styles
    241         Styles styles = getStyles(uiData);
    242
    243         int first = uiData.getFirst();
    244         int rows = uiData.getRows();
    245         int last;
    246
    247         if (rows <= 0)
    248         {
    249            last = rowCount;
    250         }
    251         else
    252         {
    253            last = first + rows;
    254            if (last > rowCount)
    255            {
    256                last = rowCount;
    257            }
    258         }
    259
    260         int newspaperRows;
    261         if((last - first) % newspaperColumns == 0)
    262         {
    263             newspaperRows = (last - first) / newspaperColumns;
    264         }
    265         else
    266         {
    267             newspaperRows = ((last - first) / newspaperColumns) + 1;
    268         }
    269         boolean newspaperHorizontalOrientation = isNewspaperHorizontalOrientation(component);
    270
    271         // walk through the newspaper rows
    272         for(int nr = 0; nr < newspaperRows; nr++)
    273         {
    274             boolean rowStartRendered = false;
    275             // walk through the newspaper columns
    276             for(int nc = 0; nc < newspaperColumns; nc++)
    277             {
    278
    279                 // the current row in the 'real' table
    280                 int currentRow;
    281                 if (newspaperHorizontalOrientation)
    282                 {
    283                     currentRow = nr * newspaperColumns + nc + first;
    284                 }
    285                 else
    286                 {
    287                     currentRow = nc * newspaperRows + nr + first;
    288                 }
    289
    290                 // if this row is not to be rendered
    291                 if(currentRow >= last)
    292                 {
    293                     continue;
    294                 }
    295
    296                 // bail if any row does not exist
    297                 uiData.setRowIndex(currentRow);
    298                 if(!uiData.isRowAvailable())
    299                 {
    300                     log.error("Row is not available. Rowindex = " + currentRow);
    301                     break;
    302                 }
    303
    304                 if (nc == 0)
    305                 {
    306                     // first column in table, start new row
    307                     beforeRow(facesContext, uiData);
    308
    309                     HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    310                     renderRowStart(facesContext, writer, uiData, styles, nr);
    311                     rowStartRendered = true;
    312                 }
    313
    314                 List children = null;
    315                 for (int j = 0, size = getChildCount(component); j < size; j++)
    316                 {
    317                     if (children == null)
    318                     {
    319                         children = getChildren(component);
    320                     }
    321                     UIComponent child = (UIComponent) children.get(j);
    322                     if (child.isRendered())
    323                     {
    324                         boolean columnRendering = child instanceof UIColumn;
    325
    326                         if (columnRendering)
    327                         {
    328                             beforeColumn(facesContext, uiData, j);
    329                         }
    330
    331                         encodeColumnChild(facesContext, writer, uiData, child,
    332                                 styles, nc * uiData.getChildCount() + j);
    333
    334                         if (columnRendering)
    335                         {
    336                             afterColumn(facesContext, uiData, j);
    337                         }
    338                     }
    339                 }
    340
    341                 if (hasNewspaperTableSpacer(uiData))
    342                 {
    343                     // draw the spacer facet
    344                     if(nc < newspaperColumns - 1)
    345                     {
    346                         renderSpacerCell(facesContext, writer, uiData);
    347                     }
    348                 }
    349             }
    350             if (rowStartRendered)
    351             {
    352                 renderRowEnd(facesContext, writer, uiData);
    353                 afterRow(facesContext, uiData);
    354             }
    355         }
    356     }
    357
    358     private void encodeInnerHtmlUnknownRowCount(FacesContext facesContext, UIComponent component)throws IOException
    359     {
    360         UIData uiData = (UIData) component;
    361         ResponseWriter writer = facesContext.getResponseWriter();
    362
    363         Styles styles = getStyles(uiData);
    364
    365         int first = uiData.getFirst();
    366         int rows = uiData.getRows();
    367         int currentRow = first;
    368
    369         while(true)
    370         {
    371             uiData.setRowIndex(currentRow);
    372             if (!uiData.isRowAvailable())
    373             {
    374                 break;
    375             }
    376
    377             // first column in table, start new row
    378             beforeRow(facesContext, uiData);
    379
    380             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    381             renderRowStart(facesContext, writer, uiData, styles, currentRow);
    382
    383             List children = null;
    384             for (int j = 0, size = getChildCount(component); j < size; j++)
    385             {
    386                 if (children == null)
    387                 {
    388                     children = getChildren(component);
    389                 }
    390                 UIComponent child = (UIComponent) children.get(j);
    391                 if (child.isRendered())
    392                 {
    393                     boolean columnRendering = child instanceof UIColumn;
    394
    395                     if (columnRendering)
    396                     {
    397                         beforeColumn(facesContext, uiData, j);
    398                     }
    399
    400                     encodeColumnChild(facesContext, writer, uiData, child,
    401                             styles, j);
    402
    403                     if (columnRendering)
    404                     {
    405                         afterColumn(facesContext, uiData, j);
    406                     }
    407                 }
    408             }
    409
    410             renderRowEnd(facesContext, writer, uiData);
    411             afterRow(facesContext, uiData);
    412
    413             currentRow++;
    414
    415             if (rows > 0 && currentRow-first > rows )
    416             {
    417                 break;
    418             }
    419         }
    420     }
    421
    422     protected void encodeColumnChild(FacesContext facesContext, ResponseWriter writer,
    423         UIData uiData, UIComponent component, Styles styles, int columnStyleIndex) throws IOException
    424     {
    425         if (component instanceof UIColumn)
    426         {
    427             renderColumnBody(facesContext, writer, uiData, component, styles, columnStyleIndex);
    428         }
    429     }
    430
    431     /**
    432      * Renders the body of a given <code>UIColumn</code> (everything but
    433      * the header and footer facets). This emits a TD cell, whose contents
    434      * are the result of calling encodeBegin, encodeChildren and
    435      * encodeEnd methods on the component (or its associated renderer).
    436      *
    437      * @param facesContext the <code>FacesContext</code>.
    438      * @param writer the <code>ResponseWriter</code>.
    439      * @param uiData the <code>UIData</code> being rendered.
    440      * @param component the <code>UIComponent</code> to render.
    441      * @param styles the styleClasses of rows and columns
    442      * @param columnStyleIndex the index of the column
    443      * @throws IOException if an exception occurs.
    444      */
    445     protected void renderColumnBody(
    446             FacesContext facesContext,
    447             ResponseWriter writer,
    448             UIData uiData,
    449             UIComponent component,
    450             Styles styles, int columnStyleIndex) throws IOException
    451     {
    452         writer.startElement(HTML.TD_ELEM, uiData);
    453         if (styles.hasColumnStyle())
    454         {
    455             writer.writeAttribute(HTML.CLASS_ATTR, styles.getColumnStyle(columnStyleIndex), null);
    456         }
    457
    458         RendererUtils.renderChild(facesContext, component);
    459         writer.endElement(HTML.TD_ELEM);
    460     }
    461
    462     /**
    463      * Renders the start of a new row of body content.
    464      * @param facesContext the <code>FacesContext</code>.
    465      * @param writer the <code>ResponseWriter</code>.
    466      * @param uiData the <code>UIData</code> being rendered.
    467      * @param styles the styleClasses of rows and columns
    468      * @param rowStyleIndex the index of the row
    469      * @throws IOException if an exceptoin occurs.
    470      */
    471     protected void renderRowStart(
    472         FacesContext facesContext,
    473         ResponseWriter writer,
    474         UIData uiData,
    475         Styles styles, int rowStyleIndex) throws IOException
    476     {
    477         writer.startElement(HTML.TR_ELEM, uiData);
    478
    479         renderRowStyle(facesContext, writer, uiData, styles, rowStyleIndex);
    480
    481         Object rowId = uiData.getAttributes().get(org.apache.myfaces.shared_orchestra.renderkit.JSFAttr.ROW_ID);
    482
    483         if (rowId != null)
    484         {
    485             writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
    486         }
    487     }
    488
    489     protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex) throws IOException
    490     {
    491         if(styles.hasRowStyle()) {
    492             String rowStyle = styles.getRowStyle(rowStyleIndex);
    493             writer.writeAttribute(HTML.CLASS_ATTR, rowStyle, null);
    494         }
    495     }
    496
    497     /**
    498      * Renders the end of a row of body content.
    499      * @param facesContext the <code>FacesContext</code>.
    500      * @param writer the <code>ResponseWriter</code>.
    501      * @param uiData the <code>UIData</code> being rendered.
    502      * @throws IOException if an exceptoin occurs.
    503      */
    504     protected void renderRowEnd(
    505         FacesContext facesContext,
    506         ResponseWriter writer,
    507         UIData uiData) throws IOException
    508     {
    509         writer.endElement(HTML.TR_ELEM);
    510     }
    511
    512     /**
    513      * Perform any operations necessary immediately before the TABLE start tag
    514      * is output.
    515      *
    516      * @param facesContext the <code>FacesContext</code>.
    517      * @param uiData the <code>UIData</code> being rendered.
    518      * @throws java.io.IOException
    519      */
    520     protected void beforeTable(FacesContext facesContext, UIData uiData) throws IOException
    521     {
    522     }
    523
    524     /**
    525      * Perform any operations necessary after TABLE start tag is output
    526      * but before the TBODY start tag.
    527      * <p>
    528      * This method generates the THEAD/TFOOT sections of a table if there
    529      * are any header or footer facets defined on the table or on any child
    530      * UIColumn component.
    531      *
    532      * @param facesContext the <code>FacesContext</code>.
    533      * @param uiData the <code>UIData</code> being rendered.
    534      */
    535     protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException
    536     {
    537         ResponseWriter writer = facesContext.getResponseWriter();
    538         renderFacet(facesContext, writer, uiData, true);
    539         renderFacet(facesContext, writer, uiData, false);
    540     }
    541
    542     /**
    543      * Perform any operations necessary immediately before each TR start tag
    544      * is output.
    545      *
    546      * @param facesContext the <code>FacesContext</code>.
    547      * @param uiData the <code>UIData</code> being rendered.
    548      */
    549     protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException
    550     {
    551     }
    552
    553     /**
    554      * Perform any operations necessary immediately after each TR end tag
    555      * is output.
    556      *
    557      * @param facesContext the <code>FacesContext</code>.
    558      * @param uiData the <code>UIData</code> being rendered.
    559      */
    560     protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException
    561     {
    562     }
    563     /**
    564      * Perform any operations necessary immediately before each column child is rendered
    565      *
    566      * @param facesContext the <code>FacesContext</code>.
    567      * @param uiData the <code>UIData</code> being rendered.
    568      * @param columnIndex the index of the currenly rendered column
    569      */
    570     protected void beforeColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException
    571     {
    572     }
    573     /**
    574      * Perform any operations necessary immediately after each column child is rendered
    575      *
    576      * @param facesContext the <code>FacesContext</code>.
    577      * @param uiData the <code>UIData</code> being rendered.
    578      * @param columnIndex the index of the currenly rendered column
    579      */
    580     protected void afterColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException
    581     {
    582     }
    583     /**
    584      *Perform any operations necessary immediately before each column child's header or footer is rendered
    585      *
    586      * @param facesContext the <code>FacesContext</code>.
    587      * @param uiData the <code>UIData</code> being rendered.
    588      * @param header true if the header of the column child is rendered
    589      * @param columnIndex the index of the currenly rendered column
    590      */
    591     protected void beforeColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header, int columnIndex) throws IOException
    592     {
    593     }
    594     /**
    595      * Perform any operations necessary immediately after each column child's header of footer is rendered
    596      *
    597      * @param facesContext the <code>FacesContext</code>.
    598      * @param uiData the <code>UIData</code> being rendered.
    599      * @param header true if the header of the column child is rendered
    600      * @param columnIndex the index of the currenly rendered column
    601      */
    602     protected void afterColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header, int columnIndex) throws IOException
    603     {
    604     }
    605
    606     /**
    607      * Perform any operations necessary in the TBODY start tag.
    608      *
    609      * @param facesContext the <code>FacesContext</code>.
    610      * @param uiData the <code>UIData</code> being rendered.
    611      */
    612     protected void inBodyStart(FacesContext facesContext, UIData uiData) throws IOException
    613     {
    614     }
    615
    616     /**
    617      * Perform any operations necessary immediately after the TBODY end tag
    618      * is output.
    619      *
    620      * @param facesContext the <code>FacesContext</code>.
    621      * @param uiData the <code>UIData</code> being rendered.
    622      */
    623     protected void afterBody(FacesContext facesContext, UIData uiData) throws IOException
    624     {
    625     }
    626
    627     /**
    628      * Perform any operations necessary immediately after the TABLE end tag
    629      * is output.
    630      *
    631      * @param facesContext the <code>FacesContext</code>.
    632      * @param uiData the <code>UIData</code> being rendered.
    633      */
    634     protected void afterTable(FacesContext facesContext, UIData uiData) throws IOException
    635     {
    636     }
    637
    638     /**
    639      * @see javax.faces.render.Renderer#encodeEnd(FacesContext, UIComponent)
    640      */
    641     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
    642     {
    643         RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);
    644
    645         endTable(facesContext, uiComponent);
    646
    647         afterTable(facesContext, (UIData) uiComponent);
    648     }
    649
    650     /**
    651      * actually render the end of the table
    652      */
    653     protected void endTable(FacesContext facesContext, UIComponent uiComponent) throws IOException
    654     {
    655         ResponseWriter writer = facesContext.getResponseWriter();
    656         writer.endElement(HTML.TABLE_ELEM);
    657         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    658     }
    659
    660     /**
    661      * Renders either the header or the footer facets for the UIData component
    662      * and all the child UIColumn components, as a THEAD or TFOOT element
    663      * containing TR (row) elements.
    664      * <p>
    665      * If there is a header or footer attached to the UIData then that is
    666      * rendered as a TR element whose COLSPAN is the sum of all rendered
    667      * columns in the table. This allows that header/footer to take up the
    668      * entire width of the table.
    669      * <p>
    670      * If any child column has a header or footer then a TR is rendered
    671      * with a TH cell for each column child.
    672      *
    673      * @param facesContext the <code>FacesContext</code>.
    674      * @param writer the <code>ResponseWriter</code>.
    675      * @param component the UIData component
    676      * @param header whether this is the header facet (if not, then the footer facet).
    677      * @throws IOException if an exception occurs.
    678      */
    679     protected void renderFacet(FacesContext facesContext, ResponseWriter writer,
    680             UIComponent component, boolean header)
    681             throws IOException
    682     {
    683         int colspan = 0;
    684         boolean hasColumnFacet = false;
    685         for (Iterator it = getChildren(component).iterator(); it.hasNext();)
    686         {
    687             UIComponent uiComponent = (UIComponent) it.next();
    688             if(uiComponent.isRendered())
    689             {
    690                 // a UIColumn has a span of 1, anything else has a span of 0
    691                 colspan += determineChildColSpan(uiComponent);
    692
    693                 // hasColumnFacet is true if *any* child column has a facet of
    694                 // the specified type.
    695                 if (!hasColumnFacet)
    696                 {
    697                      hasColumnFacet = hasFacet(header, uiComponent);
    698                 }
    699             }
    700         }
    701
    702         UIComponent facet = header ? (UIComponent) component.getFacets().get(HEADER_FACET_NAME)
    703                 : (UIComponent) component.getFacets().get(FOOTER_FACET_NAME);
    704         if (facet != null || hasColumnFacet)
    705         {
    706             // Header or Footer present on either the UIData or a column, so we
    707             // definitely need to render the THEAD or TFOOT section.
    708             String elemName = determineHeaderFooterTag(facesContext, component, header);
    709
    710             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    711             if (elemName != null)
    712             {
    713                 writer.startElement(elemName, component);
    714             }
    715             if (header)
    716             {
    717                 String headerStyleClass = getHeaderClass(component);
    718                 if (facet != null)
    719                     renderTableHeaderRow(facesContext, writer, component, facet, headerStyleClass, colspan);
    720                 if (hasColumnFacet)
    721                     renderColumnHeaderRow(facesContext, writer, component, headerStyleClass);
    722             }
    723             else
    724             {
    725                 String footerStyleClass = getFooterClass(component);
    726                 if (hasColumnFacet)
    727                     renderColumnFooterRow(facesContext, writer, component, footerStyleClass);
    728                 if (facet != null)
    729                     renderTableFooterRow(facesContext, writer, component, facet, footerStyleClass, colspan);
    730             }
    731             if (elemName != null)
    732             {
    733                 writer.endElement(elemName);
    734             }
    735         }
    736     }
    737
    738     protected String determineHeaderFooterTag(FacesContext facesContext, UIComponent component, boolean header)
    739     {
    740         return header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM;
    741     }
    742
    743     /**
    744      * @param header
    745      * @param uiComponent
    746      * @return boolean
    747      */
    748     protected boolean hasFacet(boolean header, UIComponent uiComponent)
    749     {
    750         if (uiComponent instanceof UIColumn)
    751         {
    752             UIColumn uiColumn = (UIColumn) uiComponent;
    753             return header ? uiColumn.getHeader() != null : uiColumn.getFooter() != null;
    754         }
    755         return false;
    756     }
    757
    758     /**
    759      * Calculate the number of columns the specified child component will span
    760      * when rendered.
    761      * <p>
    762      * Normally, this is a fairly simple calculation: a UIColumn component
    763      * is rendered as one column, every other child type is not rendered
    764      * (ie spans zero columns). However custom subclasses of this renderer may
    765      * override this method to handle cases where a single component renders
    766      * as multiple columns.
    767      */
    768     protected int determineChildColSpan(UIComponent uiComponent)
    769     {
    770         if (uiComponent instanceof UIColumn)
    771         {
    772             return 1;
    773         }
    774         return 0;
    775     }
    776
    777     /**
    778      * Renders the header row of the table being rendered.
    779      * @param facesContext the <code>FacesContext</code>.
    780      * @param writer the <code>ResponseWriter</code>.
    781      * @param component the <code>UIComponent</code> for whom a table is being rendered.
    782      * @param headerFacet the facet for the header.
    783      * @param headerStyleClass the styleClass of the header.
    784      * @param colspan the number of columns the header should span.  Typically, this is
    785      * the number of columns in the table.
    786      * @throws IOException if an exception occurs.
    787      */
    788     protected void renderTableHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
    789             UIComponent headerFacet, String headerStyleClass, int colspan) throws IOException
    790     {
    791         renderTableHeaderOrFooterRow(facesContext, writer, component, headerFacet, headerStyleClass, determineHeaderCellTag(facesContext, component),
    792                 colspan);
    793     }
    794
    795     /**
    796      * Renders the footer row of the table being rendered.
    797      * @param facesContext the <code>FacesContext</code>.
    798      * @param writer the <code>ResponseWriter</code>.
    799      * @param component the <code>UIComponent</code> for whom a table is being rendered.
    800      * @param footerFacet the facet for the footer.
    801      * @param footerStyleClass the styleClass of the footer.
    802      * @param colspan the number of columns the header should span.  Typically, this is
    803      * the number of columns in the table.
    804      * @throws IOException if an exception occurs.
    805      */
    806     protected void renderTableFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
    807             UIComponent footerFacet, String footerStyleClass, int colspan) throws IOException
    808     {
    809         renderTableHeaderOrFooterRow(facesContext, writer, component, footerFacet, footerStyleClass, HTML.TD_ELEM,
    810                 colspan);
    811     }
    812
    813     /**
    814      * Renders the header row for the columns, which is a separate row from the header row for the
    815      * <code>UIData</code> header facet.
    816      *
    817      * @param facesContext the <code>FacesContext</code>.
    818      * @param writer the <code>ResponseWriter</code>.
    819      * @param component the UIData component for whom a table is being rendered.
    820      * @param headerStyleClass the styleClass of the header
    821      * @throws IOException if an exception occurs.
    822      */
    823     protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
    824             String headerStyleClass) throws IOException
    825     {
    826         renderColumnHeaderOrFooterRow(facesContext, writer, component, headerStyleClass, true);
    827     }
    828
    829     /**
    830      * Renders the footer row for the columns, which is a separate row from the footer row for the
    831      * <code>UIData</code> footer facet.
    832      * @param facesContext the <code>FacesContext</code>.
    833      * @param writer the <code>ResponseWriter</code>.
    834      * @param component the <code>UIComponent</code> for whom a table is being rendered.
    835      * @param footerStyleClass the styleClass of the footerStyleClass
    836      * @throws IOException if an exception occurs.
    837      */
    838     protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
    839             String footerStyleClass) throws IOException
    840     {
    841         renderColumnHeaderOrFooterRow(facesContext, writer, component, footerStyleClass, false);
    842     }
    843
    844     protected void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component,
    845             UIComponent facet, String styleClass, String colElementName, int colspan) throws IOException
    846     {
    847         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    848         writer.startElement(HTML.TR_ELEM, component);
    849         writer.startElement(colElementName, component);
    850         if (colElementName.equals(determineHeaderCellTag(facesContext, component)))
    851         {
    852             writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null);
    853         }
    854
    855         // span all the table's columns
    856         int newsPaperColumns = getNewspaperColumns(component);
    857         int totalColumns = colspan * newsPaperColumns;
    858         if(hasNewspaperTableSpacer(component))
    859         {
    860             totalColumns = totalColumns + newsPaperColumns - 1;
    861         }
    862         writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(totalColumns), null);
    863         if (styleClass != null)
    864         {
    865             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
    866         }
    867         if (facet != null)
    868         {
    869             RendererUtils.renderChild(facesContext, facet);
    870         }
    871         writer.endElement(colElementName);
    872         writer.endElement(HTML.TR_ELEM);
    873     }
    874
    875     /**
    876      * @param component the UIData component for whom a table is being rendered.
    877      */
    878     private void renderColumnHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer,
    879             UIComponent component, String styleClass, boolean header) throws IOException
    880     {
    881         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
    882
    883         writer.startElement(HTML.TR_ELEM, component);
    884         int columnIndex = 0;
    885         int newspaperColumns = getNewspaperColumns(component);
    886         for(int nc = 0; nc < newspaperColumns; nc++)
    887         {
    888             for (Iterator it = getChildren(component).iterator(); it.hasNext();)
    889             {
    890                 UIComponent uiComponent = (UIComponent) it.next();
    891                 if (uiComponent.isRendered())
    892                 {
    893                     if (component instanceof UIData && uiComponent instanceof UIColumn)
    894                         beforeColumnHeaderOrFooter(facesContext, (UIData)component, header, columnIndex);
    895
    896                     renderColumnChildHeaderOrFooterRow(facesContext, writer, uiComponent, styleClass, header);
    897
    898                     if (component instanceof UIData && uiComponent instanceof UIColumn)
    899                         afterColumnHeaderOrFooter(facesContext, (UIData)component, header, columnIndex);
    900                 }
    901                 columnIndex += 1;
    902             }
    903
    904             if (hasNewspaperTableSpacer(component))
    905             {
    906                 // draw the spacer facet
    907                 if(nc < newspaperColumns - 1) renderSpacerCell(facesContext, writer, component);
    908             }
    909         }
    910         writer.endElement(HTML.TR_ELEM);
    911     }
    912
    913       /**
    914       * Renders a spacer between adjacent newspaper columns.
    915       */
    916     protected void renderSpacerCell(FacesContext facesContext, ResponseWriter writer, UIComponent component) throws IOException {
    917         UIComponent spacer = getNewspaperTableSpacer(component);
    918         if(spacer == null) return;
    919
    920          writer.startElement(HTML.TD_ELEM, component);
    921          RendererUtils.renderChild(facesContext, spacer);
    922          writer.endElement(HTML.TD_ELEM);
    923      }
    924
    925     protected void renderColumnChildHeaderOrFooterRow(FacesContext facesContext,
    926         ResponseWriter writer, UIComponent uiComponent, String styleClass, boolean header) throws IOException
    927     {
    928         if (uiComponent instanceof UIColumn)
    929         {
    930             if (header)
    931             {
    932                 renderColumnHeaderCell(facesContext, writer, uiComponent,
    933                     ((UIColumn) uiComponent).getHeader(), styleClass, 0);
    934             }
    935             else
    936             {
    937                 renderColumnFooterCell(facesContext, writer, uiComponent,
    938                     ((UIColumn) uiComponent).getFooter(), styleClass, 0);
    939             }
    940         }
    941     }
    942
    943     /**
    944      * Renders the header facet for the given <code>UIColumn</code>.
    945      * @param facesContext the <code>FacesContext</code>.
    946      * @param writer the <code>ResponseWriter</code>.
    947      * @param uiColumn the <code>UIColumn</code>.
    948      * @param headerStyleClass the styleClass of the header facet.
    949      * @param colspan the colspan for the tableData element in which the header facet
    950      * will be wrapped.
    951      * @throws IOException
    952      */
    953     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
    954         String headerStyleClass, int colspan) throws IOException
    955     {
    956         renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn.getHeader(), headerStyleClass, colspan);
    957     }
    958
    959     /**
    960      * Renders a TH cell within a TR within a THEAD section. If the specified
    961      * UIColumn object does have a header facet, then that facet is rendered
    962      * within the cell, otherwise the cell is left blank (though any specified
    963      * style class is still applied to empty cells).
    964      *
    965      * @param facesContext the <code>FacesContext</code>.
    966      * @param writer the <code>ResponseWriter</code>.
    967      * @param uiComponent the <code>UIComponent</code> to render the facet for.
    968      * @param facet the <code>UIComponent</code> to render as facet.
    969      * @param headerStyleClass the styleClass of the header facet.
    970      * @param colspan the colspan for the tableData element in which the header facet
    971      * will be wrapped.
    972      * @throws IOException
    973      */
    974     protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
    975             UIComponent facet, String headerStyleClass, int colspan) throws IOException
    976     {
    977         writer.startElement(determineHeaderCellTag(facesContext, uiComponent.getParent()), uiComponent);
    978         if (colspan > 1)
    979         {
    980             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
    981         }
    982         if (headerStyleClass != null)
    983         {
    984             writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass, null);
    985         }
    986         if (facet != null)
    987         {
    988             RendererUtils.renderChild(facesContext, facet);
    989         }
    990         writer.endElement(determineHeaderCellTag(facesContext, uiComponent.getParent()));
    991     }
    992
    993     protected String determineHeaderCellTag(FacesContext facesContext, UIComponent uiComponent)
    994     {
    995         return HTML.TH_ELEM;
    996     }
    997
    998     /**
    999      * Renders the footer facet for the given <code>UIColumn</code>.
    1000      * @param facesContext the <code>FacesContext</code>.
    1001      * @param writer the <code>ResponseWriter</code>.
    1002      * @param uiColumn the <code>UIComponent</code>.
    1003      * @param footerStyleClass the styleClass of the footer facet.
    1004      * @param colspan the colspan for the tableData element in which the footer facet
    1005      * will be wrapped.
    1006      * @throws IOException
    1007      */
    1008     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn,
    1009         String footerStyleClass, int colspan) throws IOException
    1010     {
    1011       renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn.getFooter(), footerStyleClass, colspan);
    1012     }
    1013
    1014     /**
    1015      * Renders the footer facet for the given <code>UIColumn</code>.
    1016      * @param facesContext the <code>FacesContext</code>.
    1017      * @param writer the <code>ResponseWriter</code>.
    1018      * @param uiComponent the <code>UIComponent</code> to render the facet for.
    1019      * @param facet the <code>UIComponent</code> to render as facet.
    1020      * @param footerStyleClass the styleClass of the footer facet.
    1021      * @param colspan the colspan for the tableData element in which the footer facet
    1022      * will be wrapped.
    1023      * @throws IOException
    1024      */
    1025     protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
    1026         UIComponent facet, String footerStyleClass, int colspan) throws IOException
    1027     {
    1028         writer.startElement(HTML.TD_ELEM, uiComponent);
    1029         if (colspan > 1)
    1030         {
    1031             writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(colspan), null);
    1032         }
    1033         if (footerStyleClass != null)
    1034         {
    1035             writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null);
    1036         }
    1037         if (facet != null)
    1038         {
    1039             RendererUtils.renderChild(facesContext, facet);
    1040         }
    1041         writer.endElement(HTML.TD_ELEM);
    1042     }
    1043
    1044     /**
    1045      * Gets the headerClass attribute of the given <code>UIComponent</code>.
    1046      * @param component the <code>UIComponent</code>.
    1047      * @return the headerClass attribute of the given <code>UIComponent</code>.
    1048      */
    1049     protected static String getHeaderClass(UIComponent component)
    1050     {
    1051         if (component instanceof HtmlDataTable)
    1052         {
    1053             return ((HtmlDataTable) component).getHeaderClass();
    1054         }
    1055         else
    1056         {
    1057             return (String) component.getAttributes().get(org.apache.myfaces.shared_orchestra.renderkit.JSFAttr.HEADER_CLASS_ATTR);
    1058         }
    1059     }
    1060
    1061     /**
    1062      * Gets the footerClass attribute of the given <code>UIComponent</code>.
    1063      * @param component the <code>UIComponent</code>.
    1064      * @return the footerClass attribute of the given <code>UIComponent</code>.
    1065      */
    1066     protected static String getFooterClass(UIComponent component)
    1067     {
    1068         if (component instanceof HtmlDataTable)
    1069         {
    1070             return ((HtmlDataTable) component).getFooterClass();
    1071         }
    1072         else
    1073         {
    1074             return (String) component.getAttributes().get(org.apache.myfaces.shared_orchestra.renderkit.JSFAttr.FOOTER_CLASS_ATTR);
    1075         }
    1076     }
    1077
    1078     public void decode(FacesContext context, UIComponent component)
    1079     {
    1080         super.decode(context, component);
    1081     }
    1082
    1083 }