1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts2.components;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Set;
28
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31
32 import org.apache.struts2.views.annotations.StrutsTag;
33 import org.apache.struts2.views.annotations.StrutsTagAttribute;
34 import org.apache.struts2.StrutsConstants;
35 import org.apache.struts2.dispatcher.Dispatcher;
36 import org.apache.struts2.dispatcher.mapper.ActionMapping;
37 import org.apache.struts2.portlet.context.PortletActionContext;
38 import org.apache.struts2.portlet.util.PortletUrlHelper;
39 import org.apache.struts2.views.util.UrlHelper;
40
41 import com.opensymphony.xwork2.ActionContext;
42 import com.opensymphony.xwork2.ActionInvocation;
43 import com.opensymphony.xwork2.ObjectFactory;
44 import com.opensymphony.xwork2.config.Configuration;
45 import com.opensymphony.xwork2.config.RuntimeConfiguration;
46 import com.opensymphony.xwork2.config.entities.ActionConfig;
47 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
48 import com.opensymphony.xwork2.inject.Inject;
49 import com.opensymphony.xwork2.interceptor.MethodFilterInterceptorUtil;
50 import com.opensymphony.xwork2.util.ValueStack;
51 import com.opensymphony.xwork2.util.TextUtils;
52 import com.opensymphony.xwork2.validator.ActionValidatorManagerFactory;
53 import com.opensymphony.xwork2.validator.FieldValidator;
54 import com.opensymphony.xwork2.validator.ValidationInterceptor;
55 import com.opensymphony.xwork2.validator.Validator;
56
57 /***
58 * <!-- START SNIPPET: javadoc -->
59 * <p/>
60 * Renders HTML an input form.<p/>
61 * <p/>
62 * The remote form allows the form to be submitted without the page being refreshed. The results from the form
63 * can be inserted into any HTML element on the page.<p/>
64 * <p/>
65 * NOTE:<p/>
66 * The order / logic in determining the posting url of the generated HTML form is as follows:-
67 * <ol>
68 * <li>
69 * If the action attribute is not specified, then the current request will be used to
70 * determine the posting url
71 * </li>
72 * <li>
73 * If the action is given, Struts will try to obtain an ActionConfig. This will be
74 * successfull if the action attribute is a valid action alias defined struts.xml.
75 * </li>
76 * <li>
77 * If the action is given and is not an action alias defined in struts.xml, Struts
78 * will used the action attribute as if it is the posting url, separting the namespace
79 * from it and using UrlHelper to generate the final url.
80 * </li>
81 * </ol>
82 * <p/>
83 * <!-- END SNIPPET: javadoc -->
84 * <p/>
85 * <p/> <b>Examples</b>
86 * <p/>
87 * <pre>
88 * <!-- START SNIPPET: example -->
89 * <p/>
90 * <s:form ... />
91 * <p/>
92 * <!-- END SNIPPET: example -->
93 * </pre>
94 *
95 */
96 @StrutsTag(name="form", tldTagClass="org.apache.struts2.views.jsp.ui.FormTag", description="Renders an input form")
97 public class Form extends ClosingUIBean {
98 public static final String OPEN_TEMPLATE = "form";
99 public static final String TEMPLATE = "form-close";
100
101 private int sequence = 0;
102
103 protected String onsubmit;
104 protected String action;
105 protected String target;
106 protected String enctype;
107 protected String method;
108 protected String namespace;
109 protected String validate;
110 protected String portletMode;
111 protected String windowState;
112 protected String acceptcharset;
113
114 protected boolean enableDynamicMethodInvocation = true;
115 protected Configuration configuration;
116 protected ObjectFactory objectFactory;
117
118 public Form(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
119 super(stack, request, response);
120 }
121
122 protected boolean evaluateNameValue() {
123 return false;
124 }
125
126 public String getDefaultOpenTemplate() {
127 return OPEN_TEMPLATE;
128 }
129
130 protected String getDefaultTemplate() {
131 return TEMPLATE;
132 }
133
134 @Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
135 public void setEnableDynamicMethodInvocation(String enable) {
136 enableDynamicMethodInvocation = "true".equals(enable);
137 }
138
139 @Inject
140 public void setConfiguration(Configuration configuration) {
141 this.configuration = configuration;
142 }
143
144 @Inject
145 public void setObjectFactory(ObjectFactory objectFactory) {
146 this.objectFactory = objectFactory;
147 }
148
149
150
151
152
153
154 protected void evaluateExtraParams() {
155 super.evaluateExtraParams();
156
157
158
159 if (validate != null) {
160 addParameter("validate", findValue(validate, Boolean.class));
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 if (onsubmit != null) {
179 addParameter("onsubmit", findString(onsubmit));
180 }
181
182 if (target != null) {
183 addParameter("target", findString(target));
184 }
185
186 if (enctype != null) {
187 addParameter("enctype", findString(enctype));
188 }
189
190 if (method != null) {
191 addParameter("method", findString(method));
192 }
193
194 if (acceptcharset != null) {
195 addParameter("acceptcharset", findString(acceptcharset));
196 }
197
198
199
200 if (!parameters.containsKey("tagNames")) {
201
202 addParameter("tagNames", new ArrayList());
203 }
204 }
205
206 /***
207 * Form component determine the its HTML element id as follows:-
208 * <ol>
209 * <li>if an 'id' attribute is specified.</li>
210 * <li>if an 'action' attribute is specified, it will be used as the id.</li>
211 * </ol>
212 */
213 protected void populateComponentHtmlId(Form form) {
214 boolean isAjax = "ajax".equalsIgnoreCase(this.theme);
215
216 String action = null;
217 if (this.action != null) {
218
219 action = findString(this.action);
220 }
221
222 if (id != null) {
223 addParameter("id", escape(id));
224 }
225 if (Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) {
226 evaluateExtraParamsPortletRequest(namespace, action);
227 } else {
228 String namespace = determineNamespace(this.namespace, getStack(),
229 request);
230 evaluateExtraParamsServletRequest(action, namespace, isAjax);
231 }
232 }
233
234 /***
235 * @param isAjax
236 * @param namespace
237 * @param action
238 */
239 private void evaluateExtraParamsServletRequest(String action, String namespace, boolean isAjax) {
240 if (action == null) {
241
242 ActionInvocation ai = (ActionInvocation) getStack().getContext().get(ActionContext.ACTION_INVOCATION);
243 if (ai != null) {
244 action = ai.getProxy().getActionName();
245 namespace = ai.getProxy().getNamespace();
246 } else {
247
248 String uri = request.getRequestURI();
249 action = uri.substring(uri.lastIndexOf('/'));
250 }
251 }
252
253 String actionMethod = "";
254
255
256 if (enableDynamicMethodInvocation) {
257 if (action.indexOf("!") != -1) {
258 int endIdx = action.lastIndexOf("!");
259 actionMethod = action.substring(endIdx + 1, action.length());
260 action = action.substring(0, endIdx);
261 }
262 }
263
264 final ActionConfig actionConfig = configuration.getRuntimeConfiguration().getActionConfig(namespace, action);
265 String actionName = action;
266 if (actionConfig != null) {
267
268 ActionMapping mapping = new ActionMapping(action, namespace, actionMethod, parameters);
269 String result = UrlHelper.buildUrl(actionMapper.getUriFromActionMapping(mapping), request, response, null);
270 addParameter("action", result);
271
272
273
274 addParameter("actionName", actionName);
275 try {
276 Class clazz = objectFactory.getClassInstance(actionConfig.getClassName());
277 addParameter("actionClass", clazz);
278 } catch (ClassNotFoundException e) {
279
280 }
281
282 addParameter("namespace", namespace);
283
284
285 if (name == null) {
286 addParameter("name", action);
287 }
288
289
290 if (id == null) {
291 addParameter("id", action);
292 }
293 } else if (action != null) {
294
295
296
297
298 String result = UrlHelper.buildUrl(action, request, response, null);
299 addParameter("action", result);
300
301
302 int slash = result.lastIndexOf('/');
303 if (slash != -1) {
304 addParameter("namespace", result.substring(0, slash));
305 } else {
306 addParameter("namespace", "");
307 }
308
309
310 if (id == null) {
311 slash = result.lastIndexOf('/');
312 int dot = result.indexOf('.', slash);
313 if (dot != -1) {
314 id = result.substring(slash + 1, dot);
315 } else {
316 id = result.substring(slash + 1);
317 }
318 addParameter("id", escape(id));
319 }
320 }
321
322
323
324
325 evaluateClientSideJsEnablement(actionName, namespace, actionMethod);
326 }
327
328 private void evaluateClientSideJsEnablement(String actionName, String namespace, String actionMethod) {
329
330
331 Boolean validate = (Boolean) getParameters().get("validate");
332 if (validate != null && validate.booleanValue()) {
333
334 addParameter("performValidation", Boolean.FALSE);
335
336 RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration();
337 ActionConfig actionConfig = runtimeConfiguration.getActionConfig(namespace, actionName);
338
339 if (actionConfig != null) {
340 List interceptors = actionConfig.getInterceptors();
341 for (Iterator i = interceptors.iterator(); i.hasNext();) {
342 InterceptorMapping interceptorMapping = (InterceptorMapping) i.next();
343 if (ValidationInterceptor.class.isInstance(interceptorMapping.getInterceptor())) {
344 ValidationInterceptor validationInterceptor = (ValidationInterceptor) interceptorMapping.getInterceptor();
345
346 Set excludeMethods = validationInterceptor.getExcludeMethodsSet();
347 Set includeMethods = validationInterceptor.getIncludeMethodsSet();
348
349 if (MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, actionMethod)) {
350 addParameter("performValidation", Boolean.TRUE);
351 }
352 return;
353 }
354 }
355 }
356 }
357 }
358
359 /***
360 * Constructs the action url adapted to a portal environment.
361 *
362 * @param action The action to create the URL for.
363 */
364 private void evaluateExtraParamsPortletRequest(String namespace, String action) {
365
366 if (this.action != null) {
367
368 action = findString(this.action);
369 }
370
371 String type = "action";
372 if (TextUtils.stringSet(method)) {
373 if ("GET".equalsIgnoreCase(method.trim())) {
374 type = "render";
375 }
376 }
377 if (action != null) {
378 String result = PortletUrlHelper.buildUrl(action, namespace,
379 getParameters(), type, portletMode, windowState);
380 addParameter("action", result);
381
382
383 int slash = result.lastIndexOf('/');
384 if (slash != -1) {
385 addParameter("namespace", result.substring(0, slash));
386 } else {
387 addParameter("namespace", "");
388 }
389
390
391
392 if (id == null) {
393 slash = action.lastIndexOf('/');
394 int dot = action.indexOf('.', slash);
395 if (dot != -1) {
396 id = action.substring(slash + 1, dot);
397 } else {
398 id = action.substring(slash + 1);
399 }
400 addParameter("id", escape(id));
401 }
402 }
403
404 }
405
406 public List getValidators(String name) {
407 Class actionClass = (Class) getParameters().get("actionClass");
408 if (actionClass == null) {
409 return Collections.EMPTY_LIST;
410 }
411
412 List all = ActionValidatorManagerFactory.getInstance().getValidators(actionClass, (String) getParameters().get("actionName"));
413 List validators = new ArrayList();
414 for (Iterator iterator = all.iterator(); iterator.hasNext();) {
415 Validator validator = (Validator) iterator.next();
416 if (validator instanceof FieldValidator) {
417 FieldValidator fieldValidator = (FieldValidator) validator;
418 if (fieldValidator.getFieldName().equals(name)) {
419 validators.add(fieldValidator);
420 }
421 }
422 }
423
424 return validators;
425 }
426
427 /***
428 * Get a incrementing sequence unique to this <code>Form</code> component.
429 * It is used by <code>Form</code> component's child that might need a
430 * sequence to make them unique.
431 *
432 * @return int
433 */
434 protected int getSequence() {
435 return sequence++;
436 }
437
438 @StrutsTagAttribute(description="HTML onsubmit attribute")
439 public void setOnsubmit(String onsubmit) {
440 this.onsubmit = onsubmit;
441 }
442
443 @StrutsTagAttribute(description="Set action nane to submit to, without .action suffix", defaultValue="current action")
444 public void setAction(String action) {
445 this.action = action;
446 }
447
448 @StrutsTagAttribute(description="HTML form target attribute")
449 public void setTarget(String target) {
450 this.target = target;
451 }
452
453 @StrutsTagAttribute(description="HTML form enctype attribute")
454 public void setEnctype(String enctype) {
455 this.enctype = enctype;
456 }
457
458 @StrutsTagAttribute(description="HTML form method attribute")
459 public void setMethod(String method) {
460 this.method = method;
461 }
462
463 @StrutsTagAttribute(description="Namespace for action to submit to", defaultValue="current namespace")
464 public void setNamespace(String namespace) {
465 this.namespace = namespace;
466 }
467
468 @StrutsTagAttribute(description="Whether client side/remote validation should be performed. Only" +
469 " useful with theme xhtml/ajax", type="Boolean", defaultValue="false")
470 public void setValidate(String validate) {
471 this.validate = validate;
472 }
473
474 @StrutsTagAttribute(description="he portlet mode to display after the form submit")
475 public void setPortletMode(String portletMode) {
476 this.portletMode = portletMode;
477 }
478
479 @StrutsTagAttribute(description="The window state to display after the form submit")
480 public void setWindowState(String windowState) {
481 this.windowState = windowState;
482 }
483
484 @StrutsTagAttribute(description="The accepted charsets for this form. The values may be comma or blank delimited.")
485 public void setAcceptcharset(String acceptcharset) {
486 this.acceptcharset = acceptcharset;
487 }
488 }