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 }