001 /*
002 * LAPIS lightweight structured text processing system
003 *
004 * Copyright (C) 1998-2002 Carnegie Mellon University,
005 * Copyright (C) 2003 Massachusetts Institute of Technology.
006 * All rights reserved.
007 *
008 * This library is free software; you can redistribute it
009 * and/or modify it under the terms of the GNU General
010 * Public License as published by the Free Software
011 * Foundation, version 2.
012 *
013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/
014 */
015 package lapisx.swing;
016
017 import javax.swing.*;
018 import javax.swing.text.*;
019 import javax.swing.text.html.HTML;
020
021 // NIY: get/set selection of SELECT element (with multiple lines visible
022 // or multi select). The relevant model is ListModel/ListSelectionModel.
023
024 /**
025 * This class provides access to the form fields of an HTML document
026 * displayed by a JEditorPane, so that an HTML form can be used for input
027 * in a Java program.
028 * <p>
029 * Only certain types of form fields can be accessed by the get* and set*
030 * methods in this class:
031 * <ul> <li> text fields (input type=text|password or textarea)
032 * <li> single-selection lists (select)
033 * <li> checkboxes (input type=checkbox)
034 * <li> radio buttons (input type=radio)
035 * </ul>
036 * Other form fields (such as buttons and multiple-selection lists)
037 * can be accessed by calling getFieldModel() to obtain the field's
038 * model object and accessing it directly.
039 * <p>
040 * <b>Warning:</b> form fields are not accessible until JEditorPane
041 * has finished loading the HTML page. To initialize form
042 * fields, you should use a PropertyChangeListener that listens for
043 * the "page" property to change:
044 * <pre>
045 * final JEditorPane editor = new JEditorPane (myURL);
046 * editor.addPropertyChangeListener (new PropertyChangeListener () {
047 * public void propertyChange (PropertyChangeEvent event) {
048 * if ("page".equals (event.getPropertyName ()))
049 * HTMLFormAccess access = new HTMLFormAccess (editor);
050 * access.setStringValue (name, value); // etc.
051 * }
052 * });
053 * </pre>
054 */
055 public class HTMLFormAccess {
056 JEditorPane editor;
057
058 /**
059 * Make an HTMLFormAccess object.
060 * @param editor JEditorPane containing an HTML form to access.
061 */
062 public HTMLFormAccess (JEditorPane editor) {
063 this.editor = editor;
064 }
065
066 /**
067 * Get value of a text field or single-selection list.
068 * @param name name attribute of form field to access (case-sensitive)
069 * @return value of form field (empty string if it's a list with no selection).
070 * @throws IllegalArgumentException if no field by that name is found
071 * @throws ClassCastException if the field is found but is not a text field or single-selection list
072 */
073 public String getStringValue (String name) {
074 Object model = getFieldModel (name);
075 if (model == null)
076 throw new IllegalArgumentException ("no field named '" + name + "'");
077
078 if (model instanceof Document) {
079 // for INPUT type=text and TEXTAREA
080 Document field = (Document) model;
081 try {
082 return field.getText (0, field.getLength ());
083 } catch (BadLocationException e) {
084 throw new RuntimeException (e.getMessage ());
085 }
086 } else if (model instanceof ComboBoxModel) {
087 // for SELECT (single selection, only 1 line visible)
088 ComboBoxModel items = (ComboBoxModel)model;
089 Object item = items.getSelectedItem ();
090 if (item == null)
091 return "";
092 else
093 return item.toString ();
094 } else
095 throw new ClassCastException ("field '" + name + "' is not a string model");
096 }
097
098 /**
099 * Set value of a text field or single-selection list.
100 * @param name name attribute of form field to access (case-sensitive)
101 * @param value value to store in form field
102 * @throws IllegalArgumentException if no field by that name is found,
103 * or if the field is a single-selection list and the value is not one
104 * of the choices
105 * @throws ClassCastException if the field is found but is not a text field or single-selection list
106 */
107 public void setStringValue (String name, String value) {
108 Object model = getFieldModel (name);
109 if (model == null)
110 throw new IllegalArgumentException ("no field named '" + name + "'");
111
112 // Replace old contents with value
113 if (model instanceof Document) {
114 // for INPUT type=text and TEXTAREA
115 Document field = (Document) model;
116 try {
117 field.remove (0, field.getLength ());
118 } catch (BadLocationException e) {
119 }
120
121 try {
122 field.insertString (0, value, null);
123 } catch (BadLocationException e) {
124 }
125 }
126 else if (model instanceof ComboBoxModel) {
127 // for SELECT (single selection, only 1 line visible)
128 ComboBoxModel items = (ComboBoxModel)model;
129 found: {
130 for (int i = 0, n = items.getSize (); i < n; ++i) {
131 Object item = items.getElementAt (i);
132 if (item != null && item.toString ().equals (value)) {
133 items.setSelectedItem (item);
134 break found;
135 }
136 }
137 throw new IllegalArgumentException
138 ("field '" + name + "' doesn't contain an option '"
139 + value + "'");
140 }
141 } else
142 throw new ClassCastException ("field '" + name
143 + "' is not a string");
144 }
145
146 /**
147 * Gets value of a checkbox or radio button.
148 * @param name name attribute of form field to access (case-sensitive)
149 * @return true if form field is checked, false if not
150 * @throws IllegalArgumentException if no field by that name is found
151 * @throws ClassCastException if the field is found but is not a checkbox or radio button
152 */
153 public boolean getBooleanValue (String name) {
154 Object model = getFieldModel (name);
155 if (model == null)
156 throw new IllegalArgumentException ("no field named '" + name + "'");
157
158 if (model instanceof ButtonModel)
159 // for INPUT=checkbox | radio
160 return ((ButtonModel) model).isSelected ();
161 else
162 throw new ClassCastException ("field '" + name
163 + "' is not a boolean");
164 }
165
166 /**
167 * Set value of a checkbox or radio button.
168 * @param name name attribute of form field to access (case-sensitive)
169 * @param value value to set in form field
170 * @throws IllegalArgumentException if no field by that name is found
171 * @throws ClassCastException if the field is found but is not a checkbox or radio button
172 */
173 public void setBooleanValue (String name, boolean value) {
174 Object model = getFieldModel (name);
175 if (model == null)
176 throw new IllegalArgumentException ("no field named '" + name + "'");
177
178 // Replace old contents with value
179 if (model instanceof ButtonModel)
180 ((ButtonModel) model).setSelected (value);
181 else
182 throw new ClassCastException ("field '" + name
183 + "' is not a boolean");
184 }
185
186 /**
187 * Get the model for a form field.
188 * @param name name attribute of form field to access (case-sensitive)
189 * @return object representing the form field's model. For text
190 * fields, the model is a Document. For buttons, checkboxes, and
191 * radio buttons, it is a ButtonModel. For lists, it is a
192 * ListModel and a ListSelectionModel (implements both).
193 * @throws IllegalArgumentException if no field by that name is found
194 */
195 public Object getFieldModel (String name) {
196 Document doc = editor.getDocument ();
197
198 Element e = findElement (doc.getDefaultRootElement (),
199 HTML.Attribute.NAME, name);
200 if (e == null)
201 return null;
202
203 Object model = e.getAttributes ()
204 .getAttribute(StyleConstants.ModelAttribute);
205 return model;
206 }
207
208 /**
209 * Search subtree rooted at element e for element with an attribute
210 * attr with the given value.
211 */
212 Element findElement (Element e,
213 HTML.Attribute attr,
214 String value) {
215 if (e == null)
216 return null;
217
218 String myValue = (String)e.getAttributes ().getAttribute (attr);
219 if (value.equals (myValue))
220 return e;
221
222 // Search through children
223 for (int i = 0, n = e.getElementCount (); i < n; ++i) {
224 Element found = findElement (e.getElement (i), attr, value);
225 if (found != null)
226 return found;
227 }
228
229 return null;
230 }
231
232 }