1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| package org.apache.tapestry.form; |
16 |
| |
17 |
| import java.util.ArrayList; |
18 |
| import java.util.Arrays; |
19 |
| import java.util.Collections; |
20 |
| import java.util.HashMap; |
21 |
| import java.util.HashSet; |
22 |
| import java.util.Iterator; |
23 |
| import java.util.List; |
24 |
| import java.util.Map; |
25 |
| import java.util.Set; |
26 |
| |
27 |
| import org.apache.hivemind.ApplicationRuntimeException; |
28 |
| import org.apache.hivemind.HiveMind; |
29 |
| import org.apache.hivemind.Location; |
30 |
| import org.apache.hivemind.Resource; |
31 |
| import org.apache.hivemind.util.ClasspathResource; |
32 |
| import org.apache.hivemind.util.Defense; |
33 |
| import org.apache.tapestry.IComponent; |
34 |
| import org.apache.tapestry.IForm; |
35 |
| import org.apache.tapestry.IMarkupWriter; |
36 |
| import org.apache.tapestry.IRender; |
37 |
| import org.apache.tapestry.IRequestCycle; |
38 |
| import org.apache.tapestry.NestedMarkupWriter; |
39 |
| import org.apache.tapestry.PageRenderSupport; |
40 |
| import org.apache.tapestry.StaleLinkException; |
41 |
| import org.apache.tapestry.Tapestry; |
42 |
| import org.apache.tapestry.TapestryUtils; |
43 |
| import org.apache.tapestry.engine.ILink; |
44 |
| import org.apache.tapestry.services.ServiceConstants; |
45 |
| import org.apache.tapestry.util.IdAllocator; |
46 |
| import org.apache.tapestry.valid.IValidationDelegate; |
47 |
| |
48 |
| |
49 |
| |
50 |
| |
51 |
| |
52 |
| |
53 |
| |
54 |
| public class FormSupportImpl implements FormSupport |
55 |
| { |
56 |
| |
57 |
| |
58 |
| |
59 |
| |
60 |
| |
61 |
| |
62 |
| public static final String FORM_IDS = "formids"; |
63 |
| |
64 |
| |
65 |
| |
66 |
| |
67 |
| |
68 |
| |
69 |
| |
70 |
| public static final String RESERVED_FORM_IDS = "reservedids"; |
71 |
| |
72 |
| |
73 |
| |
74 |
| |
75 |
| |
76 |
| |
77 |
| public static final String SUBMIT_MODE = "submitmode"; |
78 |
| |
79 |
| public static final String SCRIPT = "/org/apache/tapestry/form/Form.js"; |
80 |
| |
81 |
| private final static Set _standardReservedIds; |
82 |
| |
83 |
| |
84 |
| |
85 |
| |
86 |
| |
87 |
| |
88 |
| public static final String FIELD_FOCUS_ATTRIBUTE = "org.apache.tapestry.field-focused"; |
89 |
| |
90 |
| static |
91 |
| { |
92 |
1
| Set set = new HashSet();
|
93 |
| |
94 |
1
| set.addAll(Arrays.asList(ServiceConstants.RESERVED_IDS));
|
95 |
1
| set.add(FORM_IDS);
|
96 |
1
| set.add(RESERVED_FORM_IDS);
|
97 |
1
| set.add(SUBMIT_MODE);
|
98 |
| |
99 |
1
| _standardReservedIds = Collections.unmodifiableSet(set);
|
100 |
| } |
101 |
| |
102 |
| private final static Set _submitModes; |
103 |
| |
104 |
| static |
105 |
| { |
106 |
1
| Set set = new HashSet();
|
107 |
1
| set.add(FormConstants.SUBMIT_CANCEL);
|
108 |
1
| set.add(FormConstants.SUBMIT_NORMAL);
|
109 |
1
| set.add(FormConstants.SUBMIT_REFRESH);
|
110 |
| |
111 |
1
| _submitModes = Collections.unmodifiableSet(set);
|
112 |
| } |
113 |
| |
114 |
| |
115 |
| |
116 |
| |
117 |
| |
118 |
| |
119 |
| private int _allocatedIdIndex; |
120 |
| |
121 |
| |
122 |
| |
123 |
| |
124 |
| |
125 |
| |
126 |
| private final List _allocatedIds = new ArrayList(); |
127 |
| |
128 |
| private final IRequestCycle _cycle; |
129 |
| |
130 |
| private final IdAllocator _elementIdAllocator = new IdAllocator(); |
131 |
| |
132 |
| private String _encodingType; |
133 |
| |
134 |
| private final List _deferredRunnables = new ArrayList(); |
135 |
| |
136 |
| |
137 |
| |
138 |
| |
139 |
| |
140 |
| private final Map _prerenderMap = new HashMap(); |
141 |
| |
142 |
| |
143 |
| |
144 |
| |
145 |
| |
146 |
| |
147 |
| |
148 |
| private Map _events; |
149 |
| |
150 |
| private final IForm _form; |
151 |
| |
152 |
| private final List _hiddenValues = new ArrayList(); |
153 |
| |
154 |
| private final boolean _rewinding; |
155 |
| |
156 |
| private final IMarkupWriter _writer; |
157 |
| |
158 |
| private final Resource _script; |
159 |
| |
160 |
| private final IValidationDelegate _delegate; |
161 |
| |
162 |
| private final PageRenderSupport _pageRenderSupport; |
163 |
| |
164 |
92
| public FormSupportImpl(IMarkupWriter writer, IRequestCycle cycle, IForm form)
|
165 |
| { |
166 |
92
| Defense.notNull(writer, "writer");
|
167 |
92
| Defense.notNull(cycle, "cycle");
|
168 |
92
| Defense.notNull(form, "form");
|
169 |
| |
170 |
92
| _writer = writer;
|
171 |
92
| _cycle = cycle;
|
172 |
92
| _form = form;
|
173 |
92
| _delegate = form.getDelegate();
|
174 |
| |
175 |
92
| _rewinding = cycle.isRewound(form);
|
176 |
92
| _allocatedIdIndex = 0;
|
177 |
| |
178 |
92
| _script = new ClasspathResource(cycle.getEngine().getClassResolver(), SCRIPT);
|
179 |
| |
180 |
92
| _pageRenderSupport = TapestryUtils.getOptionalPageRenderSupport(cycle);
|
181 |
| } |
182 |
| |
183 |
| |
184 |
| |
185 |
| |
186 |
| |
187 |
6
| public void addEventHandler(FormEventType type, String functionName)
|
188 |
| { |
189 |
6
| if (_events == null)
|
190 |
3
| _events = new HashMap();
|
191 |
| |
192 |
6
| List functionList = (List) _events.get(type);
|
193 |
| |
194 |
| |
195 |
| |
196 |
| |
197 |
| |
198 |
6
| if (functionList == null)
|
199 |
| { |
200 |
3
| functionList = new ArrayList();
|
201 |
| |
202 |
3
| _events.put(type, functionList);
|
203 |
| } |
204 |
| |
205 |
6
| functionList.add(functionName);
|
206 |
| } |
207 |
| |
208 |
| |
209 |
| |
210 |
| |
211 |
| |
212 |
| |
213 |
| |
214 |
| |
215 |
| |
216 |
| |
217 |
57
| private void addHiddenFieldsForLinkParameters(ILink link)
|
218 |
| { |
219 |
57
| String[] names = link.getParameterNames();
|
220 |
57
| int count = Tapestry.size(names);
|
221 |
| |
222 |
57
| StringBuffer extraIds = new StringBuffer();
|
223 |
57
| String sep = "";
|
224 |
57
| boolean hasExtra = false;
|
225 |
| |
226 |
| |
227 |
| |
228 |
| |
229 |
| |
230 |
| |
231 |
57
| preallocateReservedIds();
|
232 |
| |
233 |
57
| for (int i = 0; i < count; i++)
|
234 |
| { |
235 |
292
| String name = names[i];
|
236 |
| |
237 |
| |
238 |
| |
239 |
292
| if (!_standardReservedIds.contains(name))
|
240 |
| { |
241 |
12
| _elementIdAllocator.allocateId(name);
|
242 |
| |
243 |
12
| extraIds.append(sep);
|
244 |
12
| extraIds.append(name);
|
245 |
| |
246 |
12
| sep = ",";
|
247 |
12
| hasExtra = true;
|
248 |
| } |
249 |
| |
250 |
292
| addHiddenFieldsForLinkParameter(link, name);
|
251 |
| } |
252 |
| |
253 |
57
| if (hasExtra)
|
254 |
12
| addHiddenValue(RESERVED_FORM_IDS, extraIds.toString());
|
255 |
| } |
256 |
| |
257 |
281
| public void addHiddenValue(String name, String value)
|
258 |
| { |
259 |
281
| _hiddenValues.add(new HiddenFieldData(name, value));
|
260 |
| } |
261 |
| |
262 |
5
| public void addHiddenValue(String name, String id, String value)
|
263 |
| { |
264 |
5
| _hiddenValues.add(new HiddenFieldData(name, id, value));
|
265 |
| } |
266 |
| |
267 |
| |
268 |
| |
269 |
| |
270 |
| |
271 |
| |
272 |
| |
273 |
52
| private String buildAllocatedIdList()
|
274 |
| { |
275 |
52
| StringBuffer buffer = new StringBuffer();
|
276 |
52
| int count = _allocatedIds.size();
|
277 |
| |
278 |
52
| for (int i = 0; i < count; i++)
|
279 |
| { |
280 |
56
| if (i > 0)
|
281 |
19
| buffer.append(',');
|
282 |
| |
283 |
56
| buffer.append(_allocatedIds.get(i));
|
284 |
| } |
285 |
| |
286 |
52
| return buffer.toString();
|
287 |
| } |
288 |
| |
289 |
52
| private void emitEventHandlers(String eventManager)
|
290 |
| { |
291 |
52
| if (_events == null || _events.isEmpty())
|
292 |
49
| return;
|
293 |
| |
294 |
3
| StringBuffer buffer = new StringBuffer();
|
295 |
| |
296 |
3
| Iterator i = _events.entrySet().iterator();
|
297 |
| |
298 |
3
| while (i.hasNext())
|
299 |
| { |
300 |
3
| Map.Entry entry = (Map.Entry) i.next();
|
301 |
3
| FormEventType type = (FormEventType) entry.getKey();
|
302 |
3
| Object value = entry.getValue();
|
303 |
| |
304 |
3
| buffer.append(eventManager);
|
305 |
3
| buffer.append(".");
|
306 |
3
| buffer.append(type.getAddListenerMethodName());
|
307 |
| |
308 |
| |
309 |
| |
310 |
3
| buffer.append("(function (event)\n{");
|
311 |
| |
312 |
3
| List l = (List) value;
|
313 |
3
| int count = l.size();
|
314 |
| |
315 |
3
| for (int j = 0; j < count; j++)
|
316 |
| { |
317 |
6
| String functionName = (String) l.get(j);
|
318 |
| |
319 |
6
| if (j > 0)
|
320 |
| { |
321 |
3
| buffer.append(";");
|
322 |
| } |
323 |
| |
324 |
6
| buffer.append("\n ");
|
325 |
6
| buffer.append(functionName);
|
326 |
| |
327 |
| |
328 |
| |
329 |
| |
330 |
6
| if (!functionName.endsWith(")"))
|
331 |
| { |
332 |
5
| buffer.append("()");
|
333 |
| } |
334 |
| } |
335 |
| |
336 |
3
| buffer.append(";\n});\n");
|
337 |
| } |
338 |
| |
339 |
| |
340 |
| |
341 |
3
| _pageRenderSupport.addInitializationScript(buffer.toString());
|
342 |
| } |
343 |
| |
344 |
| |
345 |
| |
346 |
| |
347 |
| |
348 |
| |
349 |
| |
350 |
| |
351 |
| |
352 |
| |
353 |
19
| public String getElementId(IFormComponent component)
|
354 |
| { |
355 |
19
| return getElementId(component, component.getId());
|
356 |
| } |
357 |
| |
358 |
| |
359 |
| |
360 |
| |
361 |
| |
362 |
| |
363 |
| |
364 |
| |
365 |
| |
366 |
| |
367 |
109
| public String getElementId(IFormComponent component, String baseId)
|
368 |
| { |
369 |
109
| String result = _elementIdAllocator.allocateId(baseId);
|
370 |
| |
371 |
109
| if (_rewinding)
|
372 |
| { |
373 |
51
| if (_allocatedIdIndex >= _allocatedIds.size())
|
374 |
| { |
375 |
1
| throw new StaleLinkException(FormMessages.formTooManyIds(_form, _allocatedIds
|
376 |
| .size(), component), component); |
377 |
| } |
378 |
| |
379 |
50
| String expected = (String) _allocatedIds.get(_allocatedIdIndex);
|
380 |
| |
381 |
50
| if (!result.equals(expected))
|
382 |
2
| throw new StaleLinkException(FormMessages.formIdMismatch(
|
383 |
| _form, |
384 |
| _allocatedIdIndex, |
385 |
| expected, |
386 |
| result, |
387 |
| component), component); |
388 |
| } |
389 |
| else |
390 |
| { |
391 |
58
| _allocatedIds.add(result);
|
392 |
| } |
393 |
| |
394 |
106
| _allocatedIdIndex++;
|
395 |
| |
396 |
106
| component.setName(result);
|
397 |
| |
398 |
106
| return result;
|
399 |
| } |
400 |
| |
401 |
150
| public boolean isRewinding()
|
402 |
| { |
403 |
150
| return _rewinding;
|
404 |
| } |
405 |
| |
406 |
91
| private void preallocateReservedIds()
|
407 |
| { |
408 |
91
| for (int i = 0; i < ServiceConstants.RESERVED_IDS.length; i++)
|
409 |
546
| _elementIdAllocator.allocateId(ServiceConstants.RESERVED_IDS[i]);
|
410 |
| } |
411 |
| |
412 |
| |
413 |
| |
414 |
| |
415 |
| |
416 |
| |
417 |
| |
418 |
| |
419 |
| |
420 |
| |
421 |
| |
422 |
34
| private void reinitializeIdAllocatorForRewind()
|
423 |
| { |
424 |
34
| String allocatedFormIds = _cycle.getParameter(FORM_IDS);
|
425 |
| |
426 |
34
| String[] ids = TapestryUtils.split(allocatedFormIds);
|
427 |
| |
428 |
34
| for (int i = 0; i < ids.length; i++)
|
429 |
55
| _allocatedIds.add(ids[i]);
|
430 |
| |
431 |
| |
432 |
| |
433 |
| |
434 |
34
| preallocateReservedIds();
|
435 |
| |
436 |
34
| String extraReservedIds = _cycle.getParameter(RESERVED_FORM_IDS);
|
437 |
| |
438 |
34
| ids = TapestryUtils.split(extraReservedIds);
|
439 |
| |
440 |
34
| for (int i = 0; i < ids.length; i++)
|
441 |
3
| _elementIdAllocator.allocateId(ids[i]);
|
442 |
| } |
443 |
| |
444 |
57
| public void render(String method, IRender informalParametersRenderer, ILink link)
|
445 |
| { |
446 |
57
| String eventManager = emitEventManagerInitialization();
|
447 |
| |
448 |
| |
449 |
| |
450 |
| |
451 |
57
| addHiddenFieldsForLinkParameters(link);
|
452 |
| |
453 |
| |
454 |
| |
455 |
| |
456 |
57
| addHiddenValue(SUBMIT_MODE, null);
|
457 |
| |
458 |
57
| IMarkupWriter nested = _writer.getNestedWriter();
|
459 |
| |
460 |
57
| _form.renderBody(nested, _cycle);
|
461 |
| |
462 |
52
| runDeferredRunnables();
|
463 |
| |
464 |
52
| writeTag(_writer, method, link.getURL(null, false));
|
465 |
| |
466 |
52
| _writer.attribute("name", _form.getName());
|
467 |
| |
468 |
52
| if (_encodingType != null)
|
469 |
3
| _writer.attribute("enctype", _encodingType);
|
470 |
| |
471 |
| |
472 |
| |
473 |
52
| emitEventHandlers(eventManager);
|
474 |
| |
475 |
52
| informalParametersRenderer.render(_writer, _cycle);
|
476 |
| |
477 |
| |
478 |
| |
479 |
52
| _writer.println();
|
480 |
| |
481 |
52
| writeHiddenField(FORM_IDS, null, buildAllocatedIdList());
|
482 |
52
| writeHiddenFields();
|
483 |
| |
484 |
| |
485 |
| |
486 |
52
| nested.close();
|
487 |
| |
488 |
| |
489 |
| |
490 |
52
| _writer.end();
|
491 |
| |
492 |
52
| String field = _delegate.getFocusField();
|
493 |
| |
494 |
52
| if (field == null || _pageRenderSupport == null)
|
495 |
29
| return;
|
496 |
| |
497 |
| |
498 |
| |
499 |
| |
500 |
23
| if (!_form.getFocus() || _cycle.getAttribute(FIELD_FOCUS_ATTRIBUTE) != null)
|
501 |
1
| return;
|
502 |
| |
503 |
22
| _pageRenderSupport.addInitializationScript("focus(document." + _form.getName() + "."
|
504 |
| + field + ");"); |
505 |
| |
506 |
22
| _cycle.setAttribute(FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE);
|
507 |
| } |
508 |
| |
509 |
| |
510 |
| |
511 |
| |
512 |
| |
513 |
51
| protected String emitEventManagerInitialization()
|
514 |
| { |
515 |
51
| if (_pageRenderSupport == null)
|
516 |
2
| return null;
|
517 |
| |
518 |
49
| _pageRenderSupport.addExternalScript(_script);
|
519 |
| |
520 |
49
| String formName = _form.getName();
|
521 |
| |
522 |
49
| String eventManager = formName + "_events";
|
523 |
| |
524 |
49
| _pageRenderSupport.addInitializationScript("var " + eventManager
|
525 |
| + " = new FormEventManager(document." + formName + ");"); |
526 |
| |
527 |
49
| return eventManager;
|
528 |
| } |
529 |
| |
530 |
35
| public String rewind()
|
531 |
| { |
532 |
35
| _form.getDelegate().clear();
|
533 |
| |
534 |
35
| String mode = _cycle.getParameter(SUBMIT_MODE);
|
535 |
| |
536 |
| |
537 |
| |
538 |
35
| if (FormConstants.SUBMIT_CANCEL.equals(mode))
|
539 |
1
| return mode;
|
540 |
| |
541 |
34
| reinitializeIdAllocatorForRewind();
|
542 |
| |
543 |
34
| _form.renderBody(_writer, _cycle);
|
544 |
| |
545 |
30
| int expected = _allocatedIds.size();
|
546 |
| |
547 |
| |
548 |
| |
549 |
| |
550 |
| |
551 |
30
| if (_allocatedIdIndex < expected)
|
552 |
| { |
553 |
1
| String nextExpectedId = (String) _allocatedIds.get(_allocatedIdIndex);
|
554 |
| |
555 |
1
| throw new StaleLinkException(FormMessages.formTooFewIds(_form, expected
|
556 |
| - _allocatedIdIndex, nextExpectedId), _form); |
557 |
| } |
558 |
| |
559 |
29
| runDeferredRunnables();
|
560 |
| |
561 |
29
| if (_submitModes.contains(mode))
|
562 |
5
| return mode;
|
563 |
| |
564 |
| |
565 |
| |
566 |
| |
567 |
24
| return FormConstants.SUBMIT_NORMAL;
|
568 |
| |
569 |
| } |
570 |
| |
571 |
81
| private void runDeferredRunnables()
|
572 |
| { |
573 |
81
| Iterator i = _deferredRunnables.iterator();
|
574 |
81
| while (i.hasNext())
|
575 |
| { |
576 |
2
| Runnable r = (Runnable) i.next();
|
577 |
| |
578 |
2
| r.run();
|
579 |
| } |
580 |
| } |
581 |
| |
582 |
5
| public void setEncodingType(String encodingType)
|
583 |
| { |
584 |
| |
585 |
5
| if (_encodingType != null && !_encodingType.equals(encodingType))
|
586 |
1
| throw new ApplicationRuntimeException(FormMessages.encodingTypeContention(
|
587 |
| _form, |
588 |
| _encodingType, |
589 |
| encodingType), _form, null, null); |
590 |
| |
591 |
4
| _encodingType = encodingType;
|
592 |
| } |
593 |
| |
594 |
280
| protected void writeHiddenField(IMarkupWriter writer, String name, String id, String value)
|
595 |
| { |
596 |
280
| writer.beginEmpty("input");
|
597 |
280
| writer.attribute("type", "hidden");
|
598 |
280
| writer.attribute("name", name);
|
599 |
| |
600 |
280
| if (HiveMind.isNonBlank(id))
|
601 |
2
| writer.attribute("id", id);
|
602 |
| |
603 |
280
| writer.attribute("value", value == null ? "" : value);
|
604 |
280
| writer.println();
|
605 |
| } |
606 |
| |
607 |
316
| private void writeHiddenField(String name, String id, String value)
|
608 |
| { |
609 |
316
| writeHiddenField(_writer, name, id, value);
|
610 |
| } |
611 |
| |
612 |
| |
613 |
| |
614 |
| |
615 |
| |
616 |
| |
617 |
52
| private void writeHiddenFields()
|
618 |
| { |
619 |
52
| Iterator i = _hiddenValues.iterator();
|
620 |
52
| while (i.hasNext())
|
621 |
| { |
622 |
264
| HiddenFieldData data = (HiddenFieldData) i.next();
|
623 |
| |
624 |
264
| writeHiddenField(data.getName(), data.getId(), data.getValue());
|
625 |
| } |
626 |
| } |
627 |
| |
628 |
292
| private void addHiddenFieldsForLinkParameter(ILink link, String parameterName)
|
629 |
| { |
630 |
292
| String[] values = link.getParameterValues(parameterName);
|
631 |
| |
632 |
| |
633 |
| |
634 |
292
| if (values == null)
|
635 |
109
| return;
|
636 |
| |
637 |
183
| for (int i = 0; i < values.length; i++)
|
638 |
| { |
639 |
183
| addHiddenValue(parameterName, values[i]);
|
640 |
| } |
641 |
| } |
642 |
| |
643 |
46
| protected void writeTag(IMarkupWriter writer, String method, String url)
|
644 |
| { |
645 |
46
| writer.begin("form");
|
646 |
46
| writer.attribute("method", method);
|
647 |
46
| writer.attribute("action", url);
|
648 |
| } |
649 |
| |
650 |
0
| public void prerenderField(IMarkupWriter writer, IComponent field, Location location)
|
651 |
| { |
652 |
0
| Defense.notNull(writer, "writer");
|
653 |
0
| Defense.notNull(field, "field");
|
654 |
| |
655 |
0
| String key = field.getExtendedId();
|
656 |
| |
657 |
0
| if (_prerenderMap.containsKey(key))
|
658 |
0
| throw new ApplicationRuntimeException(FormMessages.fieldAlreadyPrerendered(field),
|
659 |
| location, null); |
660 |
| |
661 |
0
| NestedMarkupWriter nested = writer.getNestedWriter();
|
662 |
| |
663 |
0
| field.render(nested, _cycle);
|
664 |
| |
665 |
0
| _prerenderMap.put(key, nested.getBuffer());
|
666 |
| } |
667 |
| |
668 |
75
| public boolean wasPrerendered(IMarkupWriter writer, IComponent field)
|
669 |
| { |
670 |
75
| String key = field.getExtendedId();
|
671 |
| |
672 |
75
| String buffer = (String) _prerenderMap.get(key);
|
673 |
| |
674 |
75
| if (buffer == null)
|
675 |
75
| return false;
|
676 |
| |
677 |
0
| writer.printRaw(buffer);
|
678 |
| |
679 |
0
| _prerenderMap.remove(key);
|
680 |
| |
681 |
0
| return true;
|
682 |
| } |
683 |
| |
684 |
2
| public void addDeferredRunnable(Runnable runnable)
|
685 |
| { |
686 |
2
| Defense.notNull(runnable, "runnable");
|
687 |
| |
688 |
2
| _deferredRunnables.add(runnable);
|
689 |
| } |
690 |
| |
691 |
0
| public void registerForFocus(IFormComponent field, int priority)
|
692 |
| { |
693 |
0
| _delegate.registerForFocus(field, priority);
|
694 |
| } |
695 |
| |
696 |
| } |