1 /* FormView.java -- A view for a variety of HTML form elements
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package javax.swing.text.html;
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;
52 import java.net.URLConnection;
53 import java.net.URLEncoder;
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;
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.
84 * Namely, this view creates the following components:
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>
102 * @author Roman Kennke (kennke@aicas.com)
104 public class FormView
105 extends ComponentView
106 implements ActionListener
109 protected class MouseEventListener
113 * Creates a new <code>MouseEventListener</code>.
115 protected MouseEventListener()
117 // Nothing to do here.
120 public void mouseReleased(MouseEvent ev)
122 String data = getImageData(ev.getPoint());
128 * Actually submits the form data.
130 private class SubmitThread
139 * Creates a new SubmitThread.
141 * @param d the submit data
143 SubmitThread(String d)
149 * Actually performs the submit.
153 if (data.length() > 0)
155 final String method = getMethod();
156 final URL actionURL = getActionURL();
157 final String target = getTarget();
159 final JEditorPane editor = (JEditorPane) getContainer();
160 final HTMLDocument doc = (HTMLDocument) editor.getDocument();
161 HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
162 if (kit.isAutoFormSubmission())
167 if (method != null && method.equals("post"))
171 conn = url.openConnection();
172 postData(conn, data);
177 url = new URL(actionURL + "?" + data);
179 Runnable loadDoc = new Runnable()
183 if (doc.isFrameDocument())
185 editor.fireHyperlinkUpdate(createSubmitEvent(method,
195 catch (IOException ex)
198 ex.printStackTrace();
203 SwingUtilities.invokeLater(loadDoc);
205 catch (MalformedURLException ex)
207 ex.printStackTrace();
209 catch (IOException ex)
211 ex.printStackTrace();
216 editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL,
223 * Determines the submit method.
225 * @return the submit method
227 private String getMethod()
229 AttributeSet formAtts = getFormAttributes();
230 String method = null;
231 if (formAtts != null)
233 method = (String) formAtts.getAttribute(HTML.Attribute.METHOD);
239 * Determines the action URL.
241 * @return the action URL
243 private URL getActionURL()
245 AttributeSet formAtts = getFormAttributes();
246 HTMLDocument doc = (HTMLDocument) getElement().getDocument();
247 URL url = doc.getBase();
248 if (formAtts != null)
251 (String) formAtts.getAttribute(HTML.Attribute.ACTION);
256 url = new URL(url, action);
258 catch (MalformedURLException ex)
268 * Fetches the target attribute.
270 * @return the target attribute or _self if none is present
272 private String getTarget()
274 AttributeSet formAtts = getFormAttributes();
275 String target = null;
276 if (formAtts != null)
278 target = (String) formAtts.getAttribute(HTML.Attribute.TARGET);
280 target = target.toLowerCase();
288 * Posts the form data over the specified connection.
290 * @param conn the connection
292 private void postData(URLConnection conn, String data)
294 conn.setDoOutput(true);
295 PrintWriter out = null;
298 out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream()));
302 catch (IOException ex)
305 ex.printStackTrace();
315 * Determines the attributes from the relevant form tag.
317 * @return the attributes from the relevant form tag, <code>null</code>
318 * when there is no form tag
320 private AttributeSet getFormAttributes()
322 AttributeSet atts = null;
323 Element form = getFormElement();
325 atts = form.getAttributes();
330 * Creates the submit event that should be fired.
332 * This is package private to avoid accessor methods.
334 * @param method the submit method
335 * @param actionURL the action URL
336 * @param target the target
338 * @return the submit event
340 FormSubmitEvent createSubmitEvent(String method, URL actionURL,
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);
353 * If the value attribute of an <code><input type="submit">>
354 * tag is not specified, then this string is used.
356 * @deprecated As of JDK1.3 the value is fetched from the UIManager property
357 * <code>FormView.submitButtonText</code>.
359 public static final String SUBMIT =
360 UIManager.getString("FormView.submitButtonText");
363 * If the value attribute of an <code><input type="reset">>
364 * tag is not specified, then this string is used.
366 * @deprecated As of JDK1.3 the value is fetched from the UIManager property
367 * <code>FormView.resetButtonText</code>.
369 public static final String RESET =
370 UIManager.getString("FormView.resetButtonText");
373 * If this is true, the maximum size is set to the preferred size.
375 private boolean maxIsPreferred;
378 * Creates a new <code>FormView</code>.
380 * @param el the element that is displayed by this view.
382 public FormView(Element el)
388 * Creates the correct AWT component for rendering the form element.
390 protected Component createComponent()
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))
399 String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
400 if (type.equals("button"))
402 String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
403 JButton b = new JButton(value);
406 b.setModel((ButtonModel) model);
407 b.addActionListener(this);
410 maxIsPreferred = true;
412 else if (type.equals("checkbox"))
414 if (model instanceof ResetableToggleButtonModel)
416 ResetableToggleButtonModel m =
417 (ResetableToggleButtonModel) model;
418 JCheckBox c = new JCheckBox();
421 maxIsPreferred = true;
424 else if (type.equals("image"))
426 String src = (String) atts.getAttribute(HTML.Attribute.SRC);
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);
435 catch (MalformedURLException ex)
437 b = new JButton(src);
441 b.setModel((ButtonModel) model);
442 b.addActionListener(this);
445 maxIsPreferred = true;
447 else if (type.equals("password"))
449 int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
451 JTextField tf = new JPasswordField();
457 tf.setDocument((Document) model);
458 tf.addActionListener(this);
460 maxIsPreferred = true;
462 else if (type.equals("radio"))
464 if (model instanceof ResetableToggleButtonModel)
466 ResetableToggleButtonModel m =
467 (ResetableToggleButtonModel) model;
468 JRadioButton c = new JRadioButton();
471 maxIsPreferred = true;
474 else if (type.equals("reset"))
476 String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
478 value = UIManager.getString("FormView.resetButtonText");
479 JButton b = new JButton(value);
482 b.setModel((ButtonModel) model);
483 b.addActionListener(this);
486 maxIsPreferred = true;
488 else if (type.equals("submit"))
490 String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
492 value = UIManager.getString("FormView.submitButtonText");
493 JButton b = new JButton(value);
496 b.setModel((ButtonModel) model);
497 b.addActionListener(this);
500 maxIsPreferred = true;
502 else if (type.equals("text"))
504 int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
506 JTextField tf = new JTextField();
512 tf.setDocument((Document) model);
513 tf.addActionListener(this);
515 maxIsPreferred = true;
518 else if (tag == HTML.Tag.TEXTAREA)
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);
530 else if (tag == HTML.Tag.SELECT)
532 if (model instanceof SelectListModel)
534 SelectListModel slModel = (SelectListModel) model;
535 JList list = new JList(slModel);
536 int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
538 list.setVisibleRowCount(size);
539 list.setSelectionModel(slModel.getSelectionModel());
540 comp = new JScrollPane(list);
542 else if (model instanceof SelectComboBoxModel)
544 SelectComboBoxModel scbModel = (SelectComboBoxModel) model;
545 comp = new JComboBox(scbModel);
547 maxIsPreferred = true;
553 * Determines the maximum span for this view on the specified axis.
555 * @param axis the axis along which to determine the span
557 * @return the maximum span for this view on the specified axis
559 * @throws IllegalArgumentException if the axis is invalid
561 public float getMaximumSpan(int axis)
565 span = getPreferredSpan(axis);
567 span = super.getMaximumSpan(axis);
572 * Processes an action from the Swing component.
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
581 * @param ev the action event
583 public void actionPerformed(ActionEvent ev)
585 Element el = getElement();
586 Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute);
587 if (tag.equals(HTML.Tag.INPUT))
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"))
596 // FIXME: Implement the remaining actions.
600 * Submits the form data. A separate thread is created to do the
603 * @param data the form data
605 protected void submitData(String data)
607 SubmitThread submitThread = new SubmitThread(data);
608 submitThread.start();
612 * Submits the form data in response to a click on a
613 * <code><input type="image"></code> element.
615 * @param imageData the mouse click coordinates
617 protected void imageSubmit(String imageData)
619 // FIXME: Implement this.
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.
628 * @param p the coordinates of the mouseclick
630 String getImageData(Point p)
632 String name = (String) getElement().getAttributes()
633 .getAttribute(HTML.Attribute.NAME);
635 if (name == null || name.equals(""))
637 data = "x=" + p.x + "&y=" + p.y;
641 data = name + ".x=" + p.x + "&" + name + ".y=" + p.y;
647 * Determines and returns the enclosing form element if there is any.
649 * This is package private to avoid accessor methods.
651 * @return the enclosing form element, or <code>null</code> if there is no
652 * enclosing form element
654 Element getFormElement()
657 Element el = getElement();
658 while (el != null && form == null)
660 AttributeSet atts = el.getAttributes();
661 if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM)
664 el = el.getParentElement();
670 * Determines the form data that is about to be submitted.
672 * @return the form data
674 private String getFormData()
676 Element form = getFormElement();
677 StringBuilder b = new StringBuilder();
680 ElementIterator i = new ElementIterator(form);
682 while ((next = i.next()) != null)
686 AttributeSet atts = next.getAttributes();
687 String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
688 if (type != null && type.equals("submit")
689 && next != getElement())
691 // Skip this. This is not the actual submit trigger.
693 else if (type == null || ! type.equals("image"))
695 getElementFormData(next, b);
704 * Fetches the form data from the specified element and appends it to
707 * @param el the element from which to fetch form data
708 * @param b the data string
710 private void getElementFormData(Element el, StringBuilder b)
712 AttributeSet atts = el.getAttributes();
713 String name = (String) atts.getAttribute(HTML.Attribute.NAME);
717 HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
718 if (tag == HTML.Tag.SELECT)
720 getSelectData(atts, b);
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)
730 addData(b, name, value);
737 * Fetches form data from select boxes.
739 * @param atts the attributes of the element
741 * @param b the form data string to append to
743 private void getSelectData(AttributeSet atts, StringBuilder b)
745 String name = (String) atts.getAttribute(HTML.Attribute.NAME);
748 Object m = atts.getAttribute(StyleConstants.ModelAttribute);
749 if (m instanceof SelectListModel)
751 SelectListModel sl = (SelectListModel) m;
752 ListSelectionModel lsm = sl.getSelectionModel();
753 for (int i = 0; i < sl.getSize(); i++)
755 if (lsm.isSelectedIndex(i))
757 Option o = (Option) sl.getElementAt(i);
758 addData(b, name, o.getValue());
762 else if (m instanceof SelectComboBoxModel)
764 SelectComboBoxModel scb = (SelectComboBoxModel) m;
765 Option o = (Option) scb.getSelectedItem();
767 addData(b, name, o.getValue());
773 * Fetches form data from a textarea.
775 * @param atts the attributes
777 * @return the form data
779 private String getTextAreaData(AttributeSet atts)
781 Document doc = (Document) atts.getAttribute(StyleConstants.ModelAttribute);
785 data = doc.getText(0, doc.getLength());
787 catch (BadLocationException ex)
795 * Fetches form data from an input tag.
797 * @param atts the attributes from which to fetch the data
799 * @return the field value
801 private String getInputFormData(AttributeSet atts)
803 String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
804 Object model = atts.getAttribute(StyleConstants.ModelAttribute);
806 if (type.equals("text") || type.equals("password"))
808 Document doc = (Document) model;
811 value = doc.getText(0, doc.getLength());
813 catch (BadLocationException ex)
819 else if (type.equals("hidden") || type.equals("submit"))
821 value = (String) atts.getAttribute(HTML.Attribute.VALUE);
825 // TODO: Implement the others. radio, checkbox and file.
830 * Actually adds the specified data to the string. It URL encodes
831 * the name and value and handles separation of the fields.
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
837 private void addData(StringBuilder b, String name, String value)
841 String encName = URLEncoder.encode(name);
844 String encValue = URLEncoder.encode(value);
849 * Resets the form data to their initial state.
851 private void resetForm()
853 Element form = getFormElement();
856 ElementIterator iter = new ElementIterator(form);
858 while ((next = iter.next()) != null)
862 AttributeSet atts = next.getAttributes();
863 Object m = atts.getAttribute(StyleConstants.ModelAttribute);
864 if (m instanceof ResetableModel)
865 ((ResetableModel) m).reset();