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.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.application.ProjectStage;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIPanel;
30  import javax.faces.component.behavior.ClientBehavior;
31  import javax.faces.component.behavior.ClientBehaviorHolder;
32  import javax.faces.component.html.HtmlPanelGrid;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  
36  import org.apache.myfaces.shared.renderkit.JSFAttr;
37  import org.apache.myfaces.shared.renderkit.RendererUtils;
38  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
39  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
40  import org.apache.myfaces.shared.util.ArrayUtils;
41  import org.apache.myfaces.shared.util.StringUtils;
42  
43  
44  /**
45   * @author Martin Marinschek
46   * @version $Revision: 1230371 $ $Date: 2012-01-11 21:09:45 -0500 (Wed, 11 Jan 2012) $
47   *          <p/>
48   *          $Log: $
49   */
50  public class HtmlGridRendererBase
51          extends HtmlRenderer
52  {
53      //private static final Log log = LogFactory.getLog(HtmlGridRendererBase.class);
54      private static final Logger log = Logger.getLogger(HtmlGridRendererBase.class.getName());
55      
56      private static final Integer[] ZERO_INT_ARRAY = new Integer[]{0};
57  
58      public boolean getRendersChildren()
59      {
60          return true;
61      }
62  
63      @Override
64      public void decode(FacesContext context, UIComponent component)
65      {
66          // Check for npe
67          super.decode(context, component);
68          
69          HtmlRendererUtils.decodeClientBehaviors(context, component);
70      }
71  
72      public void encodeBegin(FacesContext facesContext, UIComponent component)
73              throws IOException
74      {
75          // all work done in encodeEnd()
76      }
77  
78      public void encodeChildren(FacesContext context, UIComponent component)
79          throws IOException
80      {
81          // all work done in encodeEnd()
82      }
83  
84      public void encodeEnd(FacesContext facesContext, UIComponent component)
85              throws IOException
86      {
87          RendererUtils.checkParamValidity(facesContext, component, UIPanel.class);
88  
89          int columns;
90          if (component instanceof HtmlPanelGrid)
91          {
92              columns = ((HtmlPanelGrid)component).getColumns();
93          }
94          else
95          {
96              Integer i = (Integer)component.getAttributes().get(
97                      org.apache.myfaces.shared.renderkit.JSFAttr.COLUMNS_ATTR);
98              columns = i != null ? i.intValue() : 0;
99          }
100 
101         if (columns <= 0)
102         {
103             if (log.isLoggable(Level.SEVERE))
104             {
105                 log.severe("Wrong columns attribute for PanelGrid " + 
106                         component.getClientId(facesContext) + ": " + columns);
107             }
108             columns = 1;
109         }
110 
111         ResponseWriter writer = facesContext.getResponseWriter();
112         Map<String, List<ClientBehavior>> behaviors = null;
113         if (component instanceof ClientBehaviorHolder)
114         {
115             behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
116         }
117 
118         if (behaviors != null && !behaviors.isEmpty())
119         {
120             ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
121         }
122         
123         writer.startElement(HTML.TABLE_ELEM, component);
124         
125         if (component instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
126                 facesContext.getExternalContext()))
127         {
128             if (!behaviors.isEmpty())
129             {
130                 HtmlRendererUtils.writeIdAndName(writer, component, facesContext);
131             }
132             else
133             {
134                 HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
135             }
136             long commonPropertiesMarked = 0L;
137             if (isCommonPropertiesOptimizationEnabled(facesContext))
138             {
139                 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
140             }
141             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
142             {
143                 CommonPropertyUtils.renderEventProperties(writer, 
144                         commonPropertiesMarked, component);
145             }
146             else
147             {
148                 if (isCommonEventsOptimizationEnabled(facesContext))
149                 {
150                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
151                            commonPropertiesMarked,
152                            CommonEventUtils.getCommonEventsMarked(component), component, behaviors);
153                 }
154                 else
155                 {
156                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, component, behaviors);
157                 }
158             }
159             if (isCommonPropertiesOptimizationEnabled(facesContext))
160             {
161                 HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.TABLE_ATTRIBUTES);
162                 CommonPropertyUtils.renderCommonPassthroughPropertiesWithoutEvents(writer, 
163                         commonPropertiesMarked, component);
164             }
165             else
166             {
167                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
168                         HTML.TABLE_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
169             }
170         }
171         else
172         {
173             HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
174             if (isCommonPropertiesOptimizationEnabled(facesContext))
175             {
176                 HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.TABLE_ATTRIBUTES);
177                 CommonPropertyUtils.renderCommonPassthroughProperties(writer, 
178                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
179             }
180             else
181             {
182                 HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
183             }
184         }
185 
186         writer.flush();
187 
188         HtmlRendererUtils.renderTableCaption(facesContext, writer, component);
189 
190         // theader and tfooter are rendered before the tbody
191         renderHeaderOrFooter(facesContext, writer, component, columns, true);   //Header facet
192         renderHeaderOrFooter(facesContext, writer, component, columns, false);  //Footer facet
193         
194         renderChildren(facesContext, writer, component, columns);
195 
196         writer.endElement(HTML.TABLE_ELEM);
197     }
198 
199     
200 
201     protected void renderHeaderOrFooter(FacesContext context,
202                                       ResponseWriter writer,
203                                       UIComponent component,
204                                       int columns,
205                                       boolean header)
206         throws IOException
207     {
208         UIComponent facet = component.getFacet(header ? "header" : "footer");
209         if (facet == null)
210         {
211             return;
212         }
213 
214         HtmlRendererUtils.writePrettyLineSeparator(context);
215         writer.startElement(
216                 header ? org.apache.myfaces.shared.renderkit.html.HTML.THEAD_ELEM : HTML.TFOOT_ELEM, component);
217         writer.startElement(HTML.TR_ELEM, component);
218         writer.startElement(header ? HTML.TH_ELEM : HTML.TD_ELEM, component);
219 
220         String styleClass = (component instanceof HtmlPanelGrid)
221             ? (header ?
222                          ((HtmlPanelGrid)component).getHeaderClass() :
223                          ((HtmlPanelGrid)component).getFooterClass())
224             : (header ?
225                          (String)component.getAttributes().get(JSFAttr.HEADER_CLASS_ATTR) :
226                          (String)component.getAttributes().get(
227                                  org.apache.myfaces.shared.renderkit.JSFAttr.FOOTER_CLASS_ATTR));
228         if (styleClass != null)
229         {
230             writer.writeAttribute(HTML.CLASS_ATTR, styleClass,
231                                   header ? JSFAttr.HEADER_CLASS_ATTR : 
232                                       org.apache.myfaces.shared.renderkit.JSFAttr.FOOTER_CLASS_ATTR);
233         }
234 
235         if (header)
236         {
237             writer.writeAttribute(HTML.SCOPE_ATTR, HTML.SCOPE_COLGROUP_VALUE, null);
238         }
239 
240         writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.toString(columns), null);
241 
242         HtmlRendererUtils.writePrettyLineSeparator(context);
243         //RendererUtils.renderChild(context, facet);
244         facet.encodeAll(context);
245 
246         HtmlRendererUtils.writePrettyLineSeparator(context);
247         writer.endElement(header ? HTML.TH_ELEM : HTML.TD_ELEM);
248         writer.endElement(HTML.TR_ELEM);
249         writer.endElement(header ? HTML.THEAD_ELEM : HTML.TFOOT_ELEM);
250     }
251 
252     protected int childAttributes(FacesContext context,
253             ResponseWriter writer,
254             UIComponent component,
255             int columnIndex)
256         throws IOException
257     {
258         // subclasses can override this method to add attributes to the table cell <td> tag
259         return columnIndex;
260     }
261 
262     protected void renderChildren(FacesContext context,
263                                 ResponseWriter writer,
264                                 UIComponent component,
265                                 int columns)
266         throws IOException
267     {
268         String columnClasses;
269         String rowClasses;
270         if (component instanceof HtmlPanelGrid)
271         {
272             columnClasses = ((HtmlPanelGrid)component).getColumnClasses();
273             rowClasses =  ((HtmlPanelGrid)component).getRowClasses();
274         }
275         else
276         {
277             columnClasses = (String)component.getAttributes().get(
278                     org.apache.myfaces.shared.renderkit.JSFAttr.COLUMN_CLASSES_ATTR);
279             rowClasses = (String)component.getAttributes().get(JSFAttr.ROW_CLASSES_ATTR);
280         }
281 
282         String[] columnClassesArray = (columnClasses == null)
283             ? ArrayUtils.EMPTY_STRING_ARRAY
284             : StringUtils.trim(StringUtils.splitShortString(columnClasses, ','));
285         int columnClassesCount = columnClassesArray.length;
286 
287         String[] rowClassesArray = (rowClasses == null)
288             ? org.apache.myfaces.shared.util.ArrayUtils.EMPTY_STRING_ARRAY
289             : StringUtils.trim(StringUtils.splitShortString(rowClasses, ','));
290         int rowClassesCount = rowClassesArray.length;
291 
292         int childCount = getChildCount(component);
293         if (childCount > 0)
294         {
295             // get the row indizes for which a new TBODY element should be created
296             Integer[] bodyrows = null;
297             String bodyrowsAttr = (String) component.getAttributes().get(JSFAttr.BODYROWS_ATTR);
298             if(bodyrowsAttr != null && !"".equals(bodyrowsAttr)) 
299             {   
300                 String[] bodyrowsString = StringUtils.trim(StringUtils.splitShortString(bodyrowsAttr, ','));
301                 // parsing with no exception handling, because of JSF-spec: 
302                 // "If present, this must be a comma separated list of integers."
303                 bodyrows = new Integer[bodyrowsString.length];
304                 for(int i = 0; i < bodyrowsString.length; i++) 
305                 {
306                     bodyrows[i] = new Integer(bodyrowsString[i]);
307                 }
308                 
309             }
310             else
311             {
312                 bodyrows = ZERO_INT_ARRAY;
313             }
314             int bodyrowsCount = 0;
315             int rowIndex = -1;
316             int columnIndex = 0;
317             int rowClassIndex = 0;
318             boolean rowStarted = false;
319             for (int i = 0, size =  component.getChildCount(); i < size; i++)
320             {
321                 UIComponent child = component.getChildren().get(i);
322                 if (child.isRendered())
323                 {
324                     if (columnIndex == 0)
325                     {
326                         rowIndex++;
327                         
328                         if (rowStarted)
329                         {
330                             //do we have to close the last row?
331                             writer.endElement(HTML.TR_ELEM);
332                             HtmlRendererUtils.writePrettyLineSeparator(context);
333                         }
334                         
335                         // is the current row listed in the bodyrows attribute
336                         if(ArrayUtils.contains(bodyrows, rowIndex)) 
337                         {
338                             // close any preopened TBODY element first
339                             if(bodyrowsCount != 0) 
340                             {
341                                 writer.endElement(HTML.TBODY_ELEM);
342                                 HtmlRendererUtils.writePrettyLineSeparator(context);
343                             }
344                             writer.startElement(HTML.TBODY_ELEM, component); 
345                             HtmlRendererUtils.writePrettyLineSeparator(context);
346                             bodyrowsCount++;
347                         }
348                         
349                         //start of new/next row
350                         writer.startElement(HTML.TR_ELEM, component);
351                         if (rowClassIndex < rowClassesCount)
352                         {
353                             writer.writeAttribute(HTML.CLASS_ATTR, rowClassesArray[rowClassIndex], null);
354                         }
355                         rowStarted = true;
356                         rowClassIndex++;
357                         if (rowClassIndex == rowClassesCount)
358                         {
359                             rowClassIndex = 0;
360                         }
361                     }
362 
363                     writer.startElement(HTML.TD_ELEM, component);
364                     if (columnIndex < columnClassesCount)
365                     {
366                         writer.writeAttribute(HTML.CLASS_ATTR, columnClassesArray[columnIndex], null);
367                     }
368                     columnIndex = childAttributes(context, writer, child, columnIndex);
369                     //RendererUtils.renderChild(context, child);
370                     child.encodeAll(context);
371                     writer.endElement(HTML.TD_ELEM);
372 
373                     columnIndex++;
374                     if (columnIndex >= columns)
375                     {
376                         columnIndex = 0;
377                     }
378                 }
379             }
380 
381             if (rowStarted)
382             {
383                 if (columnIndex > 0)
384                 {
385                     Level level = context.isProjectStage(ProjectStage.Production) ? Level.FINE : Level.WARNING;
386                     if (log.isLoggable(level))
387                     {
388                         log.log(level, "PanelGrid " + RendererUtils.getPathToComponent(component) 
389                                 + " has not enough children. Child count should be a " 
390                                 + "multiple of the columns attribute.");
391                     }
392                     //Render empty columns, so that table is correct
393                     for ( ; columnIndex < columns; columnIndex++)
394                     {
395                         writer.startElement(HTML.TD_ELEM, component);
396                         if (columnIndex < columnClassesCount)
397                         {
398                             writer.writeAttribute(HTML.CLASS_ATTR, columnClassesArray[columnIndex], null);
399                         }
400                         writer.endElement(HTML.TD_ELEM);
401                     }
402                 }
403                 writer.endElement(HTML.TR_ELEM);
404                 HtmlRendererUtils.writePrettyLineSeparator(context);
405                 
406                 // close any preopened TBODY element first
407                 if(bodyrowsCount != 0) 
408                 {
409                     writer.endElement(HTML.TBODY_ELEM);
410                     HtmlRendererUtils.writePrettyLineSeparator(context);
411                 }
412             }
413         }
414     }
415 
416 }