Filter:   InfoImg
download Form.java
Language: Java
LOC: 330
Project Info
Tapestry: Java Web Components(tapestry)
Server: SourceForge
Type: cvs
[Show Code]






[Show Code]
...k\src\net\sf\tapestry\form\
   AbstractFormComponent.java
   AbstractTextField.java
   Button.java
   Button.jwc
   Checkbox.java
   Checkbox.jwc
   DatePicker.html
   DatePicker.java
   DatePicker.js
   DatePicker.jwc
   DatePicker.script
   ...ertySelectionModel.java
   Form.java
   Form.jwc
   FormEventType.java
   Hidden.java
   Hidden.jwc
   IFormComponent.java
   ImageSubmit.java
   ImageSubmit.jwc
   ...ertySelectionModel.java
   ...ySelectionRenderer.java
   ListEdit.java
   ListEdit.jwc
   Option.java
   Option.jwc
   package.html
   PropertySelection.java
   PropertySelection.jwc
   Radio.java
   Radio.jwc
   RadioGroup.java
   RadioGroup.jwc
   ...ySelectionRenderer.java
   Select.java
   Select.jwc
   ...ySelectionRenderer.java
   ...ertySelectionModel.java
   Submit.java
   Submit.jwc
   TextArea.java
   TextArea.jwc
   TextField.java
   TextField.jwc
   Upload.java
   Upload.jwc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
package net.sf.tapestry.form;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.tapestry.AbstractComponent;
import net.sf.tapestry.BindingException;
import net.sf.tapestry.Gesture;
import net.sf.tapestry.IActionListener;
import net.sf.tapestry.IBinding;
import net.sf.tapestry.IComponent;
import net.sf.tapestry.IDirect;
import net.sf.tapestry.IEngine;
import net.sf.tapestry.IEngineService;
import net.sf.tapestry.IForm;
import net.sf.tapestry.IMarkupWriter;
import net.sf.tapestry.IRequestCycle;
import net.sf.tapestry.RenderOnlyPropertyException;
import net.sf.tapestry.RenderRewoundException;
import net.sf.tapestry.RequestCycleException;
import net.sf.tapestry.RequiredParameterException;
import net.sf.tapestry.StaleLinkException;
import net.sf.tapestry.Tapestry;
import net.sf.tapestry.event.PageDetachListener;
import net.sf.tapestry.event.PageEvent;
import net.sf.tapestry.html.Body;
import net.sf.tapestry.valid.IValidationDelegate;

/**
 *  Component which contains form element components.  Forms use the
 *  action or direct services to handle the form submission.  A Form will wrap
 *  other components and static HTML, including
 *  form components such as {@link TextArea}, {@link TextField}, {@link Checkbox}, etc.
 * 
 *  [<a href="../../../../../ComponentReference/Form.html">Component Reference</a>]
 * 
 *  <p>When a form is submitted, it continues through the rewind cycle until
 *  <em>after</em> all of its wrapped elements have renderred.  As the form
 *  component render (in the rewind cycle), they will be updating
 *  properties of the containing page and notifying thier listeners.  Again:
 *  each form component is responsible not only for rendering HTML (to present the
 *  form), but for handling it's share of the form submission.
 *
 *  <p>Only after all that is done will the Form notify its listener.
 *
 *  <p>Starting in release 1.0.2, a Form can use either the direct service or
 *  the action service.  The default is the direct service, even though
 *  in earlier releases, only the action service was available.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: Form.java,v 1.20 2002/11/27 17:58:47 hship Exp $
 **/

public class Form extends AbstractComponent implements IForm, IDirect, PageDetachListener
{
    private String _method;
    private IActionListener _listener;
    private boolean _rewinding;
    private boolean _rendering;
    private String _name;
    private boolean _direct = true;
    private IValidationDelegate _delegate;

    // Need the stateful binding, since isStateful() can be invoked
    // when not rendering.

    private IBinding _statefulBinding;

    /**
     *  Number of element ids allocated.
     *
     *  @since 1.0.2
     *
     **/

    private int _elementCount;

    /**
     *  {@link Map}, keyed on {@link FormEventType}.  Values are either a String (the name
     *  of a single event), or a {@link List} of Strings.
     *
     *  @since 1.0.2
     **/

    private Map _events;

    private static final int EVENT_MAP_SIZE = 3;

    /**
     * A Map, keyed on component id, used to allocate new component ids.
     *
     * @since 1.0.2
     *
     **/

    private Map _allocatorMap;

    /**
     *  Class used to allocate ids (used as form element names).
     *
     **/

    private static class IdAllocator
    {
        String baseId;
        int index;

        IdAllocator(String baseId)
        {
            this.baseId = baseId + "_";
        }

        public String nextId()
        {
            return baseId + index++;
        }
    }

    /**
     *  Returns the currently active {@link IForm}, or null if no form is
     *  active.  This is a convienience method, the result will be
     *  null, or an instance of {@link IForm}, but not necessarily a
     *  <code>Form</code>.
     *
     **/

    public static IForm get(IRequestCycle cycle)
    {
        return (IForm) cycle.getAttribute(ATTRIBUTE_NAME);
    }

    /**
     *  Indicates to any wrapped form components that they should respond to the form
     *  submission.
     *
     *  @throws RenderOnlyPropertyException if not rendering.
     **/

    public boolean isRewinding()
    {
        if (!_rendering)
            throw new RenderOnlyPropertyException(this, "rewinding");

        return _rewinding;
    }

    /**
     *  Returns true if this Form is configured to use the direct
     *  service.
     *
     *  <p>This is derived from the direct parameter, and defaults
     *  to true if not bound.
     *
     *  @since 1.0.2
     **/

    public boolean isDirect()
    {
        return _direct;
    }

    /**
     *  Returns true if the stateful parameter is bound to
     *  a true value.  If stateful is not bound, also returns
     *  the default, true.
     *
     *  @since 1.0.1
     **/

    public boolean getRequiresSession()
    {
        // Can't rely on stateful property, since that is only valid
        // during render ... so we go direct to the binding.

        if (_statefulBinding == null)
            return true;

        return _statefulBinding.getBoolean();
    }

    /**
     *  Constructs a unique identifier (within the Form).  The identifier
     *  consists of the component's id, with an index number added to
     *  ensure uniqueness.
     *
     *  <p>Simply invokes {@link #getElementId(String)} with the component's id.
     *
     *
     *  @since 1.0.2
     **/

    public String getElementId(IComponent component)
    {
        return getElementId(component.getId());
    }

    /**
     *  Constructs a unique identifier from the base id.  If possible, the
     *  id is used as-is.  Otherwise, a unique identifier is appended
     *  to the id.
     *
     *  <p>This method is provided simply so that some components
     * ({@link ImageSubmit}) have more specific control over
     *  their names.
     *
     *  @since 1.0.3
     *
     **/

    public String getElementId(String baseId)
    {
        if (_allocatorMap == null)
            _allocatorMap = new HashMap();

        String result = null;

        IdAllocator allocator = (IdAllocator) _allocatorMap.get(baseId);

        if (allocator == null)
        {
            result = baseId;
            allocator = new IdAllocator(baseId);
        }
        else
            result = allocator.nextId();

        // Record the allocated id.  This protects against degenerate
        // names by the developer, such as 'foo' (in a Foreach) and
        // 'foo0' elsewhere.

        _allocatorMap.put(result, allocator);

        _elementCount++;

        return result;
    }

    /**
     *  Returns the name generated for the form.  This is used to faciliate
     *  components that write JavaScript and need to access the form or
     *  its contents.
     *
     *  <p>This value is generated when the form renders, and is not cleared.
     *  If the Form is inside a {@link net.sf.tapestry.components.Foreach}, 
     *  this will be the most recently
     *  generated name for the Form.
     *
     *  <p>This property is exposed so that sophisticated applications can write
     *  JavaScript handlers for the form and components within the form.
     *
     *  @see AbstractFormComponent#getName()
     *
     **/

    public String getName()
    {
        return _name;
    }

    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) throws RequestCycleException
    {
        if (cycle.getAttribute(ATTRIBUTE_NAME) != null)
            throw new RequestCycleException(Tapestry.getString("Form.forms-may-not-nest"), this);

        cycle.setAttribute(ATTRIBUTE_NAME, this);

        String actionId = cycle.getNextActionId();
        _name = "Form" + actionId;

        boolean renderForm = !cycle.isRewinding();
        boolean rewound = cycle.isRewound(this);

        _rewinding = rewound;

        Gesture g = getGesture(cycle, actionId);

        if (renderForm)
        {
            writer.begin("form");
            writer.attribute("method", (_method == null) ? "post" : _method);
            writer.attribute("name", _name);
            writer.attribute("action", g.getBareURL());

            generateAttributes(writer, cycle);
            writer.println();
        }

        // Write the hidden's, or at least, reserve the query parameters
        // required by the Gesture.

        writeGestureParameters(writer, g, !renderForm);

        _elementCount = 0;

        _rendering = true;
        renderBody(writer, cycle);

        if (renderForm)
        {
            // What's this for?  It's part of checking for stale links.  
            // We record how many elements we allocated ids for.
            // On rewind, we check that the same number of elements
            // ids were allocated.  If the persistent state of the page or
            // application changed between render (previous request cycle)
            // and rewind (current request cycle), then
            // this count might change.
            //
            // In some cases, state can change without changing this
            // number -- hopefully, such changes are benign since we
            // don't have a way to detect them.

            writer.beginEmpty("input");
            writer.attribute("type", "hidden");
            writer.attribute("name", _name);
            writer.attribute("value", _elementCount);
            writer.println();

            writer.end("form");

            // Write out event handlers collected during the rendering.

            emitEventHandlers(writer, cycle);
        }

        if (rewound)
        {
            String actual = cycle.getRequestContext().getParameter(_name);

            if (actual == null || Integer.parseInt(actual) != _elementCount)
                throw new StaleLinkException(Tapestry.getString("Form.bad-element-count", getExtendedId()), getPage());

            if (_listener != null)
                _listener.actionTriggered(this, cycle);

            // Abort the rewind render.

            throw new RenderRewoundException(this);
        }

        cycle.removeAttribute(ATTRIBUTE_NAME);
    }

    /**
     *  Adds an additional event handler.
     *
     *  @since 1.0.2
     * 
     **/

    public void addEventHandler(FormEventType type, String functionName)
    {
        if (_events == null)
            _events = new HashMap(EVENT_MAP_SIZE);

        Object value = _events.get(type);

        // The value can either be a String, or a List of String.  Since
        // it is rare for there to be more than one event handling function,
        // we start with just a String.

        if (value == null)
        {
            _events.put(type, functionName);
            return;
        }

        // The second function added converts it to a List.

        if (value instanceof String)
        {
            List list = new ArrayList();
            list.add(value);
            list.add(functionName);

            _events.put(type, list);
            return;
        }

        // The third and subsequent function justs
        // adds to the List.

        List list = (List) value;
        list.add(functionName);
    }

    private void emitEventHandlers(IMarkupWriter writer, IRequestCycle cycle) throws RequestCycleException
    {
        StringBuffer buffer = null;

        if (_events == null || _events.isEmpty())
            return;

        Body body = Body.get(cycle);

        if (body == null)
            throw new RequestCycleException(Tapestry.getString("Form.needs-body-for-event-handlers"), this);

        Iterator i = _events.entrySet().iterator();
        while (i.hasNext())
        {
            Map.Entry entry = (Map.Entry) i.next();
            FormEventType type = (FormEventType) entry.getKey();
            Object value = entry.getValue();

            String formPath = "document." + _name;
            String propertyName = type.getPropertyName();
            String finalFunctionName;

            boolean combineWithAnd = type.getCombineUsingAnd();

            // The typical case; one event one event handler.  Easy enough.

            if (value instanceof String)
            {
                finalFunctionName = (String) value;
            }
            else
            {

                String compositeName = propertyName + "_" + _name;

                if (buffer == null)
                    buffer = new StringBuffer(200);

                buffer.append("\nfunction ");
                buffer.append(compositeName);
                buffer.append("()\n{");

                List l = (List) value;
                int count = l.size();
               
                
                for (int j = 0; j < count; j++)
                {
                    String functionName = (String) l.get(j);

                    if (j > 0)
                    {
                        
                        if (combineWithAnd)
                            buffer.append(" &&");
                        else
                            buffer.append(";");
                    }

                    buffer.append("\n  ");

                    if (combineWithAnd)
                    {
                        if (j == 0)
                            buffer.append("return ");
                        else
                            buffer.append("  ");
                    }
    
                    buffer.append(functionName);
                    buffer.append("()");
                }

                buffer.append(";\n}\n\n");

                finalFunctionName = compositeName;
            }

            body.addOtherInitialization(formPath + "." + propertyName + " = " + finalFunctionName + ";");

        }

        if (buffer != null)
            body.addOtherScript(buffer.toString());

    }

    /**
     *  Simply invokes {@link #render(IMarkupWriter, IRequestCycle)}.
     *
     *  @since 1.0.2
     * 
     **/

    public void rewind(IMarkupWriter writer, IRequestCycle cycle) throws RequestCycleException
    {
        render(writer, cycle);
    }

    /**
     *  Method invoked by the direct service.
     *
     *  @since 1.0.2
     *
     **/

    public void trigger(IRequestCycle cycle) throws RequestCycleException
    {
        Object[] parameters = cycle.getServiceParameters();

        cycle.rewindForm(this, (String) parameters[0]);
    }

    /**
     *  Builds the Gesture for the form, using either the direct or
     *  action service. 
     *
     *  @since 1.0.3
     *
     **/

    private Gesture getGesture(IRequestCycle cycle, String actionId)
    {
        String serviceName = null;

        if (isDirect())
            serviceName = IEngineService.DIRECT_SERVICE;
        else
            serviceName = IEngineService.ACTION_SERVICE;

        IEngine engine = cycle.getEngine();
        IEngineService service = engine.getService(serviceName);

        // A single service parameter is used to store the actionId.

        return service.buildGesture(cycle, this, new String[] { actionId });
    }

    private void writeGestureParameters(IMarkupWriter writer, Gesture g, boolean reserveOnly)
    {
        String[] names = g.getParameterNames();
        int count = Tapestry.size(names);

        for (int i = 0; i < count; i++)
        {

            String name = names[i];

            // Reserve the name.

            getElementId(name);

            if (!reserveOnly)
                writeHiddenFieldsForParameter(writer, g, name);
        }
    }

    /**
     *  @since 2.2
     * 
     **/

    private void writeHiddenFieldsForParameter(IMarkupWriter writer, Gesture g, String parameterName)
    {
        String[] values = g.getParameterValues(parameterName);

        for (int i = 0; i < values.length; i++)
        {
            writer.beginEmpty("input");
            writer.attribute("type", "hidden");
            writer.attribute("name", parameterName);
            writer.attribute("value", values[i]);
            writer.println();
        }
    }

    protected void cleanupAfterRender(IRequestCycle cycle)
    {
        _rendering = false;
        _elementCount = 0;
        _events = null;
        _allocatorMap = null;

        super.cleanupAfterRender(cycle);

    }

    /**
     *  Clears the delegate property at the end of the request cycle.
     * 
     **/

    public void pageDetached(PageEvent event)
    {
        _delegate = null;
    }

    /**
     *  Adds this Form as a page detach listener, so that the delegate property
     *  can be cleared at the end of the request cycle.
     * 
     **/

    protected void finishLoad()
    {
        getPage().addPageDetachListener(this);
    }

    public IValidationDelegate getDelegate()
    {
        return _delegate;
    }

    public void setDelegate(IValidationDelegate delegate)
    {
        _delegate = delegate;
    }

    public void setDirect(boolean direct)
    {
        _direct = direct;
    }

    public IActionListener getListener()
    {
        return _listener;
    }

    public void setListener(IActionListener listener)
    {
        _listener = listener;
    }

    public String getMethod()
    {
        return _method;
    }

    public void setMethod(String method)
    {
        _method = method;
    }

    /**
     *  Invoked when not rendering, so it uses the stateful binding.
     *  If not bound, returns true.
     * 
     **/

    public boolean isStateful()
    {
        if (_statefulBinding == null)
            return true;

        return _statefulBinding.getBoolean();
    }

    public IBinding getStatefulBinding()
    {
        return _statefulBinding;
    }

    public void setStatefulBinding(IBinding statefulBinding)
    {
        _statefulBinding = statefulBinding;
    }
}