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.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.faces.application.ViewHandler;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIForm;
30  import javax.faces.component.behavior.ClientBehavior;
31  import javax.faces.component.behavior.ClientBehaviorHolder;
32  import javax.faces.component.html.HtmlForm;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  
36  import org.apache.myfaces.shared.config.MyfacesConfig;
37  import org.apache.myfaces.shared.renderkit.JSFAttr;
38  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
39  
40  public class HtmlFormRendererBase
41          extends HtmlRenderer
42  {
43      //private static final Log log = LogFactory.getLog(HtmlFormRenderer.class);
44      private static final String FORM_TARGET = HTML.FORM_ELEM;
45      private static final String HIDDEN_SUBMIT_INPUT_SUFFIX = "_SUBMIT";
46      private static final String HIDDEN_SUBMIT_INPUT_VALUE = "1";
47  
48      private static final String HIDDEN_COMMAND_INPUTS_SET_ATTR
49              = UIForm.class.getName() + ".org.apache.myfaces.HIDDEN_COMMAND_INPUTS_SET";
50  
51      private static final String SCROLL_HIDDEN_INPUT = "org.apache.myfaces.SCROLL_HIDDEN_INPUT";
52  
53      public void encodeBegin(FacesContext facesContext, UIComponent component)
54              throws IOException
55      {
56          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, UIForm.class);
57  
58          UIForm htmlForm = (UIForm)component;
59  
60          ResponseWriter writer = facesContext.getResponseWriter();
61          String clientId = htmlForm.getClientId(facesContext);
62          String acceptCharset = getAcceptCharset(facesContext, htmlForm);
63          String actionURL = getActionUrl(facesContext, htmlForm);
64          String method = getMethod(facesContext, htmlForm);
65  
66          Map<String, List<ClientBehavior>> behaviors = null;
67          if (component instanceof ClientBehaviorHolder)
68          {
69              behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
70              if (!behaviors.isEmpty())
71              {
72                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
73              }
74          }
75          
76          writer.startElement(HTML.FORM_ELEM, htmlForm);
77          writer.writeAttribute(HTML.ID_ATTR, clientId, null);
78          writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
79          writer.writeAttribute(HTML.METHOD_ATTR, method, null);
80          if (acceptCharset != null)
81          {
82              writer.writeAttribute(HTML.ACCEPT_CHARSET_ATTR, acceptCharset, null);
83          }
84          String encodedActionURL = facesContext.getExternalContext().encodeActionURL(actionURL);
85          
86          writer.writeURIAttribute(HTML.ACTION_ATTR,
87                  encodedActionURL,
88                  null);
89          
90          if (htmlForm instanceof ClientBehaviorHolder)
91          {
92              behaviors = ((ClientBehaviorHolder) htmlForm).getClientBehaviors();
93              if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
94              {
95                  CommonPropertyUtils.renderEventProperties(writer, 
96                          CommonPropertyUtils.getCommonPropertiesMarked(htmlForm), htmlForm);
97              }
98              else
99              {
100                 if (isCommonEventsOptimizationEnabled(facesContext))
101                 {
102                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
103                            CommonPropertyUtils.getCommonPropertiesMarked(htmlForm),
104                            CommonEventUtils.getCommonEventsMarked(htmlForm), htmlForm, behaviors);
105                 }
106                 else
107                 {
108                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, htmlForm, behaviors);
109                 }
110             }
111             if (isCommonPropertiesOptimizationEnabled(facesContext))
112             {
113                 CommonPropertyUtils.renderCommonPassthroughPropertiesWithoutEvents(writer, 
114                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
115                 HtmlRendererUtils.renderHTMLAttributes(writer, htmlForm, HTML.FORM_ATTRIBUTES);
116             }
117             else
118             {
119                 HtmlRendererUtils.renderHTMLAttributes(writer, htmlForm, 
120                         HTML.FORM_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
121             }
122         }
123         else
124         {
125             if (isCommonPropertiesOptimizationEnabled(facesContext))
126             {
127                 CommonPropertyUtils.renderCommonPassthroughProperties(writer, 
128                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
129                 HtmlRendererUtils.renderHTMLAttributes(writer, htmlForm, HTML.FORM_ATTRIBUTES);
130             }
131             else
132             {
133                 HtmlRendererUtils.renderHTMLAttributes(writer, htmlForm, HTML.FORM_PASSTHROUGH_ATTRIBUTES);
134             }
135         }
136 
137         writer.write(""); // force start element tag to be closed
138 
139         String encodedPartialActionURL =  facesContext.getExternalContext().encodePartialActionURL(actionURL);
140         
141         if (encodedActionURL != null && encodedPartialActionURL != null
142             && (!encodedActionURL.equals(encodedPartialActionURL)))
143         {
144             HtmlRendererUtils.renderHiddenInputField(writer, "javax.faces.encodedURL", encodedPartialActionURL);
145         }
146         
147         // not needed in this version as nothing is written to the form tag, but
148         // included for backward compatibility to the 1.1.1 patch (JIRA MYFACES-1276)
149         // However, might be needed in the future
150         beforeFormElementsStart(facesContext, component);
151         
152         MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
153         if (config.isRenderFormViewStateAtBegin())
154         {
155             renderViewStateAndHiddenFields(facesContext, component);
156         }
157         
158         afterFormElementsStart(facesContext, component);
159     }
160 
161     protected String getActionUrl(FacesContext facesContext, UIForm form)
162     {
163         return getActionUrl(facesContext);
164     }
165 
166     protected String getMethod(FacesContext facesContext, UIForm form)
167     {
168         return "post";
169     }
170 
171     protected String getAcceptCharset(FacesContext facesContext, UIForm form )
172     {
173         return (String)form.getAttributes().get( JSFAttr.ACCEPTCHARSET_ATTR );
174     }
175 
176     public void encodeEnd(FacesContext facesContext, UIComponent component)
177             throws IOException
178     {
179         ResponseWriter writer = facesContext.getResponseWriter();
180 
181         beforeFormElementsEnd(facesContext, component);
182 
183         //render hidden command inputs
184         Set set =  (Set) facesContext.getExternalContext().getRequestMap().get(
185                 getHiddenCommandInputsSetName(facesContext, component)); 
186         if (set != null && !set.isEmpty())
187         {
188             HtmlRendererUtils.renderHiddenCommandFormParams(writer, set);
189 
190             String target;
191             if (component instanceof HtmlForm)
192             {
193                 target = ((HtmlForm)component).getTarget();
194             }
195             else
196             {
197                 target = (String)component.getAttributes().get(HTML.TARGET_ATTR);
198             }
199             HtmlRendererUtils.renderClearHiddenCommandFormParamsFunction(writer,
200                                                                          component.getClientId(facesContext),
201                                                                          set,
202                                                                          target);
203         }
204 
205         MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
206         if (!config.isRenderFormViewStateAtBegin())
207         {
208             renderViewStateAndHiddenFields(facesContext, component);
209         }
210 
211         afterFormElementsEnd(facesContext, component);
212         
213         writer.endElement(HTML.FORM_ELEM);
214     }
215     
216     protected void renderViewStateAndHiddenFields(FacesContext facesContext, UIComponent component)
217             throws IOException
218     {
219         ResponseWriter writer = facesContext.getResponseWriter();
220         
221                 //write hidden input to determine "submitted" value on decode
222         writer.startElement(HTML.INPUT_ELEM, null); // component);
223         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
224         writer.writeAttribute(HTML.NAME_ATTR, component.getClientId(facesContext) +
225                                               HIDDEN_SUBMIT_INPUT_SUFFIX, null);
226         writer.writeAttribute(HTML.VALUE_ATTR, HIDDEN_SUBMIT_INPUT_VALUE, null);
227         writer.endElement(HTML.INPUT_ELEM);
228 
229         renderScrollHiddenInputIfNecessary(component, facesContext, writer);
230 
231         //write state marker at the end of the form
232         //Todo: this breaks client-side enabled AJAX components again which are searching for the state
233         //we'll need to fix this
234         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
235         viewHandler.writeState(facesContext);
236 
237         List<UIComponent> componentResources = facesContext.getViewRoot().getComponentResources(facesContext,
238             FORM_TARGET);
239         
240         for (int i = 0, size = componentResources.size(); i < size; i++)
241         {
242            UIComponent child = componentResources.get(i);
243            child.encodeAll (facesContext);
244         }
245     }
246 
247     private static String getHiddenCommandInputsSetName(FacesContext facesContext, UIComponent form)
248     {
249         StringBuilder buf = new StringBuilder(HIDDEN_COMMAND_INPUTS_SET_ATTR.length()+20);
250         buf.append(HIDDEN_COMMAND_INPUTS_SET_ATTR);
251         buf.append("_");
252         buf.append(form.getClientId(facesContext));
253         return buf.toString();
254     }
255 
256     private static String getScrollHiddenInputName(FacesContext facesContext, UIComponent form)
257     {
258         StringBuilder buf = new StringBuilder(SCROLL_HIDDEN_INPUT.length()+20);
259         buf.append(SCROLL_HIDDEN_INPUT);
260         buf.append("_");
261         buf.append(form.getClientId(facesContext));
262         return buf.toString();
263     }
264 
265 
266     public void decode(FacesContext facesContext, UIComponent component)
267     {
268         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, UIForm.class);
269 
270         /*
271         if (HTMLUtil.isDisabled(component))
272         {
273             return;
274         }
275         */
276 
277         UIForm htmlForm = (UIForm)component;
278 
279         Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
280         String submittedValue = (String)paramMap.get(component.getClientId(facesContext) +
281                                                      HIDDEN_SUBMIT_INPUT_SUFFIX);
282         if (submittedValue != null && submittedValue.equals(HIDDEN_SUBMIT_INPUT_VALUE))
283         {
284             htmlForm.setSubmitted(true);
285         }
286         else
287         {
288             htmlForm.setSubmitted(false);
289         }
290         
291         HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
292     }
293 
294 
295     public static void addHiddenCommandParameter(FacesContext facesContext, UIComponent form, String paramName)
296     {
297         Set set = (Set) facesContext.getExternalContext().getRequestMap().get(
298                 getHiddenCommandInputsSetName(facesContext, form));
299         if (set == null)
300         {
301             set = new HashSet();
302             facesContext.getExternalContext().getRequestMap().put(
303                     getHiddenCommandInputsSetName(facesContext, form), set);
304         }
305         set.add(paramName);
306     }
307 
308     public static void renderScrollHiddenInputIfNecessary(
309             UIComponent form, FacesContext facesContext, ResponseWriter writer)
310         throws IOException
311     {
312         if (form == null)
313         {
314             return;
315         }
316 
317         if (facesContext.getExternalContext().getRequestMap().get(
318                 getScrollHiddenInputName(facesContext, form)) == null)
319         {
320             if (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isAutoScroll())
321             {
322                 HtmlRendererUtils.renderAutoScrollHiddenInput(facesContext, writer);
323             }
324             facesContext.getExternalContext().getRequestMap().put(getScrollHiddenInputName(
325                     facesContext, form), Boolean.TRUE);
326         }
327     }
328 
329     /**
330      * Called before the state and any elements are added to the form tag in the
331      * encodeBegin method
332      */
333     protected void beforeFormElementsStart(FacesContext facesContext, UIComponent component)
334             throws IOException
335     {
336         
337     }
338 
339     /**
340      * Called after the state and any elements are added to the form tag in the
341      * encodeBegin method
342      */
343     protected void afterFormElementsStart(FacesContext facesContext, UIComponent component)
344             throws IOException
345     {
346         
347     }
348 
349     /**
350      * Called before the state and any elements are added to the form tag in the
351      * encodeEnd method
352      */
353     protected void beforeFormElementsEnd(FacesContext facesContext, UIComponent component)
354             throws IOException
355     {
356         
357     }
358 
359     /**
360      * Called after the state and any elements are added to the form tag in the
361      * encodeEnd method
362      */
363     protected void afterFormElementsEnd(FacesContext facesContext, UIComponent component)
364             throws IOException
365     {
366         
367     }
368 }