Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
Form |
|
| 1.2564102564102564;1.256 |
1 | // Copyright 2004, 2005 The Apache Software Foundation |
|
2 | // |
|
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 | // you may not use this file except in compliance with the License. |
|
5 | // You may obtain a copy of the License at |
|
6 | // |
|
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
|
8 | // |
|
9 | // Unless required by applicable law or agreed to in writing, software |
|
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
|
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 | // See the License for the specific language governing permissions and |
|
13 | // limitations under the License. |
|
14 | ||
15 | package org.apache.tapestry.form; |
|
16 | ||
17 | import org.apache.hivemind.ApplicationRuntimeException; |
|
18 | import org.apache.hivemind.Location; |
|
19 | import org.apache.tapestry.*; |
|
20 | import org.apache.tapestry.engine.DirectServiceParameter; |
|
21 | import org.apache.tapestry.engine.IEngineService; |
|
22 | import org.apache.tapestry.engine.ILink; |
|
23 | import org.apache.tapestry.json.JSONObject; |
|
24 | import org.apache.tapestry.listener.ListenerInvoker; |
|
25 | import org.apache.tapestry.valid.IValidationDelegate; |
|
26 | import org.apache.tapestry.web.WebResponse; |
|
27 | ||
28 | /** |
|
29 | * Component which contains form element components. A Form will wrap other components and |
|
30 | * static HTML, including form components such as {@link TextArea}, {@link TextField}, |
|
31 | * {@link Checkbox}, etc. [ <a href="../../../../../ComponentReference/Form.html">Component Reference </a>] |
|
32 | * |
|
33 | * <p> |
|
34 | * When a form is submitted, it continues through the rewind cycle until <em>after</em> all of its |
|
35 | * wrapped elements have renderred. As the form component render (in the rewind cycle), they will be |
|
36 | * updating properties of the containing page and notifying thier listeners. Again: each form |
|
37 | * component is responsible not only for rendering HTML (to present the form), but for handling it's |
|
38 | * share of the form submission. |
|
39 | * </p> |
|
40 | * |
|
41 | * <p> |
|
42 | * Only after all that is done will the Form notify its listener. |
|
43 | * </p> |
|
44 | * |
|
45 | * <p> |
|
46 | * Release 4.0 adds two new listeners, {@link #getCancel()} and {@link #getRefresh()} and |
|
47 | * corresponding client-side behavior to force a form to refresh (update, bypassing input field |
|
48 | * validation) or cancel (update immediately). |
|
49 | * </p> |
|
50 | * |
|
51 | * @author Howard Lewis Ship, David Solis |
|
52 | */ |
|
53 | ||
54 | 10 | public abstract class Form extends AbstractComponent implements IForm |
55 | { |
|
56 | private String _name; |
|
57 | ||
58 | private FormSupport _formSupport; |
|
59 | ||
60 | /** |
|
61 | * Renders informal parameters. |
|
62 | * @author hls |
|
63 | */ |
|
64 | 10 | private class RenderInformalParameters implements IRender |
65 | { |
|
66 | public void render(IMarkupWriter writer, IRequestCycle cycle) |
|
67 | { |
|
68 | 0 | renderInformalParameters(writer, cycle); |
69 | 0 | } |
70 | } |
|
71 | ||
72 | private IRender _renderInformalParameters; |
|
73 | ||
74 | /** |
|
75 | * Indicates to any wrapped form components that they should respond to the form submission. |
|
76 | * |
|
77 | * @throws ApplicationRuntimeException |
|
78 | * if not rendering. |
|
79 | */ |
|
80 | ||
81 | public boolean isRewinding() |
|
82 | { |
|
83 | 4 | if (!isRendering()) |
84 | 0 | throw Tapestry.createRenderOnlyPropertyException(this, "rewinding"); |
85 | ||
86 | 4 | return _formSupport.isRewinding(); |
87 | } |
|
88 | ||
89 | /** |
|
90 | * Injected. |
|
91 | * |
|
92 | * @since 4.0 |
|
93 | */ |
|
94 | ||
95 | public abstract IEngineService getDirectService(); |
|
96 | ||
97 | /** |
|
98 | * Returns true if the stateful parameter is bound to a true value. If stateful is not bound, |
|
99 | * also returns the default, true. |
|
100 | * |
|
101 | * @since 1.0.1 |
|
102 | */ |
|
103 | ||
104 | public boolean getRequiresSession() |
|
105 | { |
|
106 | 0 | return isStateful(); |
107 | } |
|
108 | ||
109 | /** |
|
110 | * Constructs a unique identifier (within the Form). The identifier consists of the component's |
|
111 | * id, with an index number added to ensure uniqueness. |
|
112 | * <p> |
|
113 | * Simply invokes |
|
114 | * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the |
|
115 | * component's id. |
|
116 | * |
|
117 | * @since 1.0.2 |
|
118 | */ |
|
119 | ||
120 | public String getElementId(IFormComponent component) |
|
121 | { |
|
122 | 0 | return _formSupport.getElementId(component, component.getSpecifiedId()); |
123 | } |
|
124 | ||
125 | /** |
|
126 | * Constructs a unique identifier from the base id. If possible, the id is used as-is. |
|
127 | * Otherwise, a unique identifier is appended to the id. |
|
128 | * <p> |
|
129 | * This method is provided simply so that some components ({@link ImageSubmit}) have more |
|
130 | * specific control over their names. |
|
131 | * |
|
132 | * @since 1.0.3 |
|
133 | */ |
|
134 | ||
135 | public String getElementId(IFormComponent component, String baseId) |
|
136 | { |
|
137 | 0 | return _formSupport.getElementId(component, baseId); |
138 | } |
|
139 | ||
140 | /** |
|
141 | * {@inheritDoc} |
|
142 | */ |
|
143 | public String peekClientId(IFormComponent component) |
|
144 | { |
|
145 | 0 | return _formSupport.peekClientId(component); |
146 | } |
|
147 | ||
148 | /** |
|
149 | * Returns the name generated for the form. This is used to faciliate components that write |
|
150 | * JavaScript and need to access the form or its contents. |
|
151 | * <p> |
|
152 | * This value is generated when the form renders, and is not cleared. If the Form is inside a |
|
153 | * {@link org.apache.tapestry.components.ForBean}, this will be the most recently generated |
|
154 | * name for the Form. |
|
155 | * <p> |
|
156 | * This property is exposed so that sophisticated applications can write JavaScript handlers for |
|
157 | * the form and components within the form. |
|
158 | * |
|
159 | * @see AbstractFormComponent#getName() |
|
160 | */ |
|
161 | ||
162 | public String getName() |
|
163 | { |
|
164 | 0 | return _name; |
165 | } |
|
166 | ||
167 | /** @since 3.0 * */ |
|
168 | ||
169 | protected void prepareForRender(IRequestCycle cycle) |
|
170 | { |
|
171 | 4 | super.prepareForRender(cycle); |
172 | ||
173 | 4 | TapestryUtils.storeForm(cycle, this); |
174 | 4 | } |
175 | ||
176 | protected void cleanupAfterRender(IRequestCycle cycle) |
|
177 | { |
|
178 | 4 | super.cleanupAfterRender(cycle); |
179 | ||
180 | 4 | _formSupport = null; |
181 | ||
182 | 4 | TapestryUtils.removeForm(cycle); |
183 | ||
184 | 4 | IValidationDelegate delegate = getDelegate(); |
185 | ||
186 | 4 | if (delegate != null) |
187 | 4 | delegate.setFormComponent(null); |
188 | 4 | } |
189 | ||
190 | protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) |
|
191 | { |
|
192 | 4 | _formSupport = newFormSupport(writer, cycle); |
193 | ||
194 | 4 | if (isRewinding()) |
195 | { |
|
196 | 1 | String submitType = _formSupport.rewind(); |
197 | ||
198 | 1 | IActionListener listener = findListener(submitType); |
199 | ||
200 | 1 | getListenerInvoker().invokeListener(listener, this, cycle); |
201 | ||
202 | // Abort the rewind render. |
|
203 | ||
204 | 1 | throw new RenderRewoundException(this); |
205 | } |
|
206 | ||
207 | // Note: not safe to invoke getNamespace() in Portlet world |
|
208 | // except during a RenderRequest. |
|
209 | ||
210 | 3 | _name = getClientId() + getResponse().getNamespace(); |
211 | ||
212 | 3 | if (_renderInformalParameters == null) |
213 | 3 | _renderInformalParameters = new RenderInformalParameters(); |
214 | ||
215 | 3 | ILink link = getLink(cycle); |
216 | ||
217 | 3 | _formSupport.render(getMethod(), _renderInformalParameters, link, getScheme(), getPort()); |
218 | 3 | } |
219 | ||
220 | IActionListener findListener(String mode) |
|
221 | { |
|
222 | 7 | IActionListener result = null; |
223 | ||
224 | 7 | if (mode.equals(FormConstants.SUBMIT_CANCEL)) |
225 | 2 | result = getCancel(); |
226 | 5 | else if (mode.equals(FormConstants.SUBMIT_REFRESH)) |
227 | 2 | result = getRefresh(); |
228 | 3 | else if (!getDelegate().getHasErrors()) |
229 | 2 | result = getSuccess(); |
230 | ||
231 | // If not success, cancel or refresh, or the corresponding listener |
|
232 | // is itself null, then use the default listener |
|
233 | // (which may be null as well!). |
|
234 | ||
235 | 7 | if (result == null) |
236 | 4 | result = getListener(); |
237 | ||
238 | 7 | return result; |
239 | } |
|
240 | ||
241 | /** |
|
242 | * Returns a new instance of {@link FormSupportImpl}. |
|
243 | */ |
|
244 | ||
245 | protected FormSupport newFormSupport(IMarkupWriter writer, IRequestCycle cycle) |
|
246 | { |
|
247 | 0 | return getFormSupportFactory().createFormSupport(writer, cycle, this); |
248 | } |
|
249 | ||
250 | /** |
|
251 | * Adds an additional event handler. |
|
252 | * |
|
253 | * @since 1.0.2 |
|
254 | */ |
|
255 | ||
256 | public void addEventHandler(FormEventType type, String functionName) |
|
257 | { |
|
258 | 0 | _formSupport.addEventHandler(type, functionName); |
259 | 0 | } |
260 | ||
261 | /** |
|
262 | * Simply invokes {@link #render(IMarkupWriter, IRequestCycle)}. |
|
263 | * |
|
264 | * @since 1.0.2 |
|
265 | */ |
|
266 | ||
267 | public void rewind(IMarkupWriter writer, IRequestCycle cycle) |
|
268 | { |
|
269 | 0 | cycle.getResponseBuilder().render(writer, this, cycle); |
270 | 0 | } |
271 | ||
272 | /** |
|
273 | * Method invoked by the direct service. |
|
274 | * |
|
275 | * @since 1.0.2 |
|
276 | */ |
|
277 | ||
278 | public void trigger(IRequestCycle cycle) |
|
279 | { |
|
280 | 0 | cycle.rewindForm(this); |
281 | 0 | } |
282 | ||
283 | /** |
|
284 | * Builds the EngineServiceLink for the form. |
|
285 | * |
|
286 | * @since 1.0.3 |
|
287 | */ |
|
288 | ||
289 | private ILink getLink(IRequestCycle cycle) |
|
290 | { |
|
291 | 3 | Object parameter = new DirectServiceParameter(this); |
292 | ||
293 | 3 | return getDirectService().getLink(true, parameter); |
294 | } |
|
295 | ||
296 | /** Injected. */ |
|
297 | public abstract WebResponse getResponse(); |
|
298 | ||
299 | /** |
|
300 | * delegate parameter, which has a default (starting in release 4.0). |
|
301 | */ |
|
302 | ||
303 | public abstract IValidationDelegate getDelegate(); |
|
304 | ||
305 | /** listener parameter, may be null. */ |
|
306 | public abstract IActionListener getListener(); |
|
307 | ||
308 | /** success parameter, may be null. */ |
|
309 | public abstract IActionListener getSuccess(); |
|
310 | ||
311 | /** cancel parameter, may be null. */ |
|
312 | public abstract IActionListener getCancel(); |
|
313 | ||
314 | /** refresh parameter, may be null. */ |
|
315 | public abstract IActionListener getRefresh(); |
|
316 | ||
317 | /** method parameter. */ |
|
318 | public abstract String getMethod(); |
|
319 | ||
320 | /** stateful parameter. */ |
|
321 | public abstract boolean isStateful(); |
|
322 | ||
323 | /** scheme parameter, may be null. */ |
|
324 | public abstract String getScheme(); |
|
325 | ||
326 | /** port , may be null. */ |
|
327 | public abstract Integer getPort(); |
|
328 | ||
329 | public void setEncodingType(String encodingType) |
|
330 | { |
|
331 | 0 | _formSupport.setEncodingType(encodingType); |
332 | 0 | } |
333 | ||
334 | /** @since 3.0 */ |
|
335 | ||
336 | public void addHiddenValue(String name, String value) |
|
337 | { |
|
338 | 0 | _formSupport.addHiddenValue(name, value); |
339 | 0 | } |
340 | ||
341 | /** @since 3.0 */ |
|
342 | ||
343 | public void addHiddenValue(String name, String id, String value) |
|
344 | { |
|
345 | 0 | _formSupport.addHiddenValue(name, id, value); |
346 | 0 | } |
347 | ||
348 | public void prerenderField(IMarkupWriter writer, IComponent field, Location location) |
|
349 | { |
|
350 | 0 | _formSupport.prerenderField(writer, field, location); |
351 | 0 | } |
352 | ||
353 | public boolean wasPrerendered(IMarkupWriter writer, IComponent field) |
|
354 | { |
|
355 | 0 | return _formSupport.wasPrerendered(writer, field); |
356 | } |
|
357 | ||
358 | /** @since 4.0 */ |
|
359 | ||
360 | public void addDeferredRunnable(Runnable runnable) |
|
361 | { |
|
362 | 0 | _formSupport.addDeferredRunnable(runnable); |
363 | 0 | } |
364 | ||
365 | /** |
|
366 | * Injected. |
|
367 | * |
|
368 | * @since 4.0 |
|
369 | */ |
|
370 | ||
371 | public abstract ListenerInvoker getListenerInvoker(); |
|
372 | ||
373 | public void registerForFocus(IFormComponent field, int priority) |
|
374 | { |
|
375 | 0 | _formSupport.registerForFocus(field, priority); |
376 | 0 | } |
377 | ||
378 | /** |
|
379 | * {@inheritDoc} |
|
380 | */ |
|
381 | public JSONObject getProfile() |
|
382 | { |
|
383 | 0 | return _formSupport.getProfile(); |
384 | } |
|
385 | ||
386 | /** |
|
387 | * {@inheritDoc} |
|
388 | */ |
|
389 | public boolean isFormFieldUpdating() |
|
390 | { |
|
391 | 0 | return _formSupport.isFormFieldUpdating(); |
392 | } |
|
393 | ||
394 | /** |
|
395 | * {@inheritDoc} |
|
396 | */ |
|
397 | public void setFormFieldUpdating(boolean value) |
|
398 | { |
|
399 | 0 | _formSupport.setFormFieldUpdating(value); |
400 | 0 | } |
401 | ||
402 | /** |
|
403 | * Injected. |
|
404 | * |
|
405 | * @since 4.1.1 |
|
406 | */ |
|
407 | ||
408 | public abstract FormSupportFactory getFormSupportFactory(); |
|
409 | } |