KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > SForm


1 /*
2  * $Id: SForm.java,v 1.11 2005/04/08 15:36:07 blueshift Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.wings.plaf.FormCG;
19
20 import javax.swing.event.EventListenerList JavaDoc;
21 import java.awt.event.ActionEvent JavaDoc;
22 import java.awt.event.ActionListener JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.LinkedList JavaDoc;
27 import java.util.List JavaDoc;
28
29
30 /**
31  * A Form Container. In HTML you need to wrap input fields (i.e. <code>STextField</code>)
32  * in a SForm object to work correctly. The browser uses this object/tag to identify
33  * how (POST or GET) and where to send an request originating from any input
34  * inside this form.
35  *
36  * <p><b>Note:</b>Please be aware, that some components render differently if
37  * places inside a <code>SForm</code> or not.
38  *
39  * @author <a HREF="mailto:armin.haaf@mercatis.de">Armin Haaf</a>
40  * @version $Revision: 1.11 $
41  */

42 public class SForm extends SContainer implements LowLevelEventListener {
43     private final transient static Log log = LogFactory.getLog(SForm.class);
44
45     /**
46      * Default Form encoding type. See {@link #setEncodingType(String)}.
47      */

48     // TODO check this encoding type!
49
public final static String JavaDoc ENC_TYPE_TEXT_PLAIN = "text/plain";
50     /**
51      * Multipart form encoding. Needed for file uploads. See {@link #setEncodingType(String)}.
52      */

53     public final static String JavaDoc ENC_TYPE_MULTIPART_FORM = "multipart/form-data";
54     /**
55      * URL form encoding. See {@link #setEncodingType(String)}.
56      */

57     public static final String JavaDoc URL_ENCODING = "application/x-www-form-urlencoded";
58
59     /**
60      * Use method POST for submission of the data.
61      */

62     private boolean postMethod = true;
63
64     /**
65      * EncondingType for submission of the data.
66      */

67     private String JavaDoc encType;
68
69     /**
70      * URL to which data
71      * should be sent to
72      */

73     private URL JavaDoc action;
74
75     protected final EventListenerList JavaDoc listenerList = new EventListenerList JavaDoc();
76
77     protected String JavaDoc actionCommand;
78
79     /**
80      * the button, that is activated, if no other button is pressed in this
81      * form.
82      */

83     private SButton defaultButton;
84
85     /**
86      * the WingS event thread is the servlet doGet()/doPost() context
87      * thread. Within this thread, we collect all armed components. A
88      * 'armed' component is a component, that will 'fire' an event after the
89      * first processRequest() stage is completed.
90      */

91     private static ThreadLocal JavaDoc threadArmedComponents = new ThreadLocal JavaDoc() {
92         protected synchronized Object JavaDoc initialValue() {
93             return new ArrayList JavaDoc(2);
94         }
95     };
96
97     /**
98      * Create a standard form component.
99      */

100     public SForm() {
101     }
102
103     /**
104      * Create a standard form component but redirects the request to the passed
105      * URL. Use this i.e. to address other servlets.
106      *
107      * @param action The target URL.
108      */

109     public SForm(URL JavaDoc action) {
110         setAction(action);
111     }
112
113
114     /**
115      * Create a standard form component.
116      *
117      * @param layout The layout to apply to this container.
118      * @see SContainer
119      */

120     public SForm(SLayoutManager layout) {
121         super(layout);
122     }
123
124     /**
125      * A SForm fires an event each time it was triggered (i.e. pressing asubmit button inside)
126      *
127      * @param actionCommand The action command to place insiside the {@link ActionEvent}
128      */

129     public void setActionCommand(String JavaDoc actionCommand) {
130         this.actionCommand = actionCommand;
131     }
132
133     /**
134      * @see #setActionCommand(String)
135      */

136     public String JavaDoc getActionCommand() {
137         return actionCommand;
138     }
139
140     /**
141      * Set the default button activated upon <b>enter</b>.
142      * The button is triggered if you press <b>enter</b> inside a form to submit it.
143      * @param defaultButton A button which will be rendered <b>invisible</b>.
144      * If <code>null</code> enter key pressed will be catched by the wings framework.
145      */

146     public void setDefaultButton(SButton defaultButton) {
147         this.defaultButton = defaultButton;
148     }
149
150     /**
151      * @see #setDefaultButton(SButton)
152      */

153     public SButton getDefaultButton() {
154         return this.defaultButton;
155     }
156
157     /**
158      * Add a listener for Form events. A Form event is always triggered, when
159      * a form has been submitted. Usually, this happens, whenever a submit
160      * button is pressed or some other mechanism triggered the posting of the
161      * form. Other mechanisms are
162      * <ul>
163      * <li> Java Script submit() event</li>
164      * <li> If a form contains a single text input, then many browsers
165      * submit the form, if the user presses RETURN in that field. In that
166      * case, the submit button will <em>not</em> receive any event but
167      * only the form.
168      * <li> The {@link SFileChooser} will trigger a form event, if the file
169      * size exceeded the allowed size. In that case, even if the submit
170      * button has been pressed, no submit-button event will be triggered.
171      * (For details, see {@link SFileChooser}).
172      * </ul>
173      * Form events are guaranteed to be triggered <em>after</em> all
174      * Selection-Changes and Button ActionListeners.
175      */

176     public void addActionListener(ActionListener JavaDoc listener) {
177         listenerList.add(ActionListener JavaDoc.class, listener);
178     }
179
180     /**
181      * Remove a form action listener, that has been added in
182      * {@link #addActionListener(ActionListener)}
183      */

184     public void removeActionListener(ActionListener JavaDoc listener) {
185         listenerList.remove(ActionListener JavaDoc.class, listener);
186     }
187
188     /**
189      * Fire a ActionEvent at each registered listener.
190      */

191     protected void fireActionPerformed(String JavaDoc pActionCommand) {
192         ActionEvent JavaDoc e = null;
193         // Guaranteed to return a non-null array
194
Object JavaDoc[] listeners = listenerList.getListenerList();
195         // Process the listeners last to first, notifying
196
// those that are interested in this event
197
for (int i = listeners.length - 2; i >= 0; i -= 2) {
198             if (listeners[i] == ActionListener JavaDoc.class) {
199                 // lazy create ActionEvent
200
if (e == null) {
201                     e = new ActionEvent JavaDoc(this, ActionEvent.ACTION_PERFORMED,
202                             pActionCommand);
203                 }
204                 ((ActionListener JavaDoc) listeners[i + 1]).actionPerformed(e);
205             }
206         }
207     }
208
209     /*
210      * fixme: the following static function should go in some global
211      * class.
212      */

213
214     public final static void addArmedComponent(LowLevelEventListener component) {
215         List JavaDoc armedComponents = (List JavaDoc) threadArmedComponents.get();
216         armedComponents.add(component);
217     }
218
219     /**
220      * clear armed components. This is usually not necessary, since sessions
221      * clear clear their armed components. But if there was some Exception, it
222      * might well be, that this does not happen.
223      */

224     public static void clearArmedComponents() {
225         List JavaDoc armedComponents = (List JavaDoc) threadArmedComponents.get();
226         armedComponents.clear();
227     }
228
229     /*
230      * Die Sache muss natuerlich Thread Save sein, d.h. es duerfen nur
231      * die Events gefeuert werden, die auch aus dem feuernden Thread
232      * stammen (eben dem Dispatcher Thread). Sichergestellt wird das
233      * dadurch das beim abfeuern der Event in eine Queue (ArrayList)
234      * gestellt wird, die zu dem feuernden Event gehoert. Diese Queues
235      * der verschiedenen Threads werden in einer Map verwaltet.
236      * Beim feuern wird dann die Queue, die dem aktuellen Thread
237      * entspricht gefeuert und aus der Map entfernt.
238      */

239     /**
240      * This method fires the low level events for all "armed" components of
241      * this thread (http session) in an ordered manner:
242      * <ul><li>forms
243      * <li>buttons / clickables
244      * <li>"regular" components</ul>
245      * This order derives out of the assumption, that a user first modifies
246      * regular components before he presses the button submitting his changes.
247      * Otherwise button actions would get fired before the edit components
248      * fired their events.
249      */

250     public static void fireEvents() {
251         List JavaDoc armedComponents = (List JavaDoc) threadArmedComponents.get();
252         try {
253             LowLevelEventListener component;
254             // handle form special, form event should be fired last
255
// hopefully there is only one form ;-)
256
Iterator JavaDoc iterator = armedComponents.iterator();
257             LinkedList JavaDoc formEvents = null;
258             LinkedList JavaDoc buttonEvents = null;
259
260             while (iterator.hasNext()) {
261                 component = (LowLevelEventListener) iterator.next();
262                 /* fire form events at last
263                  * there could be more than one form event (e.g. mozilla posts a
264                  * hidden element even if it is in a form outside the posted
265                  * form (if the form is nested). Forms should not be nested in HTML.
266                  */

267                 if (component instanceof SForm) {
268                     if (formEvents == null) {
269                         formEvents = new LinkedList JavaDoc();
270                     } // end of if ()
271
formEvents.add(component);
272                 } else if (component instanceof SAbstractIconTextCompound) {
273                     if (buttonEvents == null) {
274                         buttonEvents = new LinkedList JavaDoc();
275                     }
276                     buttonEvents.add(component);
277                 } else {
278                     component.fireIntermediateEvents();
279                 }
280             }
281
282             /*
283              * no buttons in forms pressed ? Then consider the default-Button.
284              */

285             if (buttonEvents == null && formEvents != null) {
286                 Iterator JavaDoc fit = formEvents.iterator();
287                 while (fit.hasNext()) {
288                     SForm form = (SForm) fit.next();
289                     SButton defaultButton = form.getDefaultButton();
290                     if (defaultButton != null) {
291                         if (buttonEvents == null) {
292                             buttonEvents = new LinkedList JavaDoc();
293                         }
294                         buttonEvents.add(defaultButton);
295                     }
296                 }
297             }
298
299             if (buttonEvents != null) {
300                 iterator = buttonEvents.iterator();
301                 while (iterator.hasNext()) {
302                     ((SAbstractIconTextCompound) iterator.next()).fireIntermediateEvents();
303                 }
304             }
305
306             if (formEvents != null) {
307                 iterator = formEvents.iterator();
308                 while (iterator.hasNext()) {
309                     ((SForm) iterator.next()).fireIntermediateEvents();
310                 }
311             }
312
313             iterator = armedComponents.iterator();
314             while (iterator.hasNext()) {
315                 component = (LowLevelEventListener) iterator.next();
316                 // fire form events at last
317
if (!(component instanceof SForm || component instanceof SAbstractIconTextCompound)) {
318                     component.fireFinalEvents();
319                 }
320             }
321
322             if (buttonEvents != null) {
323                 iterator = buttonEvents.iterator();
324                 while (iterator.hasNext()) {
325                     ((SAbstractIconTextCompound) iterator.next()).fireFinalEvents();
326                 }
327                 buttonEvents.clear();
328             }
329
330             if (formEvents != null) {
331                 iterator = formEvents.iterator();
332                 while (iterator.hasNext()) {
333                     ((SForm) iterator.next()).fireFinalEvents();
334                 }
335                 formEvents.clear();
336             }
337         } finally {
338             armedComponents.clear();
339         }
340     }
341
342
343     /**
344      * Set, whether this form is to be transmitted via <code>POST</code> (true)
345      * or <code>GET</code> (false). The default, and this is what you
346      * usually want, is <code>POST</code>.
347      */

348     public void setPostMethod(boolean postMethod) {
349         this.postMethod = postMethod;
350     }
351
352     /**
353      * Returns, whether this form is transmitted via <code>POST</code> (true)
354      * or <code>GET</code> (false). <p>
355      * <b>Default</b> is <code>true</code>.
356      *
357      * @return <code>true</code> if form postedt via <code>POST</code>,
358      * <code>false</code> if via <code>GET</code> (false).
359      */

360     public boolean isPostMethod() {
361         return postMethod;
362     }
363
364     /**
365      * Set the encoding of this form. This actually is an HTML interna
366      * that bubbles up here. By default, the encoding type of any HTML-form
367      * is <code>application/x-www-form-urlencoded</code>, and as such, needn't
368      * be explicitly set with this setter. However, if you've included a
369      * file upload element (as represented by {@link SFileChooser}) in your
370      * form, this must be set to <code>multipart/form-data</code>, since only
371      * then, files are transmitted correctly. In 'normal' forms without
372      * file upload, it is not necessary to set it to
373      * <code>multipart/form-data</code>; actually it enlarges the data to
374      * be transmitted, so you probably don't want to do this, then.
375      *
376      * @param type the encoding type; one of <code>multipart/form-data</code>,
377      * <code>application/x-www-form-urlencoded</code> or null to detect encoding.
378      */

379     public void setEncodingType(String JavaDoc type) {
380         encType = type;
381     }
382
383     /**
384      * Get the current encoding type, as set with
385      * {@link #setEncodingType(String)}
386      *
387      * @return string containting the encoding type. This is something like
388      * <code>multipart/form-data</code>,
389      * <code>application/x-www-form-urlencoded</code> .. or 'null'
390      * by default.
391      */

392     public String JavaDoc getEncodingType() {
393         if (encType == null) {
394             return detectEncodingType(this);
395         } else {
396             return encType;
397         }
398     }
399
400     protected String JavaDoc detectEncodingType(SContainer pContainer) {
401         for (int i = 0; i < pContainer.getComponentCount(); i++) {
402             SComponent tComponent = pContainer.getComponent(i);
403
404             if (tComponent instanceof SFileChooser) {
405                 return ENC_TYPE_MULTIPART_FORM;
406             } else if (tComponent instanceof SContainer) {
407                 String JavaDoc tContainerEncoding = detectEncodingType((SContainer) tComponent);
408
409                 if (tContainerEncoding != null) {
410                     return tContainerEncoding;
411                 }
412             }
413         }
414
415         return null;
416     }
417
418
419     public void setAction(URL JavaDoc action) {
420         this.action = action;
421     }
422
423
424     public URL JavaDoc getAction() {
425         return action;
426     }
427
428
429     public RequestURL getRequestURL() {
430         RequestURL addr = super.getRequestURL();
431         if (getAction() != null) {
432             addr.addParameter(getAction().toString()); // ??
433
}
434         return addr;
435     }
436
437     public void processLowLevelEvent(String JavaDoc action, String JavaDoc[] values) {
438         processKeyEvents(values);
439
440         // we have to wait, until all changed states of our form have
441
// changed, before we anything can happen.
442
SForm.addArmedComponent(this);
443     }
444
445     public void fireIntermediateEvents() {
446     }
447
448     public void fireFinalEvents() {
449         fireKeyEvents();
450         fireActionPerformed(getActionCommand());
451     }
452
453     /** @see LowLevelEventListener#isEpochCheckEnabled() */
454     private boolean epochCheckEnabled = true;
455
456     /** @see LowLevelEventListener#isEpochCheckEnabled() */
457     public boolean isEpochCheckEnabled() {
458         return epochCheckEnabled;
459     }
460
461     /** @see LowLevelEventListener#isEpochCheckEnabled() */
462     public void setEpochCheckEnabled(boolean epochCheckEnabled) {
463         this.epochCheckEnabled = epochCheckEnabled;
464     }
465
466     public SComponent addComponent(SComponent c, Object JavaDoc constraint, int index) {
467         if (c instanceof SForm)
468             log.warn("WARNING: attempt to nest forms; won't work.");
469         return super.addComponent(c, constraint, index);
470     }
471
472     public void setCG(FormCG cg) {
473         super.setCG(cg);
474     }
475 }
476
Popular Tags