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