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