1. /*
  2. * @(#)FormView.java 1.14 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.text.html;
  11. import java.net.*;
  12. import java.io.*;
  13. import java.awt.*;
  14. import java.awt.event.*;
  15. import javax.swing.*;
  16. import javax.swing.event.*;
  17. import javax.swing.text.*;
  18. /**
  19. * Component decorator that implements the view interface
  20. * for form elements, <input>, <textarea>,
  21. * and <select>. The model for the component is stored
  22. * as an attribute of the the element (using StyleConstants.ModelAttribute),
  23. * and is used to build the component of the view. The type
  24. * of the model is assumed to of the type that would be set by
  25. * <code>HTMLDocument.HTMLReader.FormAction</code>. If there are
  26. * multiple views mapped over the document, they will share the
  27. * embedded component models.
  28. * <p>
  29. * The following table shows what components get built
  30. * by this view.
  31. * <table>
  32. * <tr>
  33. * <th>Element Type
  34. * <th>Component built
  35. * <tr>
  36. * <td>input, type button
  37. * <td>JButton
  38. * <tr>
  39. * <td>input, type checkbox
  40. * <td>JCheckBox
  41. * <tr>
  42. * <td>input, type image
  43. * <td>JButton
  44. * <tr>
  45. * <td>input, type password
  46. * <td>JPasswordField
  47. * <tr>
  48. * <td>input, type radio
  49. * <td>JRadioButton
  50. * <tr>
  51. * <td>input, type reset
  52. * <td>JButton
  53. * <tr>
  54. * <td>input, type submit
  55. * <td>JButton
  56. * <tr>
  57. * <td>input, type text
  58. * <td>JTextField
  59. * <tr>
  60. * <td>select, size > 1 or multiple attribute defined
  61. * <td>JList in a JScrollPane
  62. * <tr>
  63. * <td>select, size unspecified or 1
  64. * <td>JComboBox
  65. * <tr>
  66. * <td>textarea
  67. * <td>JTextArea in a JScrollPane
  68. * </table>
  69. *
  70. * @author Timothy Prinzing
  71. * @author Sunita Mani
  72. * @version 1.14 02/02/00
  73. */
  74. public class FormView extends ComponentView implements ActionListener {
  75. /**
  76. * If a value attribute is not specified for a FORM input element
  77. * of type "submit", then this default string is used.
  78. *
  79. * @deprecated As of 1.3, value now comes from UIManager property
  80. * FormView.submitButtonText
  81. */
  82. public static final String SUBMIT = new String("Submit Query");
  83. /**
  84. * If a value attribute is not specified for a FORM input element
  85. * of type "reset", then this default string is used.
  86. *
  87. * @deprecated As of 1.3, value comes from UIManager UIManager property
  88. * FormView.resetButtonText
  89. */
  90. public static final String RESET = new String("Reset");
  91. /**
  92. * Creates a new FormView object.
  93. *
  94. * @param elem the element to decorate
  95. */
  96. public FormView(Element elem) {
  97. super(elem);
  98. }
  99. /**
  100. * Create the component. This is basically a
  101. * big switch statement based upon the tag type
  102. * and html attributes of the associated element.
  103. */
  104. protected Component createComponent() {
  105. AttributeSet attr = getElement().getAttributes();
  106. HTML.Tag t = (HTML.Tag)
  107. attr.getAttribute(StyleConstants.NameAttribute);
  108. JComponent c = null;
  109. Object model = attr.getAttribute(StyleConstants.ModelAttribute);
  110. if (t == HTML.Tag.INPUT) {
  111. c = createInputComponent(attr, model);
  112. } else if (t == HTML.Tag.SELECT) {
  113. if (model instanceof OptionListModel) {
  114. JList list = new JList((ListModel) model);
  115. int size = HTML.getIntegerAttributeValue(attr,
  116. HTML.Attribute.SIZE,
  117. 1);
  118. list.setVisibleRowCount(size);
  119. list.setSelectionModel((ListSelectionModel)model);
  120. c = new JScrollPane(list);
  121. } else {
  122. c = new JComboBox((ComboBoxModel) model);
  123. }
  124. } else if (t == HTML.Tag.TEXTAREA) {
  125. JTextArea area = new JTextArea((Document) model);
  126. int rows = HTML.getIntegerAttributeValue(attr,
  127. HTML.Attribute.ROWS,
  128. 0);
  129. area.setRows(rows);
  130. int cols = HTML.getIntegerAttributeValue(attr,
  131. HTML.Attribute.COLS,
  132. 0);
  133. area.setColumns(cols);
  134. c = new JScrollPane(area,
  135. JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
  136. JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
  137. }
  138. if (c != null) {
  139. c.setAlignmentY(1.0f);
  140. }
  141. return c;
  142. }
  143. /**
  144. * Creates a component for an <INPUT> element based on the
  145. * value of the "type" attribute.
  146. *
  147. * @param set of attributes associated with the <INPUT> element.
  148. * @param model the value of the StyleConstants.ModelAttribute
  149. * @return the component.
  150. */
  151. private JComponent createInputComponent(AttributeSet attr, Object model) {
  152. JComponent c = null;
  153. String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
  154. if (type.equals("submit") || type.equals("reset")) {
  155. String value = (String)
  156. attr.getAttribute(HTML.Attribute.VALUE);
  157. if (value == null) {
  158. if (type.equals("submit")) {
  159. value = UIManager.getString("FormView.submitButtonText");
  160. } else {
  161. value = UIManager.getString("FormView.resetButtonText");
  162. }
  163. }
  164. JButton button = new JButton(value);
  165. if (model != null) {
  166. button.setModel((ButtonModel)model);
  167. button.addActionListener(this);
  168. }
  169. c = button;
  170. } else if (type.equals("image")) {
  171. String srcAtt = (String) attr.getAttribute(HTML.Attribute.SRC);
  172. JButton button;
  173. try {
  174. URL base = ((HTMLDocument)getElement().getDocument()).getBase();
  175. URL srcURL = new URL(base, srcAtt);
  176. Icon icon = new ImageIcon(srcURL);
  177. button = new JButton(icon);
  178. } catch (MalformedURLException e) {
  179. button = new JButton(srcAtt);
  180. }
  181. if (model != null) {
  182. button.setModel((ButtonModel)model);
  183. button.addMouseListener(new MouseEventListener());
  184. }
  185. c = button;
  186. } else if (type.equals("checkbox")) {
  187. c = new JCheckBox();
  188. if (model != null) {
  189. boolean checked = (attr.getAttribute(HTML.Attribute.CHECKED) != null);
  190. ((JToggleButton.ToggleButtonModel) model).setSelected(checked);
  191. ((JCheckBox)c).setModel((JToggleButton.ToggleButtonModel)model);
  192. }
  193. } else if (type.equals("radio")) {
  194. c = new JRadioButton();
  195. if (model != null) {
  196. boolean checked = (attr.getAttribute(HTML.Attribute.CHECKED) != null);
  197. ((JToggleButton.ToggleButtonModel)model).setSelected(checked);
  198. ((JRadioButton)c).setModel((JToggleButton.ToggleButtonModel)model);
  199. }
  200. } else if (type.equals("text")) {
  201. int size = HTML.getIntegerAttributeValue(attr,
  202. HTML.Attribute.SIZE,
  203. -1);
  204. JTextField field;
  205. if (size > 0) {
  206. // If the size is specified, we don't want to allow the
  207. // text field to be bigger than the preferred size.
  208. field = new JTextField() {
  209. public Dimension getMaximumSize() {
  210. return getPreferredSize();
  211. }
  212. };
  213. field.setColumns(size);
  214. }
  215. else {
  216. field = new JTextField();
  217. }
  218. c = field;
  219. if (model != null) {
  220. field.setDocument((Document) model);
  221. }
  222. String value = (String)
  223. attr.getAttribute(HTML.Attribute.VALUE);
  224. if (value != null) {
  225. field.setText(value);
  226. }
  227. field.addActionListener(this);
  228. } else if (type.equals("password")) {
  229. JPasswordField field = new JPasswordField();
  230. c = field;
  231. if (model != null) {
  232. field.setDocument((Document) model);
  233. }
  234. int size = HTML.getIntegerAttributeValue(attr,
  235. HTML.Attribute.SIZE,
  236. -1);
  237. if (size > 0) {
  238. field.setColumns(size);
  239. }
  240. String value = (String)
  241. attr.getAttribute(HTML.Attribute.VALUE);
  242. if (value != null) {
  243. field.setText(value);
  244. }
  245. field.addActionListener(this);
  246. }
  247. return c;
  248. }
  249. /**
  250. * Responsible for processeing the ActionEvent.
  251. * If the element associated with the FormView,
  252. * has a type of "submit", "reset", "text" or "password"
  253. * then the action is processed. In the case of a "submit"
  254. * the form is submitted. In the case of a "reset"
  255. * the form is reset to its original state.
  256. * In the case of "text" or "password", if the
  257. * element is the last one of type "text" or "password",
  258. * the form is submitted. Otherwise, focus is transferred
  259. * to the next component in the form.
  260. *
  261. * @param evt the ActionEvent.
  262. */
  263. public void actionPerformed(ActionEvent evt) {
  264. Element element = getElement();
  265. StringBuffer dataBuffer = new StringBuffer();
  266. HTMLDocument doc = (HTMLDocument)getDocument();
  267. AttributeSet attr = element.getAttributes();
  268. String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
  269. if (type.equals("submit")) {
  270. doc.getFormData(dataBuffer, element);
  271. submitData(dataBuffer.toString());
  272. } else if (type.equals("reset")) {
  273. doc.resetForm(element);
  274. } else if (type.equals("text") || type.equals("password")) {
  275. if (doc.isLastTextOrPasswordField(element)) {
  276. doc.getFormData(dataBuffer, element);
  277. submitData(dataBuffer.toString());
  278. } else {
  279. getComponent().transferFocus();
  280. }
  281. }
  282. }
  283. /**
  284. * This method is responsible for submitting the form data.
  285. * A thread is forked to undertake the submission.
  286. */
  287. protected void submitData(String data) {
  288. //System.err.println("data ->"+data+"<-");
  289. SubmitThread dataThread = new SubmitThread(getElement(), data);
  290. dataThread.start();
  291. }
  292. /**
  293. * The SubmitThread is responsible for submitting the form.
  294. * It performs a POST or GET based on the value of method
  295. * attribute associated with HTML.Tag.FORM. In addition to
  296. * submitting, it is also responsible for display the
  297. * results of the form submission.
  298. */
  299. class SubmitThread extends Thread {
  300. String data;
  301. HTMLDocument hdoc;
  302. HTMLDocument newDoc;
  303. AttributeSet formAttr;
  304. InputStream in;
  305. public SubmitThread(Element elem, String data) {
  306. this.data = data;
  307. hdoc = (HTMLDocument)elem.getDocument();
  308. formAttr = hdoc.getFormAttributes(elem.getAttributes());
  309. }
  310. /**
  311. * This method is responsible for extracting the
  312. * method and action attributes associated with the
  313. * <FORM> and using those to determine how (POST or GET)
  314. * and where (URL) to submit the form. If action is
  315. * not specified, the base url of the existing document is
  316. * used. Also, if method is not specified, the default is
  317. * GET. Once form submission is done, run uses the
  318. * SwingUtilities.invokeLater() method, to load the results
  319. * of the form submission into the current JEditorPane.
  320. */
  321. public void run() {
  322. if (data.length() > 0) {
  323. String method = getMethod();
  324. String action = getAction();
  325. URL url;
  326. try {
  327. URL actionURL;
  328. /* if action is null use the base url and ensure that
  329. the file name excludes any parameters that may be attached */
  330. URL baseURL = hdoc.getBase();
  331. if (action == null) {
  332. String file = baseURL.getFile();
  333. actionURL = new URL(baseURL.getProtocol(),
  334. baseURL.getHost(),
  335. baseURL.getPort(),
  336. file);
  337. } else {
  338. actionURL = new URL(baseURL, action);
  339. }
  340. URLConnection connection;
  341. if ("post".equals(method)) {
  342. url = actionURL;
  343. connection = url.openConnection();
  344. postData(connection, data);
  345. } else {
  346. /* the default, GET */
  347. url = new URL(actionURL+"?"+data);
  348. connection = url.openConnection();
  349. }
  350. in = connection.getInputStream();
  351. // safe assumption since we are in an html document
  352. JEditorPane c = (JEditorPane)getContainer();
  353. HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
  354. newDoc = (HTMLDocument)kit.createDefaultDocument();
  355. newDoc.putProperty(Document.StreamDescriptionProperty, url);
  356. Runnable callLoadDocument = new Runnable() {
  357. public void run() {
  358. loadDocument();
  359. }
  360. };
  361. SwingUtilities.invokeLater(callLoadDocument);
  362. } catch (MalformedURLException m) {
  363. // REMIND how do we deal with exceptions ??
  364. } catch (IOException e) {
  365. // REMIND how do we deal with exceptions ??
  366. }
  367. }
  368. }
  369. /**
  370. * This method is responsible for loading the
  371. * document into the FormView's container,
  372. * which is a JEditorPane.
  373. */
  374. public void loadDocument() {
  375. JEditorPane c = (JEditorPane)getContainer();
  376. try {
  377. c.read(in, newDoc);
  378. } catch (IOException e) {
  379. // REMIND failed to load document
  380. }
  381. }
  382. /**
  383. * Get the value of the action attribute.
  384. */
  385. public String getAction() {
  386. if (formAttr == null) {
  387. return null;
  388. }
  389. return (String)formAttr.getAttribute(HTML.Attribute.ACTION);
  390. }
  391. /**
  392. * Get the form's method parameter.
  393. */
  394. String getMethod() {
  395. if (formAttr != null) {
  396. String method = (String)formAttr.getAttribute(HTML.Attribute.METHOD);
  397. if (method != null) {
  398. return method.toLowerCase();
  399. }
  400. }
  401. return null;
  402. }
  403. /**
  404. * This method is responsible for writing out the form submission
  405. * data when the method is POST.
  406. *
  407. * @param connection to use.
  408. * @param data to write.
  409. */
  410. public void postData(URLConnection connection, String data) {
  411. connection.setDoOutput(true);
  412. PrintWriter out = null;
  413. try {
  414. out = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));
  415. out.print(data);
  416. out.flush();
  417. } catch (IOException e) {
  418. // REMIND: should do something reasonable!
  419. } finally {
  420. if (out != null) {
  421. out.close();
  422. }
  423. }
  424. }
  425. }
  426. /**
  427. * MouseEventListener class to handle form submissions when
  428. * an input with type equal to image is clicked on.
  429. * A MouseListener is necessary since along with the image
  430. * data the coordinates associated with the mouse click
  431. * need to be submitted.
  432. */
  433. protected class MouseEventListener extends MouseAdapter {
  434. public void mouseReleased(MouseEvent evt) {
  435. String imageData = getImageData(evt.getPoint());
  436. imageSubmit(imageData);
  437. }
  438. }
  439. /**
  440. * This method is called to submit a form in response
  441. * to a click on an image -- an <INPUT> form
  442. * element of type "image".
  443. *
  444. * @param the mouse click coordinates.
  445. */
  446. protected void imageSubmit(String imageData) {
  447. StringBuffer dataBuffer = new StringBuffer();
  448. Element elem = getElement();
  449. HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
  450. hdoc.getFormData(dataBuffer, getElement());
  451. if (dataBuffer.length() > 0) {
  452. dataBuffer.append('&');
  453. }
  454. dataBuffer.append(imageData);
  455. submitData(dataBuffer.toString());
  456. return;
  457. }
  458. /**
  459. * Extracts the value of the name attribute
  460. * associated with the input element of type
  461. * image. If name is defined it is encoded using
  462. * the URLEncoder.encode() method and the
  463. * image data is returned in the following format:
  464. * name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
  465. * otherwise,
  466. * "x="+ x +"&y="+ y
  467. *
  468. * @param point associated with the mouse click.
  469. * @return the image data.
  470. */
  471. private String getImageData(Point point) {
  472. String mouseCoords = point.x + ":" + point.y;
  473. int sep = mouseCoords.indexOf(':');
  474. String x = mouseCoords.substring(0, sep);
  475. String y = mouseCoords.substring(++sep);
  476. String name = (String) getElement().getAttributes().getAttribute(HTML.Attribute.NAME);
  477. String data;
  478. if (name == null || name.equals("")) {
  479. data = "x="+ x +"&y="+ y;
  480. } else {
  481. name = URLEncoder.encode(name);
  482. data = name + ".x" +"="+ x +"&"+ name +".y"+"="+ y;
  483. }
  484. return data;
  485. }
  486. }