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