1. /*
  2. * @(#)FormView.java 1.26 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text.html;
  8. import java.net.*;
  9. import java.io.*;
  10. import java.awt.*;
  11. import java.awt.event.*;
  12. import java.util.*;
  13. import javax.swing.*;
  14. import javax.swing.event.*;
  15. import javax.swing.text.*;
  16. /**
  17. * Component decorator that implements the view interface
  18. * for form elements, <input>, <textarea>,
  19. * and <select>. The model for the component is stored
  20. * as an attribute of the the element (using StyleConstants.ModelAttribute),
  21. * and is used to build the component of the view. The type
  22. * of the model is assumed to of the type that would be set by
  23. * <code>HTMLDocument.HTMLReader.FormAction</code>. If there are
  24. * multiple views mapped over the document, they will share the
  25. * embedded component models.
  26. * <p>
  27. * The following table shows what components get built
  28. * by this view.
  29. * <table summary="shows what components get built by this view">
  30. * <tr>
  31. * <th>Element Type</th>
  32. * <th>Component built</th>
  33. * </tr>
  34. * <tr>
  35. * <td>input, type button</td>
  36. * <td>JButton</td>
  37. * </tr>
  38. * <tr>
  39. * <td>input, type checkbox</td>
  40. * <td>JCheckBox</td>
  41. * </tr>
  42. * <tr>
  43. * <td>input, type image</td>
  44. * <td>JButton</td>
  45. * </tr>
  46. * <tr>
  47. * <td>input, type password</td>
  48. * <td>JPasswordField</td>
  49. * </tr>
  50. * <tr>
  51. * <td>input, type radio</td>
  52. * <td>JRadioButton</td>
  53. * </tr>
  54. * <tr>
  55. * <td>input, type reset</td>
  56. * <td>JButton</td>
  57. * </tr>
  58. * <tr>
  59. * <td>input, type submit</td>
  60. * <td>JButton</td>
  61. * </tr>
  62. * <tr>
  63. * <td>input, type text</td>
  64. * <td>JTextField</td>
  65. * </tr>
  66. * <tr>
  67. * <td>select, size > 1 or multiple attribute defined</td>
  68. * <td>JList in a JScrollPane</td>
  69. * </tr>
  70. * <tr>
  71. * <td>select, size unspecified or 1</td>
  72. * <td>JComboBox</td>
  73. * </tr>
  74. * <tr>
  75. * <td>textarea</td>
  76. * <td>JTextArea in a JScrollPane</td>
  77. * </tr>
  78. * <tr>
  79. * <td>input, type file</td>
  80. * <td>JTextField</td>
  81. * </tr>
  82. * </table>
  83. *
  84. * @author Timothy Prinzing
  85. * @author Sunita Mani
  86. * @version 1.26 05/18/04
  87. */
  88. public class FormView extends ComponentView implements ActionListener {
  89. /**
  90. * If a value attribute is not specified for a FORM input element
  91. * of type "submit", then this default string is used.
  92. *
  93. * @deprecated As of 1.3, value now comes from UIManager property
  94. * FormView.submitButtonText
  95. */
  96. @Deprecated
  97. public static final String SUBMIT = new String("Submit Query");
  98. /**
  99. * If a value attribute is not specified for a FORM input element
  100. * of type "reset", then this default string is used.
  101. *
  102. * @deprecated As of 1.3, value comes from UIManager UIManager property
  103. * FormView.resetButtonText
  104. */
  105. @Deprecated
  106. public static final String RESET = new String("Reset");
  107. /**
  108. * Used to indicate if the maximum span should be the same as the
  109. * preferred span. This is used so that the Component's size doesn't
  110. * change if there is extra room on a line. The first bit is used for
  111. * the X direction, and the second for the y direction.
  112. */
  113. private short maxIsPreferred;
  114. /**
  115. * Creates a new FormView object.
  116. *
  117. * @param elem the element to decorate
  118. */
  119. public FormView(Element elem) {
  120. super(elem);
  121. }
  122. /**
  123. * Create the component. This is basically a
  124. * big switch statement based upon the tag type
  125. * and html attributes of the associated element.
  126. */
  127. protected Component createComponent() {
  128. AttributeSet attr = getElement().getAttributes();
  129. HTML.Tag t = (HTML.Tag)
  130. attr.getAttribute(StyleConstants.NameAttribute);
  131. JComponent c = null;
  132. Object model = attr.getAttribute(StyleConstants.ModelAttribute);
  133. if (t == HTML.Tag.INPUT) {
  134. c = createInputComponent(attr, model);
  135. } else if (t == HTML.Tag.SELECT) {
  136. if (model instanceof OptionListModel) {
  137. JList list = new JList((ListModel) model);
  138. int size = HTML.getIntegerAttributeValue(attr,
  139. HTML.Attribute.SIZE,
  140. 1);
  141. list.setVisibleRowCount(size);
  142. list.setSelectionModel((ListSelectionModel)model);
  143. c = new JScrollPane(list);
  144. } else {
  145. c = new JComboBox((ComboBoxModel) model);
  146. maxIsPreferred = 3;
  147. }
  148. } else if (t == HTML.Tag.TEXTAREA) {
  149. JTextArea area = new JTextArea((Document) model);
  150. int rows = HTML.getIntegerAttributeValue(attr,
  151. HTML.Attribute.ROWS,
  152. 1);
  153. area.setRows(rows);
  154. int cols = HTML.getIntegerAttributeValue(attr,
  155. HTML.Attribute.COLS,
  156. 20);
  157. maxIsPreferred = 3;
  158. area.setColumns(cols);
  159. c = new JScrollPane(area,
  160. JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
  161. JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
  162. }
  163. if (c != null) {
  164. c.setAlignmentY(1.0f);
  165. }
  166. return c;
  167. }
  168. /**
  169. * Creates a component for an <INPUT> element based on the
  170. * value of the "type" attribute.
  171. *
  172. * @param set of attributes associated with the <INPUT> element.
  173. * @param model the value of the StyleConstants.ModelAttribute
  174. * @return the component.
  175. */
  176. private JComponent createInputComponent(AttributeSet attr, Object model) {
  177. JComponent c = null;
  178. String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
  179. if (type.equals("submit") || type.equals("reset")) {
  180. String value = (String)
  181. attr.getAttribute(HTML.Attribute.VALUE);
  182. if (value == null) {
  183. if (type.equals("submit")) {
  184. value = UIManager.getString("FormView.submitButtonText");
  185. } else {
  186. value = UIManager.getString("FormView.resetButtonText");
  187. }
  188. }
  189. JButton button = new JButton(value);
  190. if (model != null) {
  191. button.setModel((ButtonModel)model);
  192. button.addActionListener(this);
  193. }
  194. c = button;
  195. maxIsPreferred = 3;
  196. } else if (type.equals("image")) {
  197. String srcAtt = (String) attr.getAttribute(HTML.Attribute.SRC);
  198. JButton button;
  199. try {
  200. URL base = ((HTMLDocument)getElement().getDocument()).getBase();
  201. URL srcURL = new URL(base, srcAtt);
  202. Icon icon = new ImageIcon(srcURL);
  203. button = new JButton(icon);
  204. } catch (MalformedURLException e) {
  205. button = new JButton(srcAtt);
  206. }
  207. if (model != null) {
  208. button.setModel((ButtonModel)model);
  209. button.addMouseListener(new MouseEventListener());
  210. }
  211. c = button;
  212. maxIsPreferred = 3;
  213. } else if (type.equals("checkbox")) {
  214. c = new JCheckBox();
  215. if (model != null) {
  216. boolean checked = (attr.getAttribute(HTML.Attribute.CHECKED) != null);
  217. ((JToggleButton.ToggleButtonModel) model).setSelected(checked);
  218. ((JCheckBox)c).setModel((JToggleButton.ToggleButtonModel)model);
  219. }
  220. maxIsPreferred = 3;
  221. } else if (type.equals("radio")) {
  222. c = new JRadioButton();
  223. if (model != null) {
  224. boolean checked = (attr.getAttribute(HTML.Attribute.CHECKED) != null);
  225. ((JToggleButton.ToggleButtonModel)model).setSelected(checked);
  226. ((JRadioButton)c).setModel((JToggleButton.ToggleButtonModel)model);
  227. }
  228. maxIsPreferred = 3;
  229. } else if (type.equals("text")) {
  230. int size = HTML.getIntegerAttributeValue(attr,
  231. HTML.Attribute.SIZE,
  232. -1);
  233. JTextField field;
  234. if (size > 0) {
  235. field = new JTextField();
  236. field.setColumns(size);
  237. }
  238. else {
  239. field = new JTextField();
  240. field.setColumns(20);
  241. }
  242. c = field;
  243. if (model != null) {
  244. field.setDocument((Document) model);
  245. }
  246. String value = (String)
  247. attr.getAttribute(HTML.Attribute.VALUE);
  248. if (value != null) {
  249. field.setText(value);
  250. }
  251. field.addActionListener(this);
  252. maxIsPreferred = 3;
  253. } else if (type.equals("password")) {
  254. JPasswordField field = new JPasswordField();
  255. c = field;
  256. if (model != null) {
  257. field.setDocument((Document) model);
  258. }
  259. int size = HTML.getIntegerAttributeValue(attr,
  260. HTML.Attribute.SIZE,
  261. -1);
  262. field.setColumns((size > 0) ? size : 20);
  263. String value = (String)
  264. attr.getAttribute(HTML.Attribute.VALUE);
  265. if (value != null) {
  266. field.setText(value);
  267. }
  268. field.addActionListener(this);
  269. maxIsPreferred = 3;
  270. } else if (type.equals("file")) {
  271. JTextField field = new JTextField();
  272. if (model != null) {
  273. field.setDocument((Document)model);
  274. }
  275. int size = HTML.getIntegerAttributeValue(attr, HTML.Attribute.SIZE,
  276. -1);
  277. field.setColumns((size > 0) ? size : 20);
  278. JButton browseButton = new JButton(UIManager.getString
  279. ("FormView.browseFileButtonText"));
  280. Box box = Box.createHorizontalBox();
  281. box.add(field);
  282. box.add(Box.createHorizontalStrut(5));
  283. box.add(browseButton);
  284. browseButton.addActionListener(new BrowseFileAction(
  285. attr, (Document)model));
  286. c = box;
  287. maxIsPreferred = 3;
  288. }
  289. return c;
  290. }
  291. /**
  292. * Determines the maximum span for this view along an
  293. * axis. For certain components, the maximum and preferred span are the
  294. * same. For others this will return the value
  295. * returned by Component.getMaximumSize along the
  296. * axis of interest.
  297. *
  298. * @param axis may be either View.X_AXIS or View.Y_AXIS
  299. * @return the span the view would like to be rendered into >= 0.
  300. * Typically the view is told to render into the span
  301. * that is returned, although there is no guarantee.
  302. * The parent may choose to resize or break the view.
  303. * @exception IllegalArgumentException for an invalid axis
  304. */
  305. public float getMaximumSpan(int axis) {
  306. switch (axis) {
  307. case View.X_AXIS:
  308. if ((maxIsPreferred & 1) == 1) {
  309. super.getMaximumSpan(axis);
  310. return getPreferredSpan(axis);
  311. }
  312. return super.getMaximumSpan(axis);
  313. case View.Y_AXIS:
  314. if ((maxIsPreferred & 2) == 2) {
  315. super.getMaximumSpan(axis);
  316. return getPreferredSpan(axis);
  317. }
  318. return super.getMaximumSpan(axis);
  319. default:
  320. break;
  321. }
  322. return super.getMaximumSpan(axis);
  323. }
  324. /**
  325. * Responsible for processeing the ActionEvent.
  326. * If the element associated with the FormView,
  327. * has a type of "submit", "reset", "text" or "password"
  328. * then the action is processed. In the case of a "submit"
  329. * the form is submitted. In the case of a "reset"
  330. * the form is reset to its original state.
  331. * In the case of "text" or "password", if the
  332. * element is the last one of type "text" or "password",
  333. * the form is submitted. Otherwise, focus is transferred
  334. * to the next component in the form.
  335. *
  336. * @param evt the ActionEvent.
  337. */
  338. public void actionPerformed(ActionEvent evt) {
  339. Element element = getElement();
  340. StringBuffer dataBuffer = new StringBuffer();
  341. HTMLDocument doc = (HTMLDocument)getDocument();
  342. AttributeSet attr = element.getAttributes();
  343. String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
  344. if (type.equals("submit")) {
  345. getFormData(dataBuffer);
  346. submitData(dataBuffer.toString());
  347. } else if (type.equals("reset")) {
  348. resetForm();
  349. } else if (type.equals("text") || type.equals("password")) {
  350. if (isLastTextOrPasswordField()) {
  351. getFormData(dataBuffer);
  352. submitData(dataBuffer.toString());
  353. } else {
  354. getComponent().transferFocus();
  355. }
  356. }
  357. }
  358. /**
  359. * This method is responsible for submitting the form data.
  360. * A thread is forked to undertake the submission.
  361. */
  362. protected void submitData(String data) {
  363. //System.err.println("data ->"+data+"<-");
  364. SubmitThread dataThread = new SubmitThread(getElement(), data);
  365. dataThread.start();
  366. }
  367. /**
  368. * The SubmitThread is responsible for submitting the form.
  369. * It performs a POST or GET based on the value of method
  370. * attribute associated with HTML.Tag.FORM. In addition to
  371. * submitting, it is also responsible for display the
  372. * results of the form submission.
  373. */
  374. class SubmitThread extends Thread {
  375. String data;
  376. HTMLDocument hdoc;
  377. AttributeSet formAttr;
  378. URL url;
  379. String method;
  380. String target;
  381. URL actionURL;
  382. public SubmitThread(Element elem, String data) {
  383. this.data = data;
  384. hdoc = (HTMLDocument)elem.getDocument();
  385. Element formE = getFormElement();
  386. if (formE != null) {
  387. formAttr = formE.getAttributes();
  388. }
  389. method = getMethod();
  390. try {
  391. String action = getAction();
  392. method = getMethod();
  393. target = getTarget();
  394. /* if action is null use the base url and ensure that
  395. the file name excludes any parameters that may be attached */
  396. URL baseURL = hdoc.getBase();
  397. if (action == null) {
  398. String file = baseURL.getFile();
  399. actionURL = new URL(baseURL.getProtocol(),
  400. baseURL.getHost(),
  401. baseURL.getPort(),
  402. file);
  403. } else {
  404. actionURL = new URL(baseURL, action);
  405. }
  406. } catch (MalformedURLException m) {
  407. actionURL = null;
  408. }
  409. }
  410. /**
  411. * This method is responsible for extracting the
  412. * method and action attributes associated with the
  413. * <FORM> and using those to determine how (POST or GET)
  414. * and where (URL) to submit the form. If action is
  415. * not specified, the base url of the existing document is
  416. * used. Also, if method is not specified, the default is
  417. * GET. Once form submission is done, run uses the
  418. * SwingUtilities.invokeLater() method, to load the results
  419. * of the form submission into the current JEditorPane.
  420. */
  421. public void run() {
  422. if (data.length() > 0) {
  423. try {
  424. URLConnection connection;
  425. // safe assumption since we are in an html document
  426. JEditorPane c = (JEditorPane)getContainer();
  427. HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
  428. if (kit.isAutoFormSubmission()) {
  429. if ("post".equals(method)) {
  430. url = actionURL;
  431. connection = url.openConnection();
  432. postData(connection, data);
  433. } else {
  434. /* the default, GET */
  435. url = new URL(actionURL+"?"+data);
  436. }
  437. Runnable callLoadDocument = new Runnable() {
  438. public void run() {
  439. JEditorPane c = (JEditorPane)getContainer();
  440. if (hdoc.isFrameDocument()) {
  441. c.fireHyperlinkUpdate(createFormSubmitEvent());
  442. } else {
  443. try {
  444. c.setPage(url);
  445. } catch (IOException e) {
  446. }
  447. }
  448. }
  449. };
  450. SwingUtilities.invokeLater(callLoadDocument);
  451. } else {
  452. c.fireHyperlinkUpdate(createFormSubmitEvent());
  453. }
  454. } catch (MalformedURLException m) {
  455. // REMIND how do we deal with exceptions ??
  456. } catch (IOException e) {
  457. // REMIND how do we deal with exceptions ??
  458. }
  459. }
  460. }
  461. /**
  462. * Create an event that notifies about form submission
  463. */
  464. private FormSubmitEvent createFormSubmitEvent() {
  465. FormSubmitEvent.MethodType formMethod =
  466. "post".equals(method) ? FormSubmitEvent.MethodType.POST :
  467. FormSubmitEvent.MethodType.GET;
  468. return new FormSubmitEvent(FormView.this,
  469. HyperlinkEvent.EventType.ACTIVATED,
  470. actionURL,
  471. getElement(),
  472. target,
  473. formMethod,
  474. data);
  475. }
  476. /**
  477. * Get the value of the target attribute.
  478. */
  479. private String getTarget() {
  480. if (formAttr != null) {
  481. String target = (String)formAttr.getAttribute(HTML.Attribute.TARGET);
  482. if (target != null) {
  483. return target.toLowerCase();
  484. }
  485. }
  486. return "_self";
  487. }
  488. /**
  489. * Get the value of the action attribute.
  490. */
  491. public String getAction() {
  492. if (formAttr == null) {
  493. return null;
  494. }
  495. return (String)formAttr.getAttribute(HTML.Attribute.ACTION);
  496. }
  497. /**
  498. * Get the form's method parameter.
  499. */
  500. String getMethod() {
  501. if (formAttr != null) {
  502. String method = (String)formAttr.getAttribute(HTML.Attribute.METHOD);
  503. if (method != null) {
  504. return method.toLowerCase();
  505. }
  506. }
  507. return null;
  508. }
  509. /**
  510. * This method is responsible for writing out the form submission
  511. * data when the method is POST.
  512. *
  513. * @param connection to use.
  514. * @param data to write.
  515. */
  516. public void postData(URLConnection connection, String data) {
  517. connection.setDoOutput(true);
  518. PrintWriter out = null;
  519. try {
  520. out = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));
  521. out.print(data);
  522. out.flush();
  523. } catch (IOException e) {
  524. // REMIND: should do something reasonable!
  525. } finally {
  526. if (out != null) {
  527. out.close();
  528. }
  529. }
  530. }
  531. }
  532. /**
  533. * MouseEventListener class to handle form submissions when
  534. * an input with type equal to image is clicked on.
  535. * A MouseListener is necessary since along with the image
  536. * data the coordinates associated with the mouse click
  537. * need to be submitted.
  538. */
  539. protected class MouseEventListener extends MouseAdapter {
  540. public void mouseReleased(MouseEvent evt) {
  541. String imageData = getImageData(evt.getPoint());
  542. imageSubmit(imageData);
  543. }
  544. }
  545. /**
  546. * This method is called to submit a form in response
  547. * to a click on an image -- an <INPUT> form
  548. * element of type "image".
  549. *
  550. * @param imageData the mouse click coordinates.
  551. */
  552. protected void imageSubmit(String imageData) {
  553. StringBuffer dataBuffer = new StringBuffer();
  554. Element elem = getElement();
  555. HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
  556. getFormData(dataBuffer);
  557. if (dataBuffer.length() > 0) {
  558. dataBuffer.append('&');
  559. }
  560. dataBuffer.append(imageData);
  561. submitData(dataBuffer.toString());
  562. return;
  563. }
  564. /**
  565. * Extracts the value of the name attribute
  566. * associated with the input element of type
  567. * image. If name is defined it is encoded using
  568. * the URLEncoder.encode() method and the
  569. * image data is returned in the following format:
  570. * name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
  571. * otherwise,
  572. * "x="+ x +"&y="+ y
  573. *
  574. * @param point associated with the mouse click.
  575. * @return the image data.
  576. */
  577. private String getImageData(Point point) {
  578. String mouseCoords = point.x + ":" + point.y;
  579. int sep = mouseCoords.indexOf(':');
  580. String x = mouseCoords.substring(0, sep);
  581. String y = mouseCoords.substring(++sep);
  582. String name = (String) getElement().getAttributes().getAttribute(HTML.Attribute.NAME);
  583. String data;
  584. if (name == null || name.equals("")) {
  585. data = "x="+ x +"&y="+ y;
  586. } else {
  587. name = URLEncoder.encode(name);
  588. data = name + ".x" +"="+ x +"&"+ name +".y"+"="+ y;
  589. }
  590. return data;
  591. }
  592. /**
  593. * The following methods provide functionality required to
  594. * iterate over a the elements of the form and in the case
  595. * of a form submission, extract the data from each model
  596. * that is associated with each form element, and in the
  597. * case of reset, reinitialize the each model to its
  598. * initial state.
  599. */
  600. /**
  601. * Returns the Element representing the <code>FORM</code>.
  602. */
  603. private Element getFormElement() {
  604. Element elem = getElement();
  605. while (elem != null) {
  606. if (elem.getAttributes().getAttribute
  607. (StyleConstants.NameAttribute) == HTML.Tag.FORM) {
  608. return elem;
  609. }
  610. elem = elem.getParentElement();
  611. }
  612. return null;
  613. }
  614. /**
  615. * Iterates over the
  616. * element hierarchy, extracting data from the
  617. * models associated with the relevant form elements.
  618. * "Relevant" means the form elements that are part
  619. * of the same form whose element triggered the submit
  620. * action.
  621. *
  622. * @param buffer the buffer that contains that data to submit
  623. * @param targetElement the element that triggered the
  624. * form submission
  625. */
  626. void getFormData(StringBuffer buffer) {
  627. Element formE = getFormElement();
  628. if (formE != null) {
  629. ElementIterator it = new ElementIterator(formE);
  630. Element next;
  631. while ((next = it.next()) != null) {
  632. if (isControl(next)) {
  633. String type = (String)next.getAttributes().getAttribute
  634. (HTML.Attribute.TYPE);
  635. if (type != null && type.equals("submit") &&
  636. next != getElement()) {
  637. // do nothing - this submit isnt the trigger
  638. } else if (type == null || !type.equals("image")) {
  639. // images only result in data if they triggered
  640. // the submit and they require that the mouse click
  641. // coords be appended to the data. Hence its
  642. // processing is handled by the view.
  643. loadElementDataIntoBuffer(next, buffer);
  644. }
  645. }
  646. }
  647. }
  648. }
  649. /**
  650. * Loads the data
  651. * associated with the element into the buffer.
  652. * The format in which data is appended depends
  653. * on the type of the form element. Essentially
  654. * data is loaded in name/value pairs.
  655. *
  656. */
  657. private void loadElementDataIntoBuffer(Element elem, StringBuffer buffer) {
  658. AttributeSet attr = elem.getAttributes();
  659. String name = (String)attr.getAttribute(HTML.Attribute.NAME);
  660. if (name == null) {
  661. return;
  662. }
  663. String value = null;
  664. HTML.Tag tag = (HTML.Tag)elem.getAttributes().getAttribute
  665. (StyleConstants.NameAttribute);
  666. if (tag == HTML.Tag.INPUT) {
  667. value = getInputElementData(attr);
  668. } else if (tag == HTML.Tag.TEXTAREA) {
  669. value = getTextAreaData(attr);
  670. } else if (tag == HTML.Tag.SELECT) {
  671. loadSelectData(attr, buffer);
  672. }
  673. if (name != null && value != null) {
  674. appendBuffer(buffer, name, value);
  675. }
  676. }
  677. /**
  678. * Returns the data associated with an <INPUT> form
  679. * element. The value of "type" attributes is
  680. * used to determine the type of the model associated
  681. * with the element and then the relevant data is
  682. * extracted.
  683. */
  684. private String getInputElementData(AttributeSet attr) {
  685. Object model = attr.getAttribute(StyleConstants.ModelAttribute);
  686. String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
  687. String value = null;
  688. if (type.equals("text") || type.equals("password")) {
  689. Document doc = (Document)model;
  690. try {
  691. value = doc.getText(0, doc.getLength());
  692. } catch (BadLocationException e) {
  693. value = null;
  694. }
  695. } else if (type.equals("submit") || type.equals("hidden")) {
  696. value = (String) attr.getAttribute(HTML.Attribute.VALUE);
  697. if (value == null) {
  698. value = "";
  699. }
  700. } else if (type.equals("radio") || type.equals("checkbox")) {
  701. ButtonModel m = (ButtonModel)model;
  702. if (m.isSelected()) {
  703. value = (String) attr.getAttribute(HTML.Attribute.VALUE);
  704. if (value == null) {
  705. value = "on";
  706. }
  707. }
  708. } else if (type.equals("file")) {
  709. Document doc = (Document)model;
  710. String path;
  711. try {
  712. path = doc.getText(0, doc.getLength());
  713. } catch (BadLocationException e) {
  714. path = null;
  715. }
  716. if (path != null && path.length() > 0) {
  717. value = path;
  718. /*
  719. try {
  720. Reader reader = new BufferedReader(new FileReader(path));
  721. StringBuffer buffer = new StringBuffer();
  722. char[] cBuff = new char[1024];
  723. int read;
  724. try {
  725. while ((read = reader.read(cBuff)) != -1) {
  726. buffer.append(cBuff, 0, read);
  727. }
  728. } catch (IOException ioe) {
  729. buffer = null;
  730. }
  731. try {
  732. reader.close();
  733. } catch (IOException ioe) {}
  734. if (buffer != null) {
  735. value = buffer.toString();
  736. }
  737. } catch (IOException ioe) {}
  738. */
  739. }
  740. }
  741. return value;
  742. }
  743. /**
  744. * Returns the data associated with the <TEXTAREA> form
  745. * element. This is done by getting the text stored in the
  746. * Document model.
  747. */
  748. private String getTextAreaData(AttributeSet attr) {
  749. Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
  750. try {
  751. return doc.getText(0, doc.getLength());
  752. } catch (BadLocationException e) {
  753. return null;
  754. }
  755. }
  756. /**
  757. * Loads the buffer with the data associated with the Select
  758. * form element. Basically, only items that are selected
  759. * and have their name attribute set are added to the buffer.
  760. */
  761. private void loadSelectData(AttributeSet attr, StringBuffer buffer) {
  762. String name = (String)attr.getAttribute(HTML.Attribute.NAME);
  763. if (name == null) {
  764. return;
  765. }
  766. Object m = attr.getAttribute(StyleConstants.ModelAttribute);
  767. if (m instanceof OptionListModel) {
  768. OptionListModel model = (OptionListModel)m;
  769. for (int i = 0; i < model.getSize(); i++) {
  770. if (model.isSelectedIndex(i)) {
  771. Option option = (Option) model.getElementAt(i);
  772. appendBuffer(buffer, name, option.getValue());
  773. }
  774. }
  775. } else if (m instanceof ComboBoxModel) {
  776. ComboBoxModel model = (ComboBoxModel)m;
  777. Option option = (Option)model.getSelectedItem();
  778. if (option != null) {
  779. appendBuffer(buffer, name, option.getValue());
  780. }
  781. }
  782. }
  783. /**
  784. * Appends name / value pairs into the
  785. * buffer. Both names and values are encoded using the
  786. * URLEncoder.encode() method before being added to the
  787. * buffer.
  788. */
  789. private void appendBuffer(StringBuffer buffer, String name, String value) {
  790. if (buffer.length() > 0) {
  791. buffer.append('&');
  792. }
  793. String encodedName = URLEncoder.encode(name);
  794. buffer.append(encodedName);
  795. buffer.append('=');
  796. String encodedValue = URLEncoder.encode(value);
  797. buffer.append(encodedValue);
  798. }
  799. /**
  800. * Returns true if the Element <code>elem</code> represents a control.
  801. */
  802. private boolean isControl(Element elem) {
  803. return elem.isLeaf();
  804. }
  805. /**
  806. * Iterates over the element hierarchy to determine if
  807. * the element parameter, which is assumed to be an
  808. * <INPUT> element of type password or text, is the last
  809. * one of either kind, in the form to which it belongs.
  810. */
  811. boolean isLastTextOrPasswordField() {
  812. Element parent = getFormElement();
  813. Element elem = getElement();
  814. if (parent != null) {
  815. ElementIterator it = new ElementIterator(parent);
  816. Element next;
  817. boolean found = false;
  818. while ((next = it.next()) != null) {
  819. if (next == elem) {
  820. found = true;
  821. }
  822. else if (found && isControl(next)) {
  823. AttributeSet elemAttr = next.getAttributes();
  824. if (HTMLDocument.matchNameAttribute
  825. (elemAttr, HTML.Tag.INPUT)) {
  826. String type = (String)elemAttr.getAttribute
  827. (HTML.Attribute.TYPE);
  828. if ("text".equals(type) || "password".equals(type)) {
  829. return false;
  830. }
  831. }
  832. }
  833. }
  834. }
  835. return true;
  836. }
  837. /**
  838. * Resets the form
  839. * to its initial state by reinitializing the models
  840. * associated with each form element to their initial
  841. * values.
  842. *
  843. * param elem the element that triggered the reset
  844. */
  845. void resetForm() {
  846. Element parent = getFormElement();
  847. if (parent != null) {
  848. ElementIterator it = new ElementIterator(parent);
  849. Element next;
  850. while((next = it.next()) != null) {
  851. if (isControl(next)) {
  852. AttributeSet elemAttr = next.getAttributes();
  853. Object m = elemAttr.getAttribute(StyleConstants.
  854. ModelAttribute);
  855. if (m instanceof TextAreaDocument) {
  856. TextAreaDocument doc = (TextAreaDocument)m;
  857. doc.reset();
  858. } else if (m instanceof PlainDocument) {
  859. try {
  860. PlainDocument doc = (PlainDocument)m;
  861. doc.remove(0, doc.getLength());
  862. if (HTMLDocument.matchNameAttribute
  863. (elemAttr, HTML.Tag.INPUT)) {
  864. String value = (String)elemAttr.
  865. getAttribute(HTML.Attribute.VALUE);
  866. if (value != null) {
  867. doc.insertString(0, value, null);
  868. }
  869. }
  870. } catch (BadLocationException e) {
  871. }
  872. } else if (m instanceof OptionListModel) {
  873. OptionListModel model = (OptionListModel) m;
  874. int size = model.getSize();
  875. for (int i = 0; i < size; i++) {
  876. model.removeIndexInterval(i, i);
  877. }
  878. BitSet selectionRange = model.getInitialSelection();
  879. for (int i = 0; i < selectionRange.size(); i++) {
  880. if (selectionRange.get(i)) {
  881. model.addSelectionInterval(i, i);
  882. }
  883. }
  884. } else if (m instanceof OptionComboBoxModel) {
  885. OptionComboBoxModel model = (OptionComboBoxModel) m;
  886. Option option = model.getInitialSelection();
  887. if (option != null) {
  888. model.setSelectedItem(option);
  889. }
  890. } else if (m instanceof JToggleButton.ToggleButtonModel) {
  891. boolean checked = ((String)elemAttr.getAttribute
  892. (HTML.Attribute.CHECKED) != null);
  893. JToggleButton.ToggleButtonModel model =
  894. (JToggleButton.ToggleButtonModel)m;
  895. model.setSelected(checked);
  896. }
  897. }
  898. }
  899. }
  900. }
  901. /**
  902. * BrowseFileAction is used for input type == file. When the user
  903. * clicks the button a JFileChooser is brought up allowing the user
  904. * to select a file in the file system. The resulting path to the selected
  905. * file is set in the text field (actually an instance of Document).
  906. */
  907. private class BrowseFileAction implements ActionListener {
  908. private AttributeSet attrs;
  909. private Document model;
  910. BrowseFileAction(AttributeSet attrs, Document model) {
  911. this.attrs = attrs;
  912. this.model = model;
  913. }
  914. public void actionPerformed(ActionEvent ae) {
  915. // PENDING: When mime support is added to JFileChooser use the
  916. // accept value of attrs.
  917. JFileChooser fc = new JFileChooser();
  918. fc.setMultiSelectionEnabled(false);
  919. if (fc.showOpenDialog(getContainer()) ==
  920. JFileChooser.APPROVE_OPTION) {
  921. File selected = fc.getSelectedFile();
  922. if (selected != null) {
  923. try {
  924. if (model.getLength() > 0) {
  925. model.remove(0, model.getLength());
  926. }
  927. model.insertString(0, selected.getPath(), null);
  928. } catch (BadLocationException ble) {}
  929. }
  930. }
  931. }
  932. }
  933. }