001    /* FormView.java -- A view for a variety of HTML form elements
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.text.html;
040    
041    import java.awt.Component;
042    import java.awt.Point;
043    import java.awt.event.ActionEvent;
044    import java.awt.event.ActionListener;
045    import java.awt.event.MouseAdapter;
046    import java.awt.event.MouseEvent;
047    import java.io.IOException;
048    import java.io.OutputStreamWriter;
049    import java.io.PrintWriter;
050    import java.net.MalformedURLException;
051    import java.net.URL;
052    import java.net.URLConnection;
053    import java.net.URLEncoder;
054    
055    import javax.swing.ButtonModel;
056    import javax.swing.ImageIcon;
057    import javax.swing.JButton;
058    import javax.swing.JCheckBox;
059    import javax.swing.JComboBox;
060    import javax.swing.JEditorPane;
061    import javax.swing.JList;
062    import javax.swing.JPasswordField;
063    import javax.swing.JRadioButton;
064    import javax.swing.JScrollPane;
065    import javax.swing.JTextArea;
066    import javax.swing.JTextField;
067    import javax.swing.ListSelectionModel;
068    import javax.swing.SwingUtilities;
069    import javax.swing.UIManager;
070    import javax.swing.event.HyperlinkEvent;
071    import javax.swing.text.AttributeSet;
072    import javax.swing.text.BadLocationException;
073    import javax.swing.text.ComponentView;
074    import javax.swing.text.Document;
075    import javax.swing.text.Element;
076    import javax.swing.text.ElementIterator;
077    import javax.swing.text.StyleConstants;
078    
079    /**
080     * A View that renders HTML form elements like buttons and input fields.
081     * This is implemented as a {@link ComponentView} that creates different Swing
082     * component depending on the type and setting of the different form elements.
083     *
084     * Namely, this view creates the following components:
085     * <table>
086     * <tr><th>Element type</th><th>Swing component</th></tr>
087     * <tr><td>input, button</td><td>JButton</td></tr>
088     * <tr><td>input, checkbox</td><td>JButton</td></tr>
089     * <tr><td>input, image</td><td>JButton</td></tr>
090     * <tr><td>input, password</td><td>JButton</td></tr>
091     * <tr><td>input, radio</td><td>JButton</td></tr>
092     * <tr><td>input, reset</td><td>JButton</td></tr>
093     * <tr><td>input, submit</td><td>JButton</td></tr>
094     * <tr><td>input, text</td><td>JButton</td></tr>
095     * <tr><td>select, size > 1 or with multiple attribute</td>
096     * <td>JList in JScrollPane</td></tr>
097     * <tr><td>select, size unspecified or == 1</td><td>JComboBox</td></tr>
098     * <tr><td>textarea, text</td><td>JTextArea in JScrollPane</td></tr>
099     * <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    }