Source for javax.swing.text.html.FormView

   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: }