All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.renderkit.html.base.HtmlTableRendererBase Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.renderkit.html.base;

import org.apache.myfaces.renderkit.html.util.HtmlRendererUtils;
import org.apache.myfaces.renderkit.html.util.ClientBehaviorRendererUtils;
import org.apache.myfaces.renderkit.html.util.CommonHtmlAttributesUtil;
import org.apache.myfaces.renderkit.html.util.CommonHtmlEventsUtil;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import jakarta.faces.component.UIColumn;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIData;
import jakarta.faces.component.behavior.ClientBehavior;
import jakarta.faces.component.behavior.ClientBehaviorHolder;
import jakarta.faces.component.html.HtmlColumn;
import jakarta.faces.component.html.HtmlDataTable;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;

import org.apache.myfaces.renderkit.RendererUtils;
import org.apache.myfaces.renderkit.html.util.ResourceUtils;
import org.apache.myfaces.util.lang.ArrayUtils;
import org.apache.myfaces.util.lang.StringUtils;
import org.apache.myfaces.renderkit.html.util.HTML;
import org.apache.myfaces.renderkit.html.util.ComponentAttrs;

/**
 * Common methods for renderers for components that subclass the standard
 * Faces HtmlDataTable component.
 */
public class HtmlTableRendererBase extends HtmlRenderer
{
    protected static final String HEADER_FACET_NAME = "header";
    protected static final String FOOTER_FACET_NAME = "footer";
    protected static final String CAPTION_FACET_NAME = "caption";
    
    private static final Logger log = Logger.getLogger(HtmlTableRendererBase.class.getName());
    
    private static final Integer[] ZERO_INT_ARRAY = new Integer[]{0};

    /**
     * @param component dataTable
     * @return number of layout columns
     */
    protected int getNewspaperColumns(UIComponent component)
    {
        return 1;
    }

    /**
     * @param component dataTable
     * @return component to display between layout columns
     */
    protected UIComponent getNewspaperTableSpacer(UIComponent component)
    {
        return null;
    }

    /**
     * @param component dataTable
     * @return whether dataTable has component to display between layout columns
     */
    protected boolean hasNewspaperTableSpacer(UIComponent component)
    {
        return false;
    }

    /**
     * @param component dataTable
     * @return whether dataTable has newspaper columns layed out horizontally
     */
    protected boolean isNewspaperHorizontalOrientation(UIComponent component)
    {
        return false;
    }

    @Override
    public boolean getRendersChildren()
    {
        return true;
    }

    /**
     * Render the necessary bits that come before any actual rows in the table.
     * 
     * @see jakarta.faces.render.Renderer#encodeBegin(FacesContext, UIComponent)
     */
    @Override
    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
    {
        RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class);

        if (uiComponent instanceof ClientBehaviorHolder)
        {
            Map> behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
            if (!behaviors.isEmpty())
            {
                ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
            }
        }
        
        beforeTable(facesContext, (UIData) uiComponent);

        startTable(facesContext, uiComponent);
    }

    /**
     * actually render the start of the table
     */
    protected void startTable(FacesContext facesContext, UIComponent uiComponent) throws IOException
    {
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.startElement(HTML.TABLE_ELEM, uiComponent);
        
        Map> behaviors = null;
        if (uiComponent instanceof ClientBehaviorHolder)
        {
            behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
            if (!behaviors.isEmpty())
            {
                HtmlRendererUtils.writeIdAndName(writer, uiComponent, facesContext);
            }
            else
            {
                HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
            }
            if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
            {
                CommonHtmlAttributesUtil.renderEventProperties(writer, 
                        CommonHtmlAttributesUtil.getMarkedAttributes(uiComponent), uiComponent);
            }
            else
            {
                if (isCommonEventsOptimizationEnabled(facesContext))
                {
                    CommonHtmlEventsUtil.renderBehaviorizedEventHandlers(facesContext, writer, 
                           CommonHtmlAttributesUtil.getMarkedAttributes(uiComponent),
                           CommonHtmlEventsUtil.getMarkedEvents(uiComponent), uiComponent, behaviors);
                }
                else
                {
                    HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
                }
            }
            if (isCommonPropertiesOptimizationEnabled(facesContext))
            {
                HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_ATTRIBUTES);
                CommonHtmlAttributesUtil.renderCommonPassthroughPropertiesWithoutEvents(writer, 
                        CommonHtmlAttributesUtil.getMarkedAttributes(uiComponent), uiComponent);
            }
            else
            {
                HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
                        HTML.TABLE_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
            }
        }
        else
        {
            HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
            if (isCommonPropertiesOptimizationEnabled(facesContext))
            {
                HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.TABLE_ATTRIBUTES);
                CommonHtmlAttributesUtil.renderCommonPassthroughProperties(writer, 
                        CommonHtmlAttributesUtil.getMarkedAttributes(uiComponent), uiComponent);
            }
            else
            {
                HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
                        HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
            }
        }
    }

    /**
     * Render the TBODY section of the html table. See also method encodeInnerHtml.
     * 
     * @see jakarta.faces.render.Renderer#encodeChildren(FacesContext, UIComponent)
     */
    @Override
    public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
    {
        RendererUtils.checkParamValidity(facesContext, component, UIData.class);

        beforeBody(facesContext, (UIData) component);

        encodeInnerHtml(facesContext, component);

        afterBody(facesContext, (UIData) component);
    }

    /**
     * Renders the caption facet.
     * @param facesContext the FacesContext.
     * @param writer the ResponseWriter.
     * @param component the parent UIComponent containing the facets.
     * @throws IOException if an exception occurs.
     */
    protected void renderCaptionFacet(FacesContext facesContext, ResponseWriter writer, UIComponent component)
            throws IOException
    {
        HtmlRendererUtils.renderTableCaption(facesContext, writer, component);
    }  
    
    /**
     * Renders the colgroups facet.
     * @param facesContext the FacesContext.
     * @param writer the ResponseWriter.
     * @param component the parent UIComponent containing the facets.
     * @throws IOException if an exception occurs.
     * @since 2.0
     */
    protected void renderColgroupsFacet(FacesContext facesContext, ResponseWriter writer, UIComponent component)
            throws IOException
    {
        UIComponent colgroupsFacet = component.getFacet("colgroups");
        if (colgroupsFacet == null)
        {
            // no facet to be rendered
            return;
        }
        // render the facet
        //RendererUtils.renderChild(facesContext, colgroupsFacet);
        colgroupsFacet.encodeAll(facesContext);
    } 
    
    /**
     * Gets styles for the specified component.
     */
    protected static Styles getStyles(UIData uiData)
    {
        String rowClasses;
        String columnClasses;
        if(uiData instanceof HtmlDataTable) 
        {
            rowClasses = ((HtmlDataTable)uiData).getRowClasses();
            columnClasses = ((HtmlDataTable)uiData).getColumnClasses();
        }
        else
        {
            rowClasses = (String)uiData.getAttributes().get(ComponentAttrs.ROW_CLASSES_ATTR);
            columnClasses = (String)uiData.getAttributes().get(ComponentAttrs.COLUMN_CLASSES_ATTR);
        }
        return new Styles(rowClasses, columnClasses);
    }

    /**
     * Class manages the styles from String lists.
     */
    protected static class Styles
    {
        private String[] _columnStyle;
        private String[] _rowStyle;

        Styles(String rowStyles, String columnStyles)
        {
            _rowStyle = (rowStyles == null)
                ? ArrayUtils.EMPTY_STRING_ARRAY
                : StringUtils.trim(
                    StringUtils.splitShortString(rowStyles, ','));
            _columnStyle = (columnStyles == null)
                ? ArrayUtils.EMPTY_STRING_ARRAY
                : StringUtils.trim(
                    StringUtils.splitShortString(columnStyles, ','));
        }

        public String getRowStyle(int idx)
        {
            if (!hasRowStyle())
            {
                return null;
            }
            return _rowStyle[idx % _rowStyle.length];
        }

        public String getColumnStyle(int idx)
        {
            if (!hasColumnStyle())
            {
                return null;
            }
            //return _columnStyle[idx % _columnStyle.length];
            if (idx < _columnStyle.length)
            {
                return _columnStyle[idx];
            }
            return null;   
        }

        public boolean hasRowStyle()
        {
            return _rowStyle.length > 0;
        }

        public boolean hasColumnStyle()
        {
            return _columnStyle.length > 0;
        }
    }

    private Integer[] getBodyRows(FacesContext facesContext, UIComponent component)
    {
        Integer[] bodyrows = null;
        String bodyrowsAttr = (String) component.getAttributes().get(ComponentAttrs.BODYROWS_ATTR);
        if (bodyrowsAttr != null && !bodyrowsAttr.isEmpty())
        {   
            String[] bodyrowsString = StringUtils.trim(StringUtils.splitShortString(bodyrowsAttr, ','));
            // parsing with no exception handling, because of Faces-spec: 
            // "If present, this must be a comma separated list of integers."
            bodyrows = new Integer[bodyrowsString.length];
            for(int i = 0; i < bodyrowsString.length; i++) 
            {
                bodyrows[i] = Integer.valueOf(bodyrowsString[i]);
            }
            
        }
        else
        {
            bodyrows = ZERO_INT_ARRAY;
        }
        return bodyrows;
    }

    /**
     * Renders everything inside the TBODY tag by iterating over the row objects
     * between offsets first and first+rows and applying the UIColumn components
     * to those objects. 
     * 

* This method is separated from the encodeChildren so that it can be overridden by * subclasses. One class that uses this functionality is autoUpdateDataTable. */ public void encodeInnerHtml(FacesContext facesContext, UIComponent component)throws IOException { UIData uiData = (UIData) component; ResponseWriter writer = facesContext.getResponseWriter(); int rowCount = uiData.getRowCount(); int newspaperColumns = getNewspaperColumns(component); if (rowCount == -1 && newspaperColumns == 1) { encodeInnerHtmlUnknownRowCount(facesContext, component); return; } if (rowCount == 0) { //nothing to render, to get valid xhtml we render an empty dummy row writer.startElement(HTML.TBODY_ELEM, null); // uiData); writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null); writer.startElement(HTML.TR_ELEM, null); // uiData); writer.startElement(HTML.TD_ELEM, null); // uiData); writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); writer.endElement(HTML.TBODY_ELEM); return; } // begin the table // get the CSS styles Styles styles = getStyles(uiData); int first = uiData.getFirst(); int rows = uiData.getRows(); int last; if (rows <= 0) { last = rowCount; } else { last = first + rows; if (last > rowCount) { last = rowCount; } } int newspaperRows; if((last - first) % newspaperColumns == 0) { newspaperRows = (last - first) / newspaperColumns; } else { newspaperRows = ((last - first) / newspaperColumns) + 1; } boolean newspaperHorizontalOrientation = isNewspaperHorizontalOrientation(component); // get the row indizes for which a new TBODY element should be created Integer[] bodyrows = getBodyRows(facesContext, component); int bodyrowsCount = 0; // walk through the newspaper rows for(int nr = 0; nr < newspaperRows; nr++) { boolean rowStartRendered = false; // walk through the newspaper columns for(int nc = 0; nc < newspaperColumns; nc++) { // the current row in the 'real' table int currentRow; if (newspaperHorizontalOrientation) { currentRow = nr * newspaperColumns + nc + first; } else { currentRow = nc * newspaperRows + nr + first; } // if this row is not to be rendered if(currentRow >= last) { continue; } // bail if any row does not exist uiData.setRowIndex(currentRow); if(!uiData.isRowAvailable()) { log.severe("Row is not available. Rowindex = " + currentRow); break; } if (nc == 0) { // first column in table, start new row beforeRow(facesContext, uiData); // is the current row listed in the bodyrows attribute if(ArrayUtils.contains(bodyrows, currentRow)) { // close any preopened TBODY element first if(bodyrowsCount != 0) { writer.endElement(HTML.TBODY_ELEM); } writer.startElement(HTML.TBODY_ELEM, null); // uiData); // Do not attach bodyrowsCount to the first TBODY element, because of backward compatibility writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + (bodyrowsCount == 0 ? "" : bodyrowsCount), null); bodyrowsCount++; } renderRowStart(facesContext, writer, uiData, styles, nr); rowStartRendered = true; } List children = null; int columnStyleIndex = 0; for (int j = 0, size = getChildCount(component); j < size; j++) { if (children == null) { children = getChildren(component); } UIComponent child = (UIComponent) children.get(j); if (child.isRendered()) { boolean columnRendering = child instanceof UIColumn; if (columnRendering) { beforeColumn(facesContext, uiData, columnStyleIndex); } encodeColumnChild(facesContext, writer, uiData, child, styles, nc * uiData.getChildCount() + columnStyleIndex); if (columnRendering) { afterColumn(facesContext, uiData, columnStyleIndex); } columnStyleIndex = columnStyleIndex + getColumnCountForComponent(facesContext, uiData, child); } } if (hasNewspaperTableSpacer(uiData)) { // draw the spacer facet if(nc < newspaperColumns - 1) { renderSpacerCell(facesContext, writer, uiData); } } } if (rowStartRendered) { renderRowEnd(facesContext, writer, uiData); afterRow(facesContext, uiData); } } if(bodyrowsCount != 0) { // close the last TBODY element writer.endElement(HTML.TBODY_ELEM); } } private void encodeInnerHtmlUnknownRowCount(FacesContext facesContext, UIComponent component)throws IOException { UIData uiData = (UIData) component; ResponseWriter writer = facesContext.getResponseWriter(); Styles styles = getStyles(uiData); Integer[] bodyrows = getBodyRows(facesContext, component); int bodyrowsCount = 0; int first = uiData.getFirst(); int rows = uiData.getRows(); int currentRow = first; boolean isRowRendered = false; while(true) { uiData.setRowIndex(currentRow); if (!uiData.isRowAvailable()) { break; } isRowRendered = true; // first column in table, start new row beforeRow(facesContext, uiData); // is the current row listed in the bodyrows attribute if(ArrayUtils.contains(bodyrows, currentRow)) { // close any preopened TBODY element first if(bodyrowsCount != 0) { writer.endElement(HTML.TBODY_ELEM); } writer.startElement(HTML.TBODY_ELEM, null); // uiData); // Do not attach bodyrowsCount to the first TBODY element, because of backward compatibility writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + (bodyrowsCount == 0 ? "" : bodyrowsCount), null); bodyrowsCount++; } renderRowStart(facesContext, writer, uiData, styles, currentRow); List children = null; int columnStyleIndex = 0; for (int j = 0, size = getChildCount(component); j < size; j++) { if (children == null) { children = getChildren(component); } UIComponent child = children.get(j); if (child.isRendered()) { boolean columnRendering = child instanceof UIColumn; if (columnRendering) { beforeColumn(facesContext, uiData, columnStyleIndex); } encodeColumnChild(facesContext, writer, uiData, child, styles, columnStyleIndex); if (columnRendering) { afterColumn(facesContext, uiData, columnStyleIndex); } columnStyleIndex = columnStyleIndex + getColumnCountForComponent(facesContext, uiData, child); } } renderRowEnd(facesContext, writer, uiData); afterRow(facesContext, uiData); currentRow++; if (rows > 0 && currentRow-first > rows ) { break; } } if (!isRowRendered) { //nothing to render, to get valid xhtml we render an empty dummy row writer.startElement(HTML.TBODY_ELEM, null); // uiData); writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null); writer.startElement(HTML.TR_ELEM, null); // uiData); writer.startElement(HTML.TD_ELEM, null); // uiData); writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); writer.endElement(HTML.TBODY_ELEM); return; } if(bodyrowsCount != 0) { // close the last TBODY element writer.endElement(HTML.TBODY_ELEM); } } protected void encodeColumnChild(FacesContext facesContext, ResponseWriter writer, UIData uiData, UIComponent component, Styles styles, int columnStyleIndex) throws IOException { if (component instanceof UIColumn) { renderColumnBody(facesContext, writer, uiData, component, styles, columnStyleIndex); } } /** * Renders the body of a given UIColumn (everything but * the header and footer facets). This emits a TD cell, whose contents * are the result of calling encodeBegin, encodeChildren and * encodeEnd methods on the component (or its associated renderer). * * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiData the UIData being rendered. * @param component the UIComponent to render. * @throws IOException if an exception occurs. */ protected void renderColumnBody( FacesContext facesContext, ResponseWriter writer, UIData uiData, UIComponent component, Styles styles, int columnStyleIndex) throws IOException { // Get the rowHeader attribute from the attribute map, because of MYFACES-1790 Object rowHeaderAttr = component.getAttributes().get(ComponentAttrs.ROW_HEADER_ATTR); boolean rowHeader = rowHeaderAttr != null && ((Boolean) rowHeaderAttr); if(rowHeader) { writer.startElement(HTML.TH_ELEM, null); // uiData); writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_ROW_VALUE, null); } else { writer.startElement(HTML.TD_ELEM, null); // uiData); } String styleClass = null; if (component instanceof HtmlColumn) { styleClass = ((HtmlColumn) component).getStyleClass(); } if (styles.hasColumnStyle()) { if (styleClass == null) { styleClass = styles.getColumnStyle(columnStyleIndex); } else { styleClass = styleClass+ ' ' +styles.getColumnStyle(columnStyleIndex); } } if (styleClass != null) { writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null); } //RendererUtils.renderChild(facesContext, component); component.encodeAll(facesContext); if(rowHeader) { writer.endElement(HTML.TH_ELEM); } else { writer.endElement(HTML.TD_ELEM); } } /** * Renders the start of a new row of body content. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiData the UIData being rendered. * @throws IOException if an exceptoin occurs. */ protected void renderRowStart( FacesContext facesContext, ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex) throws IOException { writer.startElement(HTML.TR_ELEM, null); // uiData); renderRowStyle(facesContext, writer, uiData, styles, rowStyleIndex); Object rowId = uiData.getAttributes().get(ComponentAttrs.ROW_ID); if (rowId != null) { writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null); } } protected void renderRowStyle(FacesContext facesContext, ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex) throws IOException { String rowClass = null; if (uiData instanceof HtmlDataTable) { rowClass = ((HtmlDataTable) uiData).getRowClass(); } if (styles.hasRowStyle()) { if (rowClass == null) { rowClass = styles.getRowStyle(rowStyleIndex); } else { rowClass = rowClass + ' ' + styles.getRowStyle(rowStyleIndex); } } if (rowClass != null) { writer.writeAttribute(HTML.CLASS_ATTR, rowClass, null); } } /** * Renders the end of a row of body content. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiData the UIData being rendered. * @throws IOException if an exceptoin occurs. */ protected void renderRowEnd( FacesContext facesContext, ResponseWriter writer, UIData uiData) throws IOException { writer.endElement(HTML.TR_ELEM); } /** * Perform any operations necessary immediately before the TABLE start tag * is output. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void beforeTable(FacesContext facesContext, UIData uiData) throws IOException { } /** * Perform any operations necessary after TABLE start tag is output * but before the TBODY start tag. *

* This method generates the THEAD/TFOOT sections of a table if there * are any header or footer facets defined on the table or on any child * UIColumn component. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException { ResponseWriter writer = facesContext.getResponseWriter(); renderCaptionFacet(facesContext, writer, uiData); renderColgroupsFacet(facesContext, writer, uiData); renderFacet(facesContext, writer, uiData, true); renderFacet(facesContext, writer, uiData, false); } /** * Perform any operations necessary immediately before each TR start tag * is output. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void beforeRow(FacesContext facesContext, UIData uiData) throws IOException { } /** * Perform any operations necessary immediately after each TR end tag * is output. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void afterRow(FacesContext facesContext, UIData uiData) throws IOException { } /** * Perform any operations necessary immediately before each column child is rendered * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. * @param columnIndex the index of the currenly rendered column */ protected void beforeColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException { } /** * Perform any operations necessary immediately after each column child is rendered * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. * @param columnIndex the index of the currenly rendered column */ protected void afterColumn(FacesContext facesContext, UIData uiData, int columnIndex) throws IOException { } /** * Indicates the number of columns the component represents. By default each UIColumn instance * is 1 column * @param facesContext * @param uiData * @param child * @return */ protected int getColumnCountForComponent(FacesContext facesContext, UIData uiData, UIComponent child) { if (child instanceof UIColumn) { return 1; } return 0; } /** *Perform any operations necessary immediately before each column child's header or footer is rendered * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. * @param header true if the header of the column child is rendered * @param columnIndex the index of the currenly rendered column */ protected void beforeColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header, int columnIndex) throws IOException { } /** * Perform any operations necessary immediately after each column child's header of footer is rendered * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. * @param header true if the header of the column child is rendered * @param columnIndex the index of the currenly rendered column */ protected void afterColumnHeaderOrFooter(FacesContext facesContext, UIData uiData, boolean header, int columnIndex) throws IOException { } /** * Perform any operations necessary in the TBODY start tag. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void inBodyStart(FacesContext facesContext, UIData uiData) throws IOException { } /** * Perform any operations necessary immediately after the TBODY end tag * is output. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void afterBody(FacesContext facesContext, UIData uiData) throws IOException { } /** * Perform any operations necessary immediately after the TABLE end tag * is output. * * @param facesContext the FacesContext. * @param uiData the UIData being rendered. */ protected void afterTable(FacesContext facesContext, UIData uiData) throws IOException { } /** * @see jakarta.faces.render.Renderer#encodeEnd(FacesContext, UIComponent) */ public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException { RendererUtils.checkParamValidity(facesContext, uiComponent, UIData.class); endTable(facesContext, uiComponent); afterTable(facesContext, (UIData) uiComponent); } /** * actually render the end of the table */ protected void endTable(FacesContext facesContext, UIComponent uiComponent) throws IOException { ResponseWriter writer = facesContext.getResponseWriter(); writer.endElement(HTML.TABLE_ELEM); } /** * Renders either the header or the footer facets for the UIData component * and all the child UIColumn components, as a THEAD or TFOOT element * containing TR (row) elements. *

* If there is a header or footer attached to the UIData then that is * rendered as a TR element whose COLSPAN is the sum of all rendered * columns in the table. This allows that header/footer to take up the * entire width of the table. *

* If any child column has a header or footer then a TR is rendered * with a TH cell for each column child. * * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param component the UIData component * @param header whether this is the header facet (if not, then the footer facet). * @throws IOException if an exception occurs. */ protected void renderFacet(FacesContext facesContext, ResponseWriter writer, UIComponent component, boolean header) throws IOException { int colspan = 0; boolean hasColumnFacet = false; int childCount = component.getChildCount(); for (int i = 0; i < childCount; i++) { UIComponent uiComponent = component.getChildren().get(i); if(uiComponent.isRendered()) { // a UIColumn has a span of 1, anything else has a span of 0 colspan += determineChildColSpan(uiComponent); // hasColumnFacet is true if *any* child column has a facet of // the specified type. if (!hasColumnFacet) { hasColumnFacet = hasFacet(header, uiComponent); } } } UIComponent facet = null; if (component.getFacetCount() > 0) { facet = header ? component.getFacets().get(HEADER_FACET_NAME) : component.getFacets().get(FOOTER_FACET_NAME); } if (facet != null || hasColumnFacet) { // Header or Footer present on either the UIData or a column, so we // definitely need to render the THEAD or TFOOT section. String elemName = determineHeaderFooterTag(facesContext, component, header); if (elemName != null) { writer.startElement(elemName, null); // component); } if (header) { String headerStyleClass = getHeaderClass(component); if (facet != null) { renderTableHeaderRow(facesContext, writer, component, facet, headerStyleClass, colspan); } if (hasColumnFacet) { renderColumnHeaderRow(facesContext, writer, component, headerStyleClass); } } else { String footerStyleClass = getFooterClass(component); if (hasColumnFacet) { renderColumnFooterRow(facesContext, writer, component, footerStyleClass); } if (facet != null) { renderTableFooterRow(facesContext, writer, component, facet, footerStyleClass, colspan); } } if (elemName != null) { writer.endElement(elemName); } } } protected String determineHeaderFooterTag(FacesContext facesContext, UIComponent component, boolean header) { return header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM; } /** * @param header * @param uiComponent * @return boolean */ protected boolean hasFacet(boolean header, UIComponent uiComponent) { if (uiComponent instanceof UIColumn) { UIColumn uiColumn = (UIColumn) uiComponent; return header ? uiColumn.getHeader() != null : uiColumn.getFooter() != null; } return false; } /** * Calculate the number of columns the specified child component will span * when rendered. *

* Normally, this is a fairly simple calculation: a UIColumn component * is rendered as one column, every other child type is not rendered * (ie spans zero columns). However custom subclasses of this renderer may * override this method to handle cases where a single component renders * as multiple columns. */ protected int determineChildColSpan(UIComponent uiComponent) { if (uiComponent instanceof UIColumn) { return 1; } return 0; } /** * Renders the header row of the table being rendered. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param component the UIComponent for whom a table is being rendered. * @param headerFacet the facet for the header. * @param headerStyleClass the styleClass of the header. * @param colspan the number of columns the header should span. Typically, this is * the number of columns in the table. * @throws IOException if an exception occurs. */ protected void renderTableHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, UIComponent headerFacet, String headerStyleClass, int colspan) throws IOException { renderTableHeaderOrFooterRow(facesContext, writer, component, headerFacet, headerStyleClass, determineHeaderCellTag(facesContext, component), colspan, true); } /** * Renders the footer row of the table being rendered. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param component the UIComponent for whom a table is being rendered. * @param footerFacet the facet for the footer. * @param footerStyleClass the styleClass of the footer. * @param colspan the number of columns the header should span. Typically, this is * the number of columns in the table. * @throws IOException if an exception occurs. */ protected void renderTableFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, UIComponent footerFacet, String footerStyleClass, int colspan) throws IOException { renderTableHeaderOrFooterRow(facesContext, writer, component, footerFacet, footerStyleClass, HTML.TD_ELEM, colspan, false); } /** * Renders the header row for the columns, which is a separate row from the header row for the * UIData header facet. * * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param component the UIData component for whom a table is being rendered. * @param headerStyleClass the styleClass of the header * @throws IOException if an exception occurs. */ protected void renderColumnHeaderRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, String headerStyleClass) throws IOException { renderColumnHeaderOrFooterRow(facesContext, writer, component, headerStyleClass, true); } /** * Renders the footer row for the columns, which is a separate row from the footer row for the * UIData footer facet. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param component the UIComponent for whom a table is being rendered. * @param footerStyleClass the styleClass of the footerStyleClass * @throws IOException if an exception occurs. */ protected void renderColumnFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, String footerStyleClass) throws IOException { renderColumnHeaderOrFooterRow(facesContext, writer, component, footerStyleClass, false); } protected void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, UIComponent facet, String styleClass, String colElementName, int colspan, boolean isHeader) throws IOException { writer.startElement(HTML.TR_ELEM, null); // component); writer.startElement(colElementName, null); // component); if (colElementName.equals(determineHeaderCellTag(facesContext, component)) && isHeader) { writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null); } // span all the table's columns int newsPaperColumns = getNewspaperColumns(component); int totalColumns = colspan * newsPaperColumns; if(hasNewspaperTableSpacer(component)) { totalColumns = totalColumns + newsPaperColumns - 1; } // Only render colspan if is > 0 if (totalColumns > 0) { writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.valueOf(totalColumns), null); } if (styleClass != null) { writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null); } if (facet != null) { //RendererUtils.renderChild(facesContext, facet); facet.encodeAll(facesContext); } writer.endElement(colElementName); writer.endElement(HTML.TR_ELEM); } /** * @param component the UIData component for whom a table is being rendered. */ private void renderColumnHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent component, String styleClass, boolean header) throws IOException { writer.startElement(HTML.TR_ELEM, null); // component); int columnIndex = 0; int newspaperColumns = getNewspaperColumns(component); for(int nc = 0; nc < newspaperColumns; nc++) { for (int i = 0, childCount = component.getChildCount(); i < childCount; i++) { UIComponent uiComponent = component.getChildren().get(i); if (uiComponent.isRendered()) { if (component instanceof UIData && uiComponent instanceof UIColumn) { beforeColumnHeaderOrFooter(facesContext, (UIData) component, header, columnIndex); } renderColumnChildHeaderOrFooterRow(facesContext, writer, uiComponent, styleClass, header); if (component instanceof UIData && uiComponent instanceof UIColumn) { afterColumnHeaderOrFooter(facesContext, (UIData) component, header, columnIndex); } } columnIndex += 1; } if (hasNewspaperTableSpacer(component)) { // draw the spacer facet if(nc < newspaperColumns - 1) { renderSpacerCell(facesContext, writer, component); } } } writer.endElement(HTML.TR_ELEM); } /** * Renders a spacer between adjacent newspaper columns. */ protected void renderSpacerCell(FacesContext facesContext, ResponseWriter writer, UIComponent component) throws IOException { UIComponent spacer = getNewspaperTableSpacer(component); if(spacer == null) { return; } writer.startElement(HTML.TD_ELEM, null); // component); //RendererUtils.renderChild(facesContext, spacer); spacer.encodeAll(facesContext); writer.endElement(HTML.TD_ELEM); } protected void renderColumnChildHeaderOrFooterRow(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent, String styleClass, boolean isHeader) throws IOException { if (uiComponent instanceof UIColumn) { // allow column to override style class, new in Faces 1.2 if (uiComponent instanceof HtmlColumn) { HtmlColumn column = (HtmlColumn)uiComponent; if (isHeader && column.getHeaderClass()!=null) { styleClass = column.getHeaderClass(); } else if (!isHeader && column.getFooterClass()!=null) { styleClass = column.getFooterClass(); } } else { //This code corrects MYFACES-1790, because HtmlColumnTag //has as component type jakarta.faces.Column, so as side //effect it not create HtmlColumn, it create UIColumn //classes. UIColumn column = (UIColumn) uiComponent; if (isHeader) { String headerClass = (String) column.getAttributes().get("headerClass"); if (headerClass != null) { styleClass = headerClass; } } else { String footerClass = (String) column.getAttributes().get("footerClass"); if (footerClass != null) { styleClass = footerClass; } } } if (isHeader) { renderColumnHeaderCell(facesContext, writer, uiComponent, ((UIColumn) uiComponent).getHeader(), styleClass, 0); } else { renderColumnFooterCell(facesContext, writer, uiComponent, ((UIColumn) uiComponent).getFooter(), styleClass, 0); } } } /** * Renders the header facet for the given UIColumn. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiColumn the UIColumn. * @param headerStyleClass the styleClass of the header facet. * @param colspan the colspan for the tableData element in which the header facet * will be wrapped. * @throws IOException */ protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn, String headerStyleClass, int colspan) throws IOException { renderColumnHeaderCell(facesContext, writer, uiColumn, uiColumn.getHeader(), headerStyleClass, colspan); } /** * Renders a TH cell within a TR within a THEAD section. If the specified * UIColumn object does have a header facet, then that facet is rendered * within the cell, otherwise the cell is left blank (though any specified * style class is still applied to empty cells). * * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiComponent the UIComponent to render the facet for. * @param facet the UIComponent to render as facet. * @param headerStyleClass the styleClass of the header facet. * @param colspan the colspan for the tableData element in which the header facet * will be wrapped. * @throws IOException */ protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent, UIComponent facet, String headerStyleClass, int colspan) throws IOException { writer.startElement(determineHeaderCellTag(facesContext, uiComponent.getParent()), null); // uiComponent); if (colspan > 1) { writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.valueOf(colspan), null); } if (headerStyleClass != null) { writer.writeAttribute(HTML.CLASS_ATTR, headerStyleClass, null); } writer.writeAttribute(HTML.SCOPE_ATTR, "col", null); if (facet != null) { //RendererUtils.renderChild(facesContext, facet); facet.encodeAll(facesContext); } writer.endElement(determineHeaderCellTag(facesContext, uiComponent.getParent())); } protected String determineHeaderCellTag(FacesContext facesContext, UIComponent uiComponent) { return HTML.TH_ELEM; } /** * Renders the footer facet for the given UIColumn. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiColumn the UIComponent. * @param footerStyleClass the styleClass of the footer facet. * @param colspan the colspan for the tableData element in which the footer facet * will be wrapped. * @throws IOException */ protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIColumn uiColumn, String footerStyleClass, int colspan) throws IOException { renderColumnFooterCell(facesContext, writer, uiColumn, uiColumn.getFooter(), footerStyleClass, colspan); } /** * Renders the footer facet for the given UIColumn. * @param facesContext the FacesContext. * @param writer the ResponseWriter. * @param uiComponent the UIComponent to render the facet for. * @param facet the UIComponent to render as facet. * @param footerStyleClass the styleClass of the footer facet. * @param colspan the colspan for the tableData element in which the footer facet * will be wrapped. * @throws IOException */ protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent, UIComponent facet, String footerStyleClass, int colspan) throws IOException { writer.startElement(HTML.TD_ELEM, null); // uiComponent); if (colspan > 1) { writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.valueOf(colspan), null); } if (footerStyleClass != null) { writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null); } if (facet != null) { //RendererUtils.renderChild(facesContext, facet); facet.encodeAll(facesContext); } writer.endElement(HTML.TD_ELEM); } /** * Gets the headerClass attribute of the given UIComponent. * @param component the UIComponent. * @return the headerClass attribute of the given UIComponent. */ protected static String getHeaderClass(UIComponent component) { if (component instanceof HtmlDataTable) { return ((HtmlDataTable) component).getHeaderClass(); } else { return (String) component.getAttributes().get(ComponentAttrs.HEADER_CLASS_ATTR); } } /** * Gets the footerClass attribute of the given UIComponent. * @param component the UIComponent. * @return the footerClass attribute of the given UIComponent. */ protected static String getFooterClass(UIComponent component) { if (component instanceof HtmlDataTable) { return ((HtmlDataTable) component).getFooterClass(); } else { return (String) component.getAttributes().get(ComponentAttrs.FOOTER_CLASS_ATTR); } } @Override public void decode(FacesContext context, UIComponent component) { super.decode(context, component); ClientBehaviorRendererUtils.decodeClientBehaviors(context, component); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy