001    package edu.nrao.sss.webapp.faces.renderer;
002    
003    import org.apache.myfaces.renderkit.html.HtmlTableRenderer;
004    
005    import javax.faces.context.FacesContext;
006    
007    import java.io.IOException;
008    
009    import javax.faces.component.UIColumn;
010    import javax.faces.component.UIComponent;
011    import javax.faces.component.UIData;
012    import javax.faces.context.ResponseWriter;
013    import javax.faces.el.ValueBinding;
014    
015    import org.apache.log4j.Logger;
016    
017    /**
018     * This class extends Apache MyFace's HtmlTableRenderer to add the ability to
019     * render a colspan attribute on UIColumn's. An example usage would be to
020     * conditionally assign a colspan value to a UIColumn tag so that some rows of
021     * that column span multiple columns, and some don't. Important to note that
022     * the columns that the span will be encompassing need to be not rendered under
023     * the  same conditions.
024     */
025    public class TableRenderer extends HtmlTableRenderer
026    {
027            private static final Logger log = Logger.getLogger(TableRenderer.class);
028    
029            protected static final String colspanAttr = "colspan";
030            protected static final String styleClassAttr = "styleClass";
031    
032            /**
033             * Overrides the renderColumnBody method of HtmlTableRenderer to add
034             * rendering of a colspan attribute when necessary.
035             */
036            protected void renderColumnBody(
037                            FacesContext facesContext,
038                            ResponseWriter writer,
039                            UIData uiData,
040                            UIComponent component,
041                            Styles styles, int columnStyleIndex) throws IOException
042            {
043                    writer.startElement("td", uiData);
044    
045                    int colspan = getColspan(component, facesContext);
046    
047                    if (colspan > 1)
048                    {
049                            //render a colspan attr
050                            writer.writeAttribute("colspan", colspan, null);
051                    }
052    
053                    //If a uicolumn has a styleClass specified, it takes precedence over any
054                    //styleClass specified with the columnClasses attr of the UIData.
055                    String sClass = getStyleClass(component, facesContext);
056                    if (sClass != null)
057                    {                     
058                            writer.writeAttribute("class", sClass, null);
059                    }
060    
061                    else
062                    {
063                            if (styles.hasColumnStyle())
064                            {
065                                    writer.writeAttribute("class", styles.getColumnStyle(columnStyleIndex), null);
066                            }
067                    }
068    
069                    RendererUtils.renderChild(facesContext, component);
070                    writer.endElement("td");
071            }
072    
073            /**
074             * Overrides the renderColumnHeaderCell method of HtmlTableRenderer to use
075             * the colspan attribute specified in the column tag.
076             */
077            protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent,
078                            UIComponent facet, String headerStyleClass, int colspan) throws IOException
079            {
080                    UIColumn uiColumn = (UIColumn)uiComponent;
081    
082                    log.debug("In renderColumnHeaderCell. Rendered? " + uiColumn.isRendered());
083                    int cs = getColspan(uiColumn, facesContext);
084                    if (cs <= 1)
085                            cs = colspan;
086    
087                    else
088                    {
089                            log.debug("Found a non-1 colspan.");
090                    }
091    
092                    //If a uicolumn has a styleClass specified, it takes precedence over any
093                    //styleClass specified with the columnClasses attr of the UIData.
094                    String sClass = getStyleClass(uiColumn, facesContext);
095                    if (sClass == null)
096                            sClass = headerStyleClass;
097    
098                    super.renderColumnHeaderCell(facesContext, writer, uiComponent, facet, sClass, cs);
099            }
100    
101            /**
102             * This is rediculous. I have to override this method to call
103             * <code>uiData.setRowIndex(0)</code> before the headers are rendered so that
104             * the rendered flag of the child UIColumn objects is correctly set. This
105             * step isn't done until we are rendering the body normally. I'm not sure
106             * what the ramifications of doing this early are. It gets reset once body
107             * rendering starts anyway, so hopefully there are none.
108             */
109            protected void beforeBody(FacesContext facesContext, UIData uiData) throws IOException
110            {
111                    ResponseWriter writer = facesContext.getResponseWriter();
112    
113                    log.debug("Running beforeBody");
114                    uiData.setRowIndex(0);
115    
116                    renderFacet(facesContext, writer, uiData, true);
117                    renderFacet(facesContext, writer, uiData, false);
118            }
119    
120            protected int getColspan(UIComponent column, FacesContext ctx)
121            {
122                    ValueBinding vb = column.getValueBinding(colspanAttr);
123                    if (vb != null)
124                    {
125                            Object val = vb.getValue(ctx);
126                            
127                            if (val instanceof Integer && val != null)
128                                    return (Integer)val;
129                    }
130                    
131                    else
132                    {
133                            String len = (String)column.getAttributes().get(colspanAttr);
134                            if (len != null)
135                            {
136                                    try
137                                    {
138                                            return Integer.parseInt(len);
139                                    }
140                                    catch (NumberFormatException nfe)
141                                    {
142                                            //log and Fall through to the "else" below
143                                            log.warn("Invalid colspan specified while rendering a column: " + len);
144                                    }
145                            }
146                    }
147    
148                    //else
149                    return 1;
150            }
151    
152            protected String getStyleClass(UIComponent column, FacesContext ctx)
153            {
154                    ValueBinding vb = column.getValueBinding(styleClassAttr);
155                    if (vb != null)
156                    {
157                            Object val = vb.getValue(ctx);
158                            
159                            if (val instanceof String && val != null)
160                                    return (String)val;
161                    }
162                    
163                    else
164                    {
165                            String sc = (String)column.getAttributes().get(styleClassAttr);
166                            if (sc != null)
167                            {
168                                    return sc;
169                            }
170                    }
171    
172                    //else
173                    return null;
174            }
175    }