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