OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / html / FormView.java
1 /* FormView.java -- A view for a variety of HTML form elements
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing.text.html;
40
41 import java.awt.Component;
42 import java.awt.Point;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.event.MouseAdapter;
46 import java.awt.event.MouseEvent;
47 import java.io.IOException;
48 import java.io.OutputStreamWriter;
49 import java.io.PrintWriter;
50 import java.net.MalformedURLException;
51 import java.net.URL;
52 import java.net.URLConnection;
53 import java.net.URLEncoder;
54
55 import javax.swing.ButtonModel;
56 import javax.swing.ImageIcon;
57 import javax.swing.JButton;
58 import javax.swing.JCheckBox;
59 import javax.swing.JComboBox;
60 import javax.swing.JEditorPane;
61 import javax.swing.JList;
62 import javax.swing.JPasswordField;
63 import javax.swing.JRadioButton;
64 import javax.swing.JScrollPane;
65 import javax.swing.JTextArea;
66 import javax.swing.JTextField;
67 import javax.swing.ListSelectionModel;
68 import javax.swing.SwingUtilities;
69 import javax.swing.UIManager;
70 import javax.swing.event.HyperlinkEvent;
71 import javax.swing.text.AttributeSet;
72 import javax.swing.text.BadLocationException;
73 import javax.swing.text.ComponentView;
74 import javax.swing.text.Document;
75 import javax.swing.text.Element;
76 import javax.swing.text.ElementIterator;
77 import javax.swing.text.StyleConstants;
78
79 /**
80  * A View that renders HTML form elements like buttons and input fields.
81  * This is implemented as a {@link ComponentView} that creates different Swing
82  * component depending on the type and setting of the different form elements.
83  *
84  * Namely, this view creates the following components:
85  * <table>
86  * <tr><th>Element type</th><th>Swing component</th></tr>
87  * <tr><td>input, button</td><td>JButton</td></tr>
88  * <tr><td>input, checkbox</td><td>JButton</td></tr>
89  * <tr><td>input, image</td><td>JButton</td></tr>
90  * <tr><td>input, password</td><td>JButton</td></tr>
91  * <tr><td>input, radio</td><td>JButton</td></tr>
92  * <tr><td>input, reset</td><td>JButton</td></tr>
93  * <tr><td>input, submit</td><td>JButton</td></tr>
94  * <tr><td>input, text</td><td>JButton</td></tr>
95  * <tr><td>select, size > 1 or with multiple attribute</td>
96  * <td>JList in JScrollPane</td></tr>
97  * <tr><td>select, size unspecified or == 1</td><td>JComboBox</td></tr>
98  * <tr><td>textarea, text</td><td>JTextArea in JScrollPane</td></tr>
99  * <tr><td>input, file</td><td>JTextField</td></tr> 
100  * </table>
101  *
102  * @author Roman Kennke (kennke@aicas.com)
103  */
104 public class FormView
105   extends ComponentView
106   implements ActionListener
107 {
108
109   protected class MouseEventListener
110     extends MouseAdapter
111   {
112     /**
113      * Creates a new <code>MouseEventListener</code>.
114      */
115     protected MouseEventListener()
116     {
117       // Nothing to do here.
118     }
119
120     public void mouseReleased(MouseEvent ev)
121     {
122       String data = getImageData(ev.getPoint());
123       imageSubmit(data);
124     }
125   }
126
127   /**
128    * Actually submits the form data.
129    */
130   private class SubmitThread
131     extends Thread
132   {
133     /**
134      * The submit data.
135      */
136     private String data;
137
138     /**
139      * Creates a new SubmitThread.
140      *
141      * @param d the submit data
142      */
143     SubmitThread(String d)
144     {
145       data = d;
146     }
147
148     /**
149      * Actually performs the submit.
150      */
151     public void run()
152     {
153       if (data.length() > 0)
154         {
155           final String method = getMethod();
156           final URL actionURL = getActionURL();
157           final String target = getTarget();
158           URLConnection conn;
159           final JEditorPane editor = (JEditorPane) getContainer();
160           final HTMLDocument doc = (HTMLDocument) editor.getDocument();
161           HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
162           if (kit.isAutoFormSubmission())
163             {
164               try
165                 {
166                   final URL url;
167                   if (method != null && method.equals("post"))
168                     {
169                       // Perform POST.
170                       url = actionURL;
171                       conn = url.openConnection();
172                       postData(conn, data);
173                     }
174                   else
175                     {
176                       // Default to GET.
177                       url = new URL(actionURL + "?" + data);
178                     }
179                   Runnable loadDoc = new Runnable()
180                   {
181                     public void run()
182                     {
183                       if (doc.isFrameDocument())
184                         {
185                           editor.fireHyperlinkUpdate(createSubmitEvent(method,
186                                                                      actionURL,
187                                                                      target));
188                         }
189                       else
190                         {
191                           try
192                           {
193                             editor.setPage(url);
194                           }
195                           catch (IOException ex)
196                           {
197                             // Oh well.
198                             ex.printStackTrace();
199                           }
200                         }
201                     }
202                   };
203                   SwingUtilities.invokeLater(loadDoc);
204                 }
205               catch (MalformedURLException ex)
206                 {
207                   ex.printStackTrace();
208                 }
209               catch (IOException ex)
210                 {
211                   ex.printStackTrace();
212                 }
213             }
214           else
215             {
216               editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL,
217                                                            target));
218             }
219         }
220     }
221
222     /**
223      * Determines the submit method.
224      *
225      * @return the submit method
226      */
227     private String getMethod()
228     {
229       AttributeSet formAtts = getFormAttributes();
230       String method = null;
231       if (formAtts != null)
232         {
233           method = (String) formAtts.getAttribute(HTML.Attribute.METHOD);
234         }
235       return method;
236     }
237
238     /**
239      * Determines the action URL.
240      *
241      * @return the action URL
242      */
243     private URL getActionURL()
244     {
245       AttributeSet formAtts = getFormAttributes();
246       HTMLDocument doc = (HTMLDocument) getElement().getDocument();
247       URL url = doc.getBase();
248       if (formAtts != null)
249         {
250           String action =
251             (String) formAtts.getAttribute(HTML.Attribute.ACTION);
252           if (action != null)
253             {
254               try
255                 {
256                   url = new URL(url, action);
257                 }
258               catch (MalformedURLException ex)
259                 {
260                   url = null;
261                 }
262             }
263         }
264       return url;
265     }
266
267     /**
268      * Fetches the target attribute.
269      *
270      * @return the target attribute or _self if none is present
271      */
272     private String getTarget()
273     {
274       AttributeSet formAtts = getFormAttributes();
275       String target = null;
276       if (formAtts != null)
277         {
278           target = (String) formAtts.getAttribute(HTML.Attribute.TARGET);
279           if (target != null)
280             target = target.toLowerCase();
281         }
282       if (target == null)
283         target = "_self";
284       return target;
285     }
286
287     /**
288      * Posts the form data over the specified connection.
289      *
290      * @param conn the connection
291      */
292     private void postData(URLConnection conn, String data)
293     {
294       conn.setDoOutput(true);
295       PrintWriter out = null;
296       try
297         {
298           out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream()));
299           out.print(data);
300           out.flush();
301         }
302       catch (IOException ex)
303         {
304           // Deal with this!
305           ex.printStackTrace();
306         }
307       finally
308         {
309           if (out != null)
310             out.close();
311         }
312     }
313
314     /**
315      * Determines the attributes from the relevant form tag.
316      *
317      * @return the attributes from the relevant form tag, <code>null</code>
318      *         when there is no form tag
319      */
320     private AttributeSet getFormAttributes()
321     {
322       AttributeSet atts = null;
323       Element form = getFormElement();
324       if (form != null)
325         atts = form.getAttributes();
326       return atts;
327     }
328
329     /**
330      * Creates the submit event that should be fired.
331      *
332      * This is package private to avoid accessor methods.
333      *
334      * @param method the submit method
335      * @param actionURL the action URL
336      * @param target the target
337      *
338      * @return the submit event
339      */
340     FormSubmitEvent createSubmitEvent(String method, URL actionURL,
341                                       String target)
342     {
343       FormSubmitEvent.MethodType m = "post".equals(method)
344                                      ? FormSubmitEvent.MethodType.POST
345                                      : FormSubmitEvent.MethodType.GET;
346       return new FormSubmitEvent(FormView.this,
347                                  HyperlinkEvent.EventType.ACTIVATED,
348                                  actionURL, getElement(), target, m, data);
349     }
350   }
351
352   /**
353    * If the value attribute of an <code>&lt;input type=&quot;submit&quot;&gt>
354    * tag is not specified, then this string is used.
355    * 
356    * @deprecated As of JDK1.3 the value is fetched from the UIManager property
357    *             <code>FormView.submitButtonText</code>.
358    */
359   public static final String SUBMIT =
360     UIManager.getString("FormView.submitButtonText");
361
362   /**
363    * If the value attribute of an <code>&lt;input type=&quot;reset&quot;&gt>
364    * tag is not specified, then this string is used.
365    * 
366    * @deprecated As of JDK1.3 the value is fetched from the UIManager property
367    *             <code>FormView.resetButtonText</code>.
368    */
369   public static final String RESET =
370     UIManager.getString("FormView.resetButtonText");
371
372   /**
373    * If this is true, the maximum size is set to the preferred size.
374    */
375   private boolean maxIsPreferred;
376
377   /**
378    * Creates a new <code>FormView</code>.
379    *
380    * @param el the element that is displayed by this view.
381    */
382   public FormView(Element el)
383   {
384     super(el);
385   }
386
387   /**
388    * Creates the correct AWT component for rendering the form element.
389    */
390   protected Component createComponent()
391   {
392     Component comp = null;
393     Element el = getElement();
394     AttributeSet atts = el.getAttributes();
395     Object tag = atts.getAttribute(StyleConstants.NameAttribute);
396     Object model = atts.getAttribute(StyleConstants.ModelAttribute);
397     if (tag.equals(HTML.Tag.INPUT))
398       {
399         String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
400         if (type.equals("button"))
401           {
402             String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
403             JButton b = new JButton(value);
404             if (model != null)
405               {
406                 b.setModel((ButtonModel) model);
407                 b.addActionListener(this);
408               }
409             comp = b;
410             maxIsPreferred = true;
411           }
412         else if (type.equals("checkbox"))
413           {
414             if (model instanceof ResetableToggleButtonModel)
415               {
416                 ResetableToggleButtonModel m =
417                   (ResetableToggleButtonModel) model;
418                 JCheckBox c = new JCheckBox();
419                 c.setModel(m);
420                 comp = c;
421                 maxIsPreferred = true;
422               }
423           }
424         else if (type.equals("image"))
425           {
426             String src = (String) atts.getAttribute(HTML.Attribute.SRC);
427             JButton b;
428             try
429               {
430                 URL base = ((HTMLDocument) el.getDocument()).getBase();
431                 URL srcURL = new URL(base, src);
432                 ImageIcon icon = new ImageIcon(srcURL);
433                 b = new JButton(icon);
434               }
435             catch (MalformedURLException ex)
436               {
437                 b = new JButton(src);
438               }
439             if (model != null)
440               {
441                 b.setModel((ButtonModel) model);
442                 b.addActionListener(this);
443               }
444             comp = b;
445             maxIsPreferred = true;
446           }
447         else if (type.equals("password"))
448           {
449             int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
450                                                      -1);
451             JTextField tf = new JPasswordField();
452             if (size > 0)
453               tf.setColumns(size);
454             else
455               tf.setColumns(20);
456             if (model != null)
457               tf.setDocument((Document) model);
458             tf.addActionListener(this);
459             comp = tf;
460             maxIsPreferred = true;
461           }
462         else if (type.equals("radio"))
463           {
464             if (model instanceof ResetableToggleButtonModel)
465               {
466                 ResetableToggleButtonModel m =
467                   (ResetableToggleButtonModel) model;
468                 JRadioButton c = new JRadioButton();
469                 c.setModel(m);
470                 comp = c;
471                 maxIsPreferred = true;
472               }
473           }
474         else if (type.equals("reset"))
475           {
476             String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
477             if (value == null)
478               value = UIManager.getString("FormView.resetButtonText");
479             JButton b = new JButton(value);
480             if (model != null)
481               {
482                 b.setModel((ButtonModel) model);
483                 b.addActionListener(this);
484               }
485             comp = b;
486             maxIsPreferred = true;
487           }
488         else if (type.equals("submit"))
489           {
490             String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
491             if (value == null)
492               value = UIManager.getString("FormView.submitButtonText");
493             JButton b = new JButton(value);
494             if (model != null)
495               {
496                 b.setModel((ButtonModel) model);
497                 b.addActionListener(this);
498               }
499             comp = b;
500             maxIsPreferred = true;
501           }
502         else if (type.equals("text"))
503           {
504             int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
505                                                      -1);
506             JTextField tf = new JTextField();
507             if (size > 0)
508               tf.setColumns(size);
509             else
510               tf.setColumns(20);
511             if (model != null)
512               tf.setDocument((Document) model);
513             tf.addActionListener(this);
514             comp = tf;
515             maxIsPreferred = true;
516           }
517       }
518     else if (tag == HTML.Tag.TEXTAREA)
519       {
520         JTextArea textArea = new JTextArea((Document) model);
521         int rows = HTML.getIntegerAttributeValue(atts, HTML.Attribute.ROWS, 1);
522         textArea.setRows(rows);
523         int cols = HTML.getIntegerAttributeValue(atts, HTML.Attribute.COLS, 20);
524         textArea.setColumns(cols);
525         maxIsPreferred = true;
526         comp = new JScrollPane(textArea,
527                                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
528                                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
529       }
530     else if (tag == HTML.Tag.SELECT)
531       {
532         if (model instanceof SelectListModel)
533           {
534             SelectListModel slModel = (SelectListModel) model;
535             JList list = new JList(slModel);
536             int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
537                                                      1);
538             list.setVisibleRowCount(size);
539             list.setSelectionModel(slModel.getSelectionModel());
540             comp = new JScrollPane(list);
541           }
542         else if (model instanceof SelectComboBoxModel)
543           {
544             SelectComboBoxModel scbModel = (SelectComboBoxModel) model;
545             comp = new JComboBox(scbModel);
546           }
547         maxIsPreferred = true;
548       }
549     return comp;
550   }
551
552   /**
553    * Determines the maximum span for this view on the specified axis.
554    *
555    * @param axis the axis along which to determine the span
556    *
557    * @return the maximum span for this view on the specified axis
558    *
559    * @throws IllegalArgumentException if the axis is invalid
560    */
561   public float getMaximumSpan(int axis)
562   {
563     float span;
564     if (maxIsPreferred)
565       span = getPreferredSpan(axis);
566     else
567       span = super.getMaximumSpan(axis);
568     return span;
569   }
570
571   /**
572    * Processes an action from the Swing component.
573    *
574    * If the action comes from a submit button, the form is submitted by calling
575    * {@link #submitData}. In the case of a reset button, the form is reset to
576    * the original state. If the action comes from a password or text field,
577    * then the input focus is transferred to the next input element in the form,
578    * unless this text/password field is the last one, in which case the form
579    * is submitted.
580    *
581    * @param ev the action event
582    */
583   public void actionPerformed(ActionEvent ev)
584   {
585     Element el = getElement();
586     Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute);
587     if (tag.equals(HTML.Tag.INPUT))
588       {
589         AttributeSet atts = el.getAttributes();
590         String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
591         if (type.equals("submit"))
592           submitData(getFormData());
593         else if (type.equals("reset"))
594           resetForm();
595       }
596     // FIXME: Implement the remaining actions.
597   }
598
599   /**
600    * Submits the form data. A separate thread is created to do the
601    * transmission.
602    *
603    * @param data the form data
604    */
605   protected void submitData(String data)
606   {
607     SubmitThread submitThread = new SubmitThread(data);
608     submitThread.start();
609   }
610
611   /**
612    * Submits the form data in response to a click on a
613    * <code>&lt;input type=&quot;image&quot;&gt;</code> element.
614    *
615    * @param imageData the mouse click coordinates
616    */
617   protected void imageSubmit(String imageData)
618   {
619     // FIXME: Implement this.
620   }
621
622   /**
623    * Determines the image data that should be submitted in response to a
624    * mouse click on a image. This is either 'x=<p.x>&y=<p.y>' if the name
625    * attribute of the element is null or '' or
626    * <name>.x=<p.x>&<name>.y=<p.y>' when the name attribute is not empty.
627    *
628    * @param p the coordinates of the mouseclick
629    */
630   String getImageData(Point p)
631   {
632     String name = (String) getElement().getAttributes()
633                                             .getAttribute(HTML.Attribute.NAME);
634     String data;
635     if (name == null || name.equals(""))
636       {
637         data = "x=" + p.x + "&y=" + p.y;
638       }
639     else
640       {
641         data = name + ".x=" + p.x + "&" + name + ".y=" + p.y; 
642       }
643     return data;
644   }
645
646   /**
647    * Determines and returns the enclosing form element if there is any.
648    *
649    * This is package private to avoid accessor methods.
650    *
651    * @return the enclosing form element, or <code>null</code> if there is no
652    *         enclosing form element
653    */
654   Element getFormElement()
655   {
656     Element form = null;
657     Element el = getElement();
658     while (el != null && form == null)
659       {
660         AttributeSet atts = el.getAttributes();
661         if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM)
662           form = el;
663         else
664           el = el.getParentElement();
665       }
666     return form;
667   }
668
669   /**
670    * Determines the form data that is about to be submitted.
671    *
672    * @return the form data
673    */
674   private String getFormData()
675   {
676     Element form = getFormElement();
677     StringBuilder b = new StringBuilder();
678     if (form != null)
679       {
680         ElementIterator i = new ElementIterator(form);
681         Element next;
682         while ((next = i.next()) != null)
683           {
684             if (next.isLeaf())
685               {
686                 AttributeSet atts = next.getAttributes();
687                 String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
688                 if (type != null && type.equals("submit")
689                     && next != getElement())
690                   {
691                     // Skip this. This is not the actual submit trigger.
692                   }
693                 else if (type == null || ! type.equals("image"))
694                   {
695                     getElementFormData(next, b);
696                   }
697               }
698           }
699       }
700     return b.toString();
701   }
702
703   /**
704    * Fetches the form data from the specified element and appends it to
705    * the data string.
706    *
707    * @param el the element from which to fetch form data
708    * @param b the data string
709    */
710   private void getElementFormData(Element el, StringBuilder b)
711   {
712     AttributeSet atts = el.getAttributes();
713     String name = (String) atts.getAttribute(HTML.Attribute.NAME);
714     if (name != null)
715       {
716         String value = null;
717         HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
718         if (tag == HTML.Tag.SELECT)
719           {
720             getSelectData(atts, b);
721           }
722         else
723           {
724             if (tag == HTML.Tag.INPUT)
725               value = getInputFormData(atts);
726             else if (tag == HTML.Tag.TEXTAREA)
727               value = getTextAreaData(atts);
728             if (name != null && value != null)
729               {
730                 addData(b, name, value);
731               }
732           }
733       }
734   }
735
736   /**
737    * Fetches form data from select boxes.
738    *
739    * @param atts the attributes of the element
740    *
741    * @param b the form data string to append to
742    */
743   private void getSelectData(AttributeSet atts, StringBuilder b)
744   {
745     String name = (String) atts.getAttribute(HTML.Attribute.NAME);
746     if (name != null)
747       {
748         Object m = atts.getAttribute(StyleConstants.ModelAttribute);
749         if (m instanceof SelectListModel)
750           {
751             SelectListModel sl = (SelectListModel) m;
752             ListSelectionModel lsm = sl.getSelectionModel();
753             for (int i = 0; i < sl.getSize(); i++)
754               {
755                 if (lsm.isSelectedIndex(i))
756                   {
757                     Option o = (Option) sl.getElementAt(i);
758                     addData(b, name, o.getValue());
759                   }
760               }
761           }
762         else if (m instanceof SelectComboBoxModel)
763           {
764             SelectComboBoxModel scb = (SelectComboBoxModel) m;
765             Option o = (Option) scb.getSelectedItem();
766             if (o != null)
767               addData(b, name, o.getValue());
768           }
769       }
770   }
771
772   /**
773    * Fetches form data from a textarea.
774    *
775    * @param atts the attributes
776    *
777    * @return the form data
778    */
779   private String getTextAreaData(AttributeSet atts)
780   {
781     Document doc = (Document) atts.getAttribute(StyleConstants.ModelAttribute);
782     String data;
783     try
784       {
785         data = doc.getText(0, doc.getLength());
786       }
787     catch (BadLocationException ex)
788       {
789         data = null;
790       }
791     return data;
792   }
793
794   /**
795    * Fetches form data from an input tag.
796    *
797    * @param atts the attributes from which to fetch the data
798    *
799    * @return the field value
800    */
801   private String getInputFormData(AttributeSet atts)
802   {
803     String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
804     Object model = atts.getAttribute(StyleConstants.ModelAttribute);
805     String value = null;
806     if (type.equals("text") || type.equals("password"))
807       {
808         Document doc = (Document) model;
809         try
810           {
811             value = doc.getText(0, doc.getLength());
812           }
813         catch (BadLocationException ex)
814           {
815             // Sigh.
816             assert false;
817           }
818       }
819     else if (type.equals("hidden") || type.equals("submit"))
820       {
821         value = (String) atts.getAttribute(HTML.Attribute.VALUE);
822         if (value == null)
823           value = "";
824       }
825     // TODO: Implement the others. radio, checkbox and file.
826     return value;
827   }
828
829   /**
830    * Actually adds the specified data to the string. It URL encodes
831    * the name and value and handles separation of the fields.
832    *
833    * @param b the string at which the form data to be added
834    * @param name the name of the field
835    * @param value the value
836    */
837   private void addData(StringBuilder b, String name, String value)
838   {
839     if (b.length() > 0)
840       b.append('&');
841     String encName = URLEncoder.encode(name);
842     b.append(encName);
843     b.append('=');
844     String encValue = URLEncoder.encode(value);
845     b.append(encValue);
846   }
847
848   /**
849    * Resets the form data to their initial state.
850    */
851   private void resetForm()
852   {
853     Element form = getFormElement();
854     if (form != null)
855       {
856         ElementIterator iter = new ElementIterator(form);
857         Element next;
858         while ((next = iter.next()) != null)
859           {
860             if (next.isLeaf())
861               {
862                 AttributeSet atts = next.getAttributes();
863                 Object m = atts.getAttribute(StyleConstants.ModelAttribute);
864                 if (m instanceof ResetableModel)
865                   ((ResetableModel) m).reset();
866               }
867           }
868       }
869   }
870 }