View Javadoc

1   /*
2    * $Id: FormRenderer.java 471754 2006-11-06 14:55:09Z husted $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts.faces.renderer;
23  
24  
25  import java.io.IOException;
26  import java.util.Map;
27  
28  import javax.faces.component.NamingContainer;
29  import javax.faces.component.UIComponent;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.servlet.http.HttpSession;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.struts.Globals;
37  import org.apache.struts.config.ActionConfig;
38  import org.apache.struts.config.ModuleConfig;
39  import org.apache.struts.faces.component.FormComponent;
40  
41  
42  /**
43   * <p><code>Renderer</code> implementation for the <code>form</code> tag
44   * from the <em>Struts-Faces Integration Library</em>.</p>
45   *
46   * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
47   */
48  
49  public class FormRenderer extends AbstractRenderer {
50  
51  
52      // -------------------------------------------------------- Static Variables
53  
54  
55      /**
56       * <p>The <code>Log</code> instance for this class.</p>
57       */
58      private static Log log = LogFactory.getLog(FormRenderer.class);
59  
60  
61      // ---------------------------------------------------------- Public Methods
62  
63  
64      /**
65       * <p>Perform setup processing that will be required for decoding the
66       * incoming request.</p>
67       *
68       * @param context FacesContext for the request we are processing
69       * @param component UIComponent to be processed
70       *
71       * @exception NullPointerException if <code>context</code>
72       *  or <code>component</code> is null
73       */
74      public void decode(FacesContext context, UIComponent component) {
75  
76          if ((context == null) || (component == null)) {
77              throw new NullPointerException();
78          }
79          String clientId = component.getClientId(context);
80          Map map = context.getExternalContext().getRequestParameterMap();
81          if (log.isDebugEnabled()) {
82              log.debug("decode(" + clientId + ") --> " +
83                        map.containsKey(clientId));
84          }
85          component.getAttributes().put
86              ("submitted",
87               map.containsKey(clientId) ? Boolean.TRUE : Boolean.FALSE);
88  
89      }
90  
91  
92      private static String passThrough[] =
93      { "enctype", "method", "onreset", "onsubmit", "style", "target", };
94  
95  
96      /**
97       * <p>Render the beginning of an HTML <code>&lt;form&gt;</code>
98       * control.</p>
99       *
100      * @param context FacesContext for the request we are processing
101      * @param component UIComponent to be rendered
102      *
103      * @exception IOException if an input/output error occurs while rendering
104      * @exception NullPointerException if <code>context</code>
105      *  or <code>component</code> is null
106      */
107     public void encodeBegin(FacesContext context, UIComponent component)
108         throws IOException {
109 
110         if ((context == null) || (component == null)) {
111             throw new NullPointerException();
112         }
113 
114         // Calculate and cache the form name
115         FormComponent form = (FormComponent) component;
116         String action = form.getAction();
117         ModuleConfig moduleConfig = form.lookupModuleConfig(context);
118         ActionConfig actionConfig = moduleConfig.findActionConfig(action);
119         if (actionConfig == null) {
120             throw new IllegalArgumentException("Cannot find action '" +
121                                                action + "' configuration");
122         }
123         String beanName = actionConfig.getAttribute();
124         if (beanName != null) {
125             form.getAttributes().put("beanName", beanName);
126         }
127 
128         // Look up attribute values we need
129         String clientId = component.getClientId(context);
130         if (log.isDebugEnabled()) {
131             log.debug("encodeBegin(" + clientId + ")");
132         }
133         String styleClass =
134             (String) component.getAttributes().get("styleClass");
135 
136         // Render the beginning of this form
137         ResponseWriter writer = context.getResponseWriter();
138         writer.startElement("form", form);
139         writer.writeAttribute("id", clientId, "clientId");
140         if (beanName != null) {
141             writer.writeAttribute("name", beanName, null);
142         }
143         writer.writeAttribute("action", action(context, component), "action");
144         if (styleClass != null) {
145             writer.writeAttribute("class", styleClass, "styleClass");
146         }
147         if (component.getAttributes().get("method") == null) {
148             writer.writeAttribute("method", "post", null);
149         }
150         renderPassThrough(context, component, writer, passThrough);
151         writer.writeText("\n", null);
152 
153         // Add a marker used by our decode() method to note this form is submitted
154         writer.startElement("input", form);
155         writer.writeAttribute("type", "hidden", null);
156         writer.writeAttribute("name", clientId, null);
157         writer.writeAttribute("value", clientId, null);
158         writer.endElement("input");
159         writer.writeText("\n", null);
160 
161         // Add a transaction token if necessary
162         HttpSession session = (HttpSession)
163             context.getExternalContext().getSession(false);
164         if (session != null) {
165             String token = (String)
166                 session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
167             if (token != null) {
168                 writer.startElement("input", form);
169                 writer.writeAttribute("type", "hidden", null);
170                 writer.writeAttribute
171                     ("name", "org.apache.struts.taglib.html.TOKEN", null);
172                 writer.writeAttribute("value", token, null);
173                 writer.endElement("input");
174                 writer.writeText("\n", null);
175             }
176         }
177 
178         // Create an instance of the form bean if necessary
179         if (component instanceof FormComponent) {
180             ((FormComponent) component).createActionForm(context);
181         }
182 
183     }
184 
185 
186     /**
187      * <p>Render the ending of an HTML <code>&lt;form&gt;</code>
188      * control.</p>
189      *
190      * @param context FacesContext for the request we are processing
191      * @param component UIComponent to be rendered
192      *
193      * @exception IOException if an input/output error occurs while rendering
194      * @exception NullPointerException if <code>context</code>
195      *  or <code>component</code> is null
196      */
197     public void encodeEnd(FacesContext context, UIComponent component)
198         throws IOException {
199 
200         if ((context == null) || (component == null)) {
201             throw new NullPointerException();
202         }
203         String clientId = component.getClientId(context);
204         if (log.isDebugEnabled()) {
205             log.debug("encodeEnd(" + clientId + ")");
206         }
207         ResponseWriter writer = context.getResponseWriter();
208 
209         // Render the hidden variable our decode() method uses to detect submits
210         writer.startElement("input", component);
211         writer.writeAttribute("type", "hidden", null);
212         writer.writeAttribute("name", component.getClientId(context), null);
213         writer.writeAttribute("value", component.getClientId(context), null);
214         writer.endElement("input");
215         writer.write("\n");
216 
217         // Write our state information (if necessary)
218         context.getApplication().getViewHandler().writeState(context);
219 
220         // Render the ending of this form
221         writer.endElement("form");
222         writer.writeText("\n", null);
223 
224         // Render focus JavaScript if requested
225         if (!(component instanceof FormComponent)) {
226             return;
227         }
228         String focus = (String) component.getAttributes().get("focus");
229         if (focus == null) {
230             return;
231         }
232         String focusIndex =
233             (String) component.getAttributes().get("focusIndex");
234         writer.writeText("\n", null);
235         FormComponent form = (FormComponent) component;
236         writer.startElement("script", form);
237         writer.writeAttribute("type", "text/javascript", null);
238         if (!isXhtml(component)) {
239             writer.writeAttribute("language", "JavaScript", null);
240         }
241         writer.writeText("\n", null);
242         if (!isXhtml(component)) {
243             writer.write("<!--\n");
244         }
245 
246         StringBuffer sb = new StringBuffer("document.forms[\"");
247         sb.append(clientId);
248         sb.append("\"].elements[\"");
249         sb.append(component.getClientId(context));
250         sb.append(NamingContainer.SEPARATOR_CHAR);
251         sb.append(focus);
252         sb.append("\"]");
253         String focusControl = sb.toString();
254 
255         writer.write("  var focusControl = ");
256         writer.write(focusControl);
257         writer.write(";\n");
258         writer.write("  if (focusControl.type != \"hidden\") {\n");
259         writer.write("    ");
260         writer.write(focusControl);
261         if (focusIndex != null) {
262             writer.write("[");
263             writer.write(focusIndex);
264             writer.write("]");
265         }
266         writer.write(".focus();\n");
267         writer.write("  }\n");
268 
269         if (!isXhtml(component)) {
270             writer.write("// -->\n");
271         }
272         writer.endElement("script");
273         writer.writeText("\n", null);
274 
275     }
276 
277 
278     // ------------------------------------------------------- Protected Methods
279 
280 
281     /**
282      * <p>Calculate and return the value to be specifed for the
283      * <code>action</action> attribute on the <code>&lt;form&gt;</code>
284      * element to be rendered.</p>
285      *
286      * @param context FacesContext for the current request
287      * @param component Component being processed
288      */
289     protected String action(FacesContext context, UIComponent component) {
290 
291         String actionURL =
292             context.getApplication().getViewHandler().
293             getActionURL(context, context.getViewRoot().getViewId());
294         if (log.isTraceEnabled()) {
295             log.trace("getActionURL(" + context.getViewRoot().getViewId() +
296                       ") --> " + actionURL);
297         }
298         return (context.getExternalContext().encodeActionURL(actionURL));
299 
300     }
301 
302 
303     /**
304      * <p>Return <code>true</code> if we should render as XHTML.</p>
305      *
306      * @param component The component we are rendering
307      */
308     protected boolean isXhtml(UIComponent component) {
309 
310         return (false); // FIXME -- check up the hierarchy
311 
312     }
313 
314 
315 }