View Javadoc

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