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