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