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 }