1 /* HTMLEditorKit.java -- 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.text.html; 40 41 42 import java.awt.event.ActionEvent; 43 import java.awt.event.MouseAdapter; 44 import java.awt.event.MouseEvent; 45 import java.awt.event.MouseMotionListener; 46 import java.awt.Cursor; 47 import java.awt.Point; 48 49 import java.io.IOException; 50 import java.io.InputStream; 51 import java.io.InputStreamReader; 52 import java.io.Reader; 53 import java.io.Serializable; 54 import java.io.StringReader; 55 import java.io.Writer; 56 import java.net.MalformedURLException; 57 import java.net.URL; 58 59 import javax.accessibility.Accessible; 60 import javax.accessibility.AccessibleContext; 61 62 import javax.swing.Action; 63 import javax.swing.JEditorPane; 64 import javax.swing.SwingUtilities; 65 import javax.swing.event.HyperlinkEvent; 66 import javax.swing.text.AttributeSet; 67 import javax.swing.text.BadLocationException; 68 import javax.swing.text.Document; 69 import javax.swing.text.EditorKit; 70 import javax.swing.text.Element; 71 import javax.swing.text.MutableAttributeSet; 72 import javax.swing.text.StyleConstants; 73 import javax.swing.text.StyledDocument; 74 import javax.swing.text.StyledEditorKit; 75 import javax.swing.text.TextAction; 76 import javax.swing.text.View; 77 import javax.swing.text.ViewFactory; 78 import javax.swing.text.html.parser.ParserDelegator; 79 80 /* Move these imports here after javax.swing.text.html to make it compile 81 with jikes. */ 82 import gnu.javax.swing.text.html.parser.GnuParserDelegator; 83 import gnu.javax.swing.text.html.parser.HTML_401F; 84 85 /** 86 * @author Lillian Angel (langel at redhat dot com) 87 */ 88 public class HTMLEditorKit 89 extends StyledEditorKit 90 implements Serializable, Cloneable, Accessible 91 { 92 93 /** 94 * Fires the hyperlink events on the associated component 95 * when needed. 96 */ 97 public static class LinkController 98 extends MouseAdapter 99 implements MouseMotionListener, Serializable 100 { 101 102 /** 103 * The element of the last anchor tag. 104 */ 105 private Element lastAnchorElement; 106 107 /** 108 * Constructor 109 */ LinkController()110 public LinkController() 111 { 112 super(); 113 } 114 115 /** 116 * Dispatched when the mouse is clicked. If the component 117 * is read-only, then the clicked event is used to drive an 118 * attempt to follow the reference specified by a link 119 * 120 * @param e - the mouse event 121 */ mouseClicked(MouseEvent e)122 public void mouseClicked(MouseEvent e) 123 { 124 JEditorPane editor = (JEditorPane) e.getSource(); 125 if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e)) 126 { 127 Point loc = e.getPoint(); 128 int pos = editor.viewToModel(loc); 129 if (pos >= 0) 130 activateLink(pos, editor, e.getX(), e.getY()); 131 } 132 } 133 134 /** 135 * Dispatched when the mouse is dragged on a component. 136 * 137 * @param e - the mouse event. 138 */ mouseDragged(MouseEvent e)139 public void mouseDragged(MouseEvent e) 140 { 141 // Nothing to do here. 142 } 143 144 /** 145 * Dispatched when the mouse cursor has moved into the component. 146 * 147 * @param e - the mouse event. 148 */ mouseMoved(MouseEvent e)149 public void mouseMoved(MouseEvent e) 150 { 151 JEditorPane editor = (JEditorPane) e.getSource(); 152 HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit(); 153 if (! editor.isEditable()) 154 { 155 Document doc = editor.getDocument(); 156 if (doc instanceof HTMLDocument) 157 { 158 Cursor newCursor = kit.getDefaultCursor(); 159 HTMLDocument htmlDoc = (HTMLDocument) doc; 160 Point loc = e.getPoint(); 161 int pos = editor.viewToModel(loc); 162 Element el = htmlDoc.getCharacterElement(pos); 163 if (pos < el.getStartOffset() || pos >= el.getEndOffset()) 164 el = null; 165 if (el != null) 166 { 167 AttributeSet aAtts = (AttributeSet) 168 el.getAttributes().getAttribute(HTML.Tag.A); 169 if (aAtts != null) 170 { 171 if (el != lastAnchorElement) 172 { 173 if (lastAnchorElement != null) 174 htmlDoc.updateSpecialClass(lastAnchorElement, 175 HTML.Attribute.DYNAMIC_CLASS, 176 null); 177 lastAnchorElement = el; 178 htmlDoc.updateSpecialClass(el, 179 HTML.Attribute.DYNAMIC_CLASS, 180 "hover"); 181 } 182 newCursor = kit.getLinkCursor(); 183 } 184 else 185 { 186 if (lastAnchorElement != null) 187 htmlDoc.updateSpecialClass(lastAnchorElement, 188 HTML.Attribute.DYNAMIC_CLASS, 189 null); 190 lastAnchorElement = null; 191 } 192 } 193 else 194 { 195 if (lastAnchorElement != null) 196 htmlDoc.updateSpecialClass(lastAnchorElement, 197 HTML.Attribute.DYNAMIC_CLASS, 198 null); 199 lastAnchorElement = null; 200 } 201 if (editor.getCursor() != newCursor) 202 { 203 editor.setCursor(newCursor); 204 } 205 } 206 } 207 } 208 209 /** 210 * If the given position represents a link, then linkActivated is called 211 * on the JEditorPane. 212 * 213 * @param pos the position 214 * @param editor the editor pane 215 */ activateLink(int pos, JEditorPane editor)216 protected void activateLink(int pos, JEditorPane editor) 217 { 218 activateLink(pos, editor); 219 } 220 activateLink(int pos, JEditorPane editor, int x, int y)221 private void activateLink(int pos, JEditorPane editor, int x, int y) 222 { 223 // TODO: This is here for future extension for mapped links support. 224 // For the time beeing we implement simple hyperlinks. 225 Document doc = editor.getDocument(); 226 if (doc instanceof HTMLDocument) 227 { 228 HTMLDocument htmlDoc = (HTMLDocument) doc; 229 Element el = htmlDoc.getCharacterElement(pos); 230 AttributeSet atts = el.getAttributes(); 231 AttributeSet anchorAtts = 232 (AttributeSet) atts.getAttribute(HTML.Tag.A); 233 String href = null; 234 if (anchorAtts != null) 235 { 236 href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF); 237 htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS, 238 "visited"); 239 } 240 else 241 { 242 // TODO: Implement link maps here. 243 } 244 HyperlinkEvent event = null; 245 if (href != null) 246 event = createHyperlinkEvent(editor, htmlDoc, href, 247 anchorAtts, el); 248 if (event != null) 249 editor.fireHyperlinkUpdate(event); 250 } 251 252 } 253 254 /** 255 * Creates a HyperlinkEvent for the specified href and anchor if 256 * possible. If for some reason this won't work, return null. 257 * 258 * @param editor the editor 259 * @param doc the document 260 * @param href the href link 261 * @param anchor the anchor 262 * @param el the element 263 * 264 * @return the hyperlink event, or <code>null</code> if we couldn't 265 * create one 266 */ createHyperlinkEvent(JEditorPane editor, HTMLDocument doc, String href, AttributeSet anchor, Element el)267 private HyperlinkEvent createHyperlinkEvent(JEditorPane editor, 268 HTMLDocument doc, 269 String href, 270 AttributeSet anchor, 271 Element el) 272 { 273 URL url; 274 try 275 { 276 URL base = doc.getBase(); 277 url = new URL(base, href); 278 279 } 280 catch (MalformedURLException ex) 281 { 282 url = null; 283 } 284 HyperlinkEvent ev; 285 if (doc.isFrameDocument()) 286 { 287 String target = null; 288 if (anchor != null) 289 target = (String) anchor.getAttribute(HTML.Attribute.TARGET); 290 if (target == null || target.equals("")) 291 target = doc.getBaseTarget(); 292 if (target == null || target.equals("")) 293 target = "_self"; 294 ev = new HTMLFrameHyperlinkEvent(editor, 295 HyperlinkEvent.EventType.ACTIVATED, 296 url, href, el, target); 297 } 298 else 299 { 300 ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, 301 url, href, el); 302 } 303 return ev; 304 } 305 } 306 307 /** 308 * This class is used to insert a string of HTML into an existing 309 * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag) 310 * identifies the parent in the document to add the elements to. The second, (addTag), 311 * identifies that the first tag should be added to the document as seen in the string. 312 * The parser will generate all appropriate (opening/closing tags_ even if they are not 313 * in the HTML string passed in. 314 */ 315 public static class InsertHTMLTextAction 316 extends HTMLTextAction 317 { 318 319 /** 320 * Tag in HTML to start adding tags from. 321 */ 322 protected HTML.Tag addTag; 323 324 /** 325 * Alternate tag in HTML to start adding tags from if parentTag is 326 * not found and alternateParentTag is not found. 327 */ 328 protected HTML.Tag alternateAddTag; 329 330 /** 331 * Alternate tag to check if parentTag is not found. 332 */ 333 protected HTML.Tag alternateParentTag; 334 335 /** 336 * HTML to insert. 337 */ 338 protected String html; 339 340 /** 341 * Tag to check for in the document. 342 */ 343 protected HTML.Tag parentTag; 344 345 /** 346 * Initializes all fields. 347 * 348 * @param name - the name of the document. 349 * @param html - the html to insert 350 * @param parentTag - the parent tag to check for 351 * @param addTag - the tag to start adding from 352 */ InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, HTML.Tag addTag)353 public InsertHTMLTextAction(String name, String html, 354 HTML.Tag parentTag, HTML.Tag addTag) 355 { 356 this(name, html, parentTag, addTag, null, null); 357 } 358 359 /** 360 * Initializes all fields and calls super 361 * 362 * @param name - the name of the document. 363 * @param html - the html to insert 364 * @param parentTag - the parent tag to check for 365 * @param addTag - the tag to start adding from 366 * @param alternateParentTag - the alternate parent tag 367 * @param alternateAddTag - the alternate add tag 368 */ InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, HTML.Tag addTag, HTML.Tag alternateParentTag, HTML.Tag alternateAddTag)369 public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, 370 HTML.Tag addTag, HTML.Tag alternateParentTag, 371 HTML.Tag alternateAddTag) 372 { 373 super(name); 374 // Fields are for easy access when the action is applied to an actual 375 // document. 376 this.html = html; 377 this.parentTag = parentTag; 378 this.addTag = addTag; 379 this.alternateParentTag = alternateParentTag; 380 this.alternateAddTag = alternateAddTag; 381 } 382 383 /** 384 * HTMLEditorKit.insertHTML is called. If an exception is 385 * thrown, it is wrapped in a RuntimeException and thrown. 386 * 387 * @param editor - the editor to use to get the editorkit 388 * @param doc - 389 * the Document to insert the HTML into. 390 * @param offset - 391 * where to begin inserting the HTML. 392 * @param html - 393 * the String to insert 394 * @param popDepth - 395 * the number of ElementSpec.EndTagTypes to generate before 396 * inserting 397 * @param pushDepth - 398 * the number of ElementSpec.StartTagTypes with a direction of 399 * ElementSpec.JoinNextDirection that should be generated before 400 * @param addTag - 401 * the first tag to start inserting into document 402 */ insertHTML(JEditorPane editor, HTMLDocument doc, int offset, String html, int popDepth, int pushDepth, HTML.Tag addTag)403 protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset, 404 String html, int popDepth, int pushDepth, 405 HTML.Tag addTag) 406 { 407 try 408 { 409 super.getHTMLEditorKit(editor).insertHTML(doc, offset, html, 410 popDepth, pushDepth, addTag); 411 } 412 catch (IOException e) 413 { 414 throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e); 415 } 416 catch (BadLocationException ex) 417 { 418 throw (RuntimeException) new RuntimeException("BadLocationException: " 419 + offset).initCause(ex); 420 } 421 } 422 423 /** 424 * Invoked when inserting at a boundary. Determines the number of pops, 425 * and then the number of pushes that need to be performed. The it calls 426 * insertHTML. 427 * 428 * @param editor - 429 * the editor to use to get the editorkit 430 * @param doc - 431 * the Document to insert the HTML into. 432 * @param offset - 433 * where to begin inserting the HTML. 434 * @param insertElement - 435 * the element to insert 436 * @param html - 437 * the html to insert 438 * @param parentTag - 439 * the parent tag 440 * @param addTag - 441 * the first tag 442 */ insertAtBoundary(JEditorPane editor, HTMLDocument doc, int offset, Element insertElement, String html, HTML.Tag parentTag, HTML.Tag addTag)443 protected void insertAtBoundary(JEditorPane editor, 444 HTMLDocument doc, int offset, 445 Element insertElement, 446 String html, HTML.Tag parentTag, 447 HTML.Tag addTag) 448 { 449 insertAtBoundry(editor, doc, offset, insertElement, 450 html, parentTag, addTag); 451 } 452 453 /** 454 * Invoked when inserting at a boundary. Determines the number of pops, 455 * and then the number of pushes that need to be performed. The it calls 456 * insertHTML. 457 * 458 * @param editor - the editor to use to get the editorkit 459 * @param doc - 460 * the Document to insert the HTML into. 461 * @param offset - 462 * where to begin inserting the HTML. 463 * @param insertElement - the element to insert 464 * @param html - the html to insert 465 * @param parentTag - the parent tag 466 * @param addTag - the first tag 467 * 468 * @deprecated as of v1.3, use insertAtBoundary 469 */ insertAtBoundry(JEditorPane editor, HTMLDocument doc, int offset, Element insertElement, String html, HTML.Tag parentTag, HTML.Tag addTag)470 protected void insertAtBoundry(JEditorPane editor, 471 HTMLDocument doc, 472 int offset, Element insertElement, 473 String html, HTML.Tag parentTag, 474 HTML.Tag addTag) 475 { 476 Element parent = insertElement; 477 Element el; 478 // Find common parent element. 479 if (offset > 0 || insertElement == null) 480 { 481 el = doc.getDefaultRootElement(); 482 while (el != null && el.getStartOffset() != offset 483 && ! el.isLeaf()) 484 el = el.getElement(el.getElementIndex(offset)); 485 parent = el != null ? el.getParentElement() : null; 486 } 487 if (parent != null) 488 { 489 int pops = 0; 490 int pushes = 0; 491 if (offset == 0 && insertElement != null) 492 { 493 el = parent; 494 while (el != null && ! el.isLeaf()) 495 { 496 el = el.getElement(el.getElementIndex(offset)); 497 pops++; 498 } 499 } 500 else 501 { 502 el = parent; 503 offset--; 504 while (el != null && ! el.isLeaf()) 505 { 506 el = el.getElement(el.getElementIndex(offset)); 507 pops++; 508 } 509 el = parent; 510 offset++; 511 while (el != null && el != insertElement) 512 { 513 el = el.getElement(el.getElementIndex(offset)); 514 pushes++; 515 } 516 } 517 pops = Math.max(0, pops - 1); 518 insertHTML(editor, doc, offset, html, pops, pushes, addTag); 519 } 520 } 521 522 /** 523 * Inserts the HTML. 524 * 525 * @param ae - the action performed 526 */ actionPerformed(ActionEvent ae)527 public void actionPerformed(ActionEvent ae) 528 { 529 JEditorPane source = getEditor(ae); 530 if (source != null) 531 { 532 HTMLDocument d = getHTMLDocument(source); 533 int offset = source.getSelectionStart(); 534 int length = d.getLength(); 535 boolean inserted = true; 536 if (! tryInsert(source, d, offset, parentTag, addTag)) 537 { 538 inserted = tryInsert(source, d, offset, alternateParentTag, 539 alternateAddTag); 540 } 541 if (inserted) 542 adjustSelection(source, d, offset, length); 543 } 544 } 545 546 /** 547 * Tries to insert the html chunk to the specified <code>addTag</code>. 548 * 549 * @param pane the editor 550 * @param doc the document 551 * @param offset the offset at which to insert 552 * @param tag the tag at which to insert 553 * @param addTag the add tag 554 * 555 * @return <code>true</code> when the html has been inserted successfully, 556 * <code>false</code> otherwise 557 */ tryInsert(JEditorPane pane, HTMLDocument doc, int offset, HTML.Tag tag, HTML.Tag addTag)558 private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset, 559 HTML.Tag tag, HTML.Tag addTag) 560 { 561 boolean inserted = false; 562 Element el = findElementMatchingTag(doc, offset, tag); 563 if (el != null && el.getStartOffset() == offset) 564 { 565 insertAtBoundary(pane, doc, offset, el, html, tag, addTag); 566 inserted = true; 567 } 568 else if (offset > 0) 569 { 570 int depth = elementCountToTag(doc, offset - 1, tag); 571 if (depth != -1) 572 { 573 insertHTML(pane, doc, offset, html, depth, 0, addTag); 574 inserted = true; 575 } 576 } 577 return inserted; 578 } 579 580 /** 581 * Adjusts the selection after an insertion has been performed. 582 * 583 * @param pane the editor pane 584 * @param doc the document 585 * @param offset the insert offset 586 * @param oldLen the old document length 587 */ adjustSelection(JEditorPane pane, HTMLDocument doc, int offset, int oldLen)588 private void adjustSelection(JEditorPane pane, HTMLDocument doc, 589 int offset, int oldLen) 590 { 591 int newLen = doc.getLength(); 592 if (newLen != oldLen && offset < newLen) 593 { 594 if (offset > 0) 595 { 596 String text; 597 try 598 { 599 text = doc.getText(offset - 1, 1); 600 } 601 catch (BadLocationException ex) 602 { 603 text = null; 604 } 605 if (text != null && text.length() > 0 606 && text.charAt(0) == '\n') 607 { 608 pane.select(offset, offset); 609 } 610 else 611 { 612 pane.select(offset + 1, offset + 1); 613 } 614 } 615 else 616 { 617 pane.select(1, 1); 618 } 619 } 620 } 621 } 622 623 /** 624 * Abstract Action class that helps inserting HTML into an existing document. 625 */ 626 public abstract static class HTMLTextAction 627 extends StyledEditorKit.StyledTextAction 628 { 629 630 /** 631 * Constructor 632 */ HTMLTextAction(String name)633 public HTMLTextAction(String name) 634 { 635 super(name); 636 } 637 638 /** 639 * Gets the HTMLDocument from the JEditorPane. 640 * 641 * @param e - the editor pane 642 * @return the html document. 643 */ getHTMLDocument(JEditorPane e)644 protected HTMLDocument getHTMLDocument(JEditorPane e) 645 { 646 Document d = e.getDocument(); 647 if (d instanceof HTMLDocument) 648 return (HTMLDocument) d; 649 throw new IllegalArgumentException("Document is not a HTMLDocument."); 650 } 651 652 /** 653 * Gets the HTMLEditorKit 654 * 655 * @param e - the JEditorPane to get the HTMLEditorKit from. 656 * @return the HTMLEditorKit 657 */ getHTMLEditorKit(JEditorPane e)658 protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) 659 { 660 EditorKit d = e.getEditorKit(); 661 if (d instanceof HTMLEditorKit) 662 return (HTMLEditorKit) d; 663 throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit."); 664 } 665 666 /** 667 * Returns an array of Elements that contain the offset. 668 * The first elements corresponds to the roots of the doc. 669 * 670 * @param doc - the document to get the Elements from. 671 * @param offset - the offset the Elements must contain 672 * @return an array of all the elements containing the offset. 673 */ getElementsAt(HTMLDocument doc, int offset)674 protected Element[] getElementsAt(HTMLDocument doc, 675 int offset) 676 { 677 return getElementsAt(doc.getDefaultRootElement(), offset, 0); 678 } 679 680 /** 681 * Helper function to get all elements using recursion. 682 */ getElementsAt(Element root, int offset, int depth)683 private Element[] getElementsAt(Element root, int offset, int depth) 684 { 685 Element[] elements = null; 686 if (root != null) 687 { 688 if (root.isLeaf()) 689 { 690 elements = new Element[depth + 1]; 691 elements[depth] = root; 692 return elements; 693 } 694 elements = getElementsAt(root.getElement(root.getElementIndex(offset)), 695 offset, depth + 1); 696 elements[depth] = root; 697 } 698 return elements; 699 } 700 701 /** 702 * Returns the number of elements, starting at the deepest point, needed 703 * to get an element representing tag. -1 if no elements are found, 0 if 704 * the parent of the leaf at offset represents the tag. 705 * 706 * @param doc - 707 * the document to search 708 * @param offset - 709 * the offset to check 710 * @param tag - 711 * the tag to look for 712 * @return - the number of elements needed to get an element representing 713 * tag. 714 */ elementCountToTag(HTMLDocument doc, int offset, HTML.Tag tag)715 protected int elementCountToTag(HTMLDocument doc, 716 int offset, HTML.Tag tag) 717 { 718 Element root = doc.getDefaultRootElement(); 719 int num = -1; 720 Element next = root.getElement(root.getElementIndex(offset)); 721 722 while (!next.isLeaf()) 723 { 724 num++; 725 if (next.getAttributes(). 726 getAttribute(StyleConstants.NameAttribute).equals(tag)) 727 return num; 728 next = next.getElement(next.getElementIndex(offset)); 729 } 730 return num; 731 } 732 733 /** 734 * Gets the deepest element at offset with the 735 * matching tag. 736 * 737 * @param doc - the document to search 738 * @param offset - the offset to check for 739 * @param tag - the tag to match 740 * @return - the element that is found, null if not found. 741 */ findElementMatchingTag(HTMLDocument doc, int offset, HTML.Tag tag)742 protected Element findElementMatchingTag(HTMLDocument doc, 743 int offset, HTML.Tag tag) 744 { 745 Element element = doc.getDefaultRootElement(); 746 Element tagElement = null; 747 748 while (element != null) 749 { 750 Object otag = element.getAttributes().getAttribute( 751 StyleConstants.NameAttribute); 752 if (otag instanceof HTML.Tag && otag.equals(tag)) 753 tagElement = element; 754 element = element.getElement(element.getElementIndex(offset)); 755 } 756 757 return tagElement; 758 } 759 } 760 761 /** 762 * A {@link ViewFactory} that is able to create {@link View}s for 763 * the <code>Element</code>s that are supported. 764 */ 765 public static class HTMLFactory 766 implements ViewFactory 767 { 768 769 /** 770 * Constructor 771 */ HTMLFactory()772 public HTMLFactory() 773 { 774 // Do Nothing here. 775 } 776 777 /** 778 * Creates a {@link View} for the specified <code>Element</code>. 779 * 780 * @param element the <code>Element</code> to create a <code>View</code> 781 * for 782 * @return the <code>View</code> for the specified <code>Element</code> 783 * or <code>null</code> if the type of <code>element</code> is 784 * not supported 785 */ create(Element element)786 public View create(Element element) 787 { 788 View view = null; 789 Object attr = 790 element.getAttributes().getAttribute(StyleConstants.NameAttribute); 791 if (attr instanceof HTML.Tag) 792 { 793 HTML.Tag tag = (HTML.Tag) attr; 794 795 if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P 796 || tag == HTML.Tag.H1 || tag == HTML.Tag.H2 797 || tag == HTML.Tag.H3 || tag == HTML.Tag.H4 798 || tag == HTML.Tag.H5 || tag == HTML.Tag.H6 799 || tag == HTML.Tag.DT) 800 view = new ParagraphView(element); 801 else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL 802 || tag == HTML.Tag.DD || tag == HTML.Tag.BODY 803 || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER 804 || tag == HTML.Tag.DIV 805 || tag == HTML.Tag.BLOCKQUOTE 806 || tag == HTML.Tag.PRE 807 || tag == HTML.Tag.FORM 808 // Misplaced TD and TH tags get mapped as vertical block. 809 // Note that correctly placed tags get mapped in TableView. 810 || tag == HTML.Tag.TD || tag == HTML.Tag.TH) 811 view = new BlockView(element, View.Y_AXIS); 812 else if (tag == HTML.Tag.TR) 813 // Misplaced TR tags get mapped as horizontal blocks. 814 // Note that correctly placed tags get mapped in TableView. 815 view = new BlockView(element, View.X_AXIS); 816 else if (tag == HTML.Tag.IMG) 817 view = new ImageView(element); 818 819 else if (tag == HTML.Tag.CONTENT) 820 view = new InlineView(element); 821 else if (tag == HTML.Tag.HEAD) 822 view = new NullView(element); 823 else if (tag == HTML.Tag.TABLE) 824 view = new javax.swing.text.html.TableView(element); 825 else if (tag == HTML.Tag.HR) 826 view = new HRuleView(element); 827 else if (tag == HTML.Tag.BR) 828 view = new BRView(element); 829 else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT 830 || tag == HTML.Tag.TEXTAREA) 831 view = new FormView(element); 832 833 else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR 834 || tag == HTML.Tag.UL || tag == HTML.Tag.OL) 835 view = new ListView(element); 836 else if (tag == HTML.Tag.FRAMESET) 837 view = new FrameSetView(element); 838 else if (tag == HTML.Tag.FRAME) 839 view = new FrameView(element); 840 else if (tag == HTML.Tag.OBJECT) 841 view = new ObjectView(element); 842 } 843 if (view == null) 844 { 845 view = new NullView(element); 846 } 847 return view; 848 } 849 } 850 851 /** 852 * The abstract HTML parser declaration. 853 */ 854 public abstract static class Parser 855 { 856 /** 857 * Parse the HTML text, calling various methods of the provided callback 858 * in response to the occurence of the corresponding HTML constructions. 859 * @param reader The reader to read the source HTML from. 860 * @param callback The callback to receive information about the parsed 861 * HTML structures 862 * @param ignoreCharSet If true, the parser ignores all charset information 863 * that may be present in HTML documents. 864 * @throws IOException, normally if the reader throws one. 865 */ parse(Reader reader, ParserCallback callback, boolean ignoreCharSet)866 public abstract void parse(Reader reader, ParserCallback callback, 867 boolean ignoreCharSet) throws IOException; 868 } 869 870 /** 871 * The "hook" that receives all information about the HTML document 872 * structure while parsing it. The methods are invoked by parser 873 * and should be normally overridden. 874 */ 875 public static class ParserCallback 876 { 877 /** 878 * If the tag does not occurs in the html stream directly, but 879 * is supposed by parser, the tag attribute set contains this additional 880 * attribute, having value Boolean.True. 881 */ 882 public static final Object IMPLIED = "_implied_"; 883 884 /** 885 * Constructor 886 */ ParserCallback()887 public ParserCallback() 888 { 889 // Nothing to do here. 890 } 891 892 /** 893 * The parser calls this method after it finishes parsing the document. 894 */ flush()895 public void flush() throws BadLocationException 896 { 897 // Nothing to do here. 898 } 899 900 /** 901 * Handle HTML comment, present in the given position. 902 * @param comment the comment 903 * @position the position of the comment in the text being parsed. 904 */ handleComment(char[] comment, int position)905 public void handleComment(char[] comment, int position) 906 { 907 // Nothing to do here. 908 } 909 910 /** 911 * Notifies about the character sequences, used to separate lines in 912 * this document. The parser calls this method after it finishes 913 * parsing the document, but before flush(). 914 * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n. 915 */ handleEndOfLineString(String end_of_line)916 public void handleEndOfLineString(String end_of_line) 917 { 918 // Nothing to do here. 919 } 920 921 /** 922 * The method is called when the HTML closing tag ((like </table>) 923 * is found or if the parser concludes that the one should be present 924 * in the current position. 925 * @param tag The tag being handled 926 * @param position the tag position in the text being parsed. 927 */ handleEndTag(HTML.Tag tag, int position)928 public void handleEndTag(HTML.Tag tag, int position) 929 { 930 // Nothing to do here. 931 } 932 933 /** 934 * Handle the error. 935 * @param message The message, explaining the error. 936 * @param position The starting position of the fragment that has caused 937 * the error in the html document being parsed. 938 */ handleError(String message, int position)939 public void handleError(String message, int position) 940 { 941 // Nothing to do here. 942 } 943 944 /** 945 * Handle the tag with no content, like <br>. The method is 946 * called for the elements that, in accordance with the current DTD, 947 * has an empty content. 948 * @param tag The tag being handled. 949 * @param position The tag position in the text being parsed. 950 */ handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, int position)951 public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, 952 int position) 953 { 954 // Nothing to do here. 955 } 956 957 /** 958 * The method is called when the HTML opening tag ((like <table>) 959 * is found or if the parser concludes that the one should be present 960 * in the current position. 961 * @param tag The tag being handled 962 * @param position The tag position in the text being parsed 963 */ handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, int position)964 public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, 965 int position) 966 { 967 // Nothing to do here. 968 } 969 970 /** 971 * Handle the text section. 972 * @param text A section text. 973 * @param position The text position in the HTML document text being parsed. 974 */ handleText(char[] text, int position)975 public void handleText(char[] text, int position) 976 { 977 // Nothing to do here. 978 } 979 } 980 981 /** 982 * Use serialVersionUID (v1.4) for interoperability. 983 */ 984 private static final long serialVersionUID = 8751997116710384592L; 985 986 /** 987 * Default cascading stylesheed file ("default.css"). 988 */ 989 public static final String DEFAULT_CSS = "default.css"; 990 991 /** 992 * The <b>bold</b> action identifier. 993 */ 994 public static final String BOLD_ACTION = "html-bold-action"; 995 996 /** 997 * The <i>italic</i> action identifier. 998 */ 999 public static final String ITALIC_ACTION = "html-italic-action"; 1000 1001 /** 1002 * The <font color="#FF0000">color</font> action indentifier 1003 * (passing the color as an argument). 1004 */ 1005 public static final String COLOR_ACTION = "html-color-action"; 1006 1007 /** 1008 * The <font size="+1">increase</font> font action identifier. 1009 */ 1010 public static final String FONT_CHANGE_BIGGER = "html-font-bigger"; 1011 1012 /** 1013 * The <font size="-1">decrease</font> font action identifier. 1014 */ 1015 public static final String FONT_CHANGE_SMALLER = "html-font-smaller"; 1016 1017 /** 1018 * Align images at the bottom. 1019 */ 1020 public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom"; 1021 1022 /** 1023 * Align images at the middle. 1024 */ 1025 public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle"; 1026 1027 /** 1028 * Align images at the top. 1029 */ 1030 public static final String IMG_ALIGN_TOP = "html-image-align-top"; 1031 1032 /** 1033 * Align images at the border. 1034 */ 1035 public static final String IMG_BORDER = "html-image-border"; 1036 1037 /** 1038 * The "logical style" action identifier, passing that style as parameter. 1039 */ 1040 public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action"; 1041 1042 /** 1043 * The "ident paragraph left" action. 1044 */ 1045 public static final String PARA_INDENT_LEFT = "html-para-indent-left"; 1046 1047 /** 1048 * The "ident paragraph right" action. 1049 */ 1050 public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; 1051 1052 /** 1053 * Actions for HTML 1054 */ 1055 private static final Action[] defaultActions = 1056 { 1057 new InsertHTMLTextAction("InsertTable", 1058 "<table border=1><tr><td></td></tr></table>", 1059 HTML.Tag.BODY, HTML.Tag.TABLE), 1060 new InsertHTMLTextAction("InsertTableRow", 1061 "<table border=1><tr><td></td></tr></table>", 1062 HTML.Tag.TABLE, HTML.Tag.TR, 1063 HTML.Tag.BODY, HTML.Tag.TABLE), 1064 new InsertHTMLTextAction("InsertTableCell", 1065 "<table border=1><tr><td></td></tr></table>", 1066 HTML.Tag.TR, HTML.Tag.TD, 1067 HTML.Tag.BODY, HTML.Tag.TABLE), 1068 new InsertHTMLTextAction("InsertUnorderedList", 1069 "<ul><li></li></ul>", 1070 HTML.Tag.BODY, HTML.Tag.UL), 1071 new InsertHTMLTextAction("InsertUnorderedListItem", 1072 "<ul><li></li></ul>", 1073 HTML.Tag.UL, HTML.Tag.LI, 1074 HTML.Tag.BODY, HTML.Tag.UL), 1075 new InsertHTMLTextAction("InsertOrderedList", 1076 "<ol><li></li></ol>", 1077 HTML.Tag.BODY, HTML.Tag.OL), 1078 new InsertHTMLTextAction("InsertOrderedListItem", 1079 "<ol><li></li></ol>", 1080 HTML.Tag.OL, HTML.Tag.LI, 1081 HTML.Tag.BODY, HTML.Tag.OL), 1082 new InsertHTMLTextAction("InsertPre", 1083 "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE) 1084 // TODO: The reference impl has an InsertHRAction too. 1085 }; 1086 1087 /** 1088 * The current style sheet. 1089 */ 1090 private StyleSheet styleSheet; 1091 1092 /** 1093 * The ViewFactory for HTMLFactory. 1094 */ 1095 HTMLFactory viewFactory; 1096 1097 /** 1098 * The Cursor for links. 1099 */ 1100 Cursor linkCursor; 1101 1102 /** 1103 * The default cursor. 1104 */ 1105 Cursor defaultCursor; 1106 1107 /** 1108 * The parser. 1109 */ 1110 Parser parser; 1111 1112 /** 1113 * The mouse listener used for links. 1114 */ 1115 private LinkController linkController; 1116 1117 /** The content type */ 1118 String contentType = "text/html"; 1119 1120 /** The input attributes defined by default.css */ 1121 MutableAttributeSet inputAttributes; 1122 1123 /** The editor pane used. */ 1124 JEditorPane editorPane; 1125 1126 /** 1127 * Whether or not the editor kit handles form submissions. 1128 * 1129 * @see #isAutoFormSubmission() 1130 * @see #setAutoFormSubmission(boolean) 1131 */ 1132 private boolean autoFormSubmission; 1133 1134 /** 1135 * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet. 1136 */ HTMLEditorKit()1137 public HTMLEditorKit() 1138 { 1139 linkController = new LinkController(); 1140 autoFormSubmission = true; 1141 } 1142 1143 /** 1144 * Gets a factory suitable for producing views of any 1145 * models that are produced by this kit. 1146 * 1147 * @return the view factory suitable for producing views. 1148 */ getViewFactory()1149 public ViewFactory getViewFactory() 1150 { 1151 if (viewFactory == null) 1152 viewFactory = new HTMLFactory(); 1153 return viewFactory; 1154 } 1155 1156 /** 1157 * Create a text storage model for this type of editor. 1158 * 1159 * @return the model 1160 */ createDefaultDocument()1161 public Document createDefaultDocument() 1162 { 1163 // Protect the shared stylesheet. 1164 StyleSheet styleSheet = getStyleSheet(); 1165 StyleSheet ss = new StyleSheet(); 1166 ss.addStyleSheet(styleSheet); 1167 1168 HTMLDocument document = new HTMLDocument(ss); 1169 document.setParser(getParser()); 1170 document.setAsynchronousLoadPriority(4); 1171 document.setTokenThreshold(100); 1172 return document; 1173 } 1174 1175 /** 1176 * Get the parser that this editor kit uses for reading HTML streams. This 1177 * method can be overridden to use the alternative parser. 1178 * 1179 * @return the HTML parser (by default, {@link ParserDelegator}). 1180 */ getParser()1181 protected Parser getParser() 1182 { 1183 if (parser == null) 1184 { 1185 parser = new GnuParserDelegator(HTML_401F.getInstance()); 1186 } 1187 return parser; 1188 } 1189 1190 /** 1191 * Inserts HTML into an existing document. 1192 * 1193 * @param doc - the Document to insert the HTML into. 1194 * @param offset - where to begin inserting the HTML. 1195 * @param html - the String to insert 1196 * @param popDepth - the number of ElementSpec.EndTagTypes 1197 * to generate before inserting 1198 * @param pushDepth - the number of ElementSpec.StartTagTypes 1199 * with a direction of ElementSpec.JoinNextDirection that 1200 * should be generated before 1201 * @param insertTag - the first tag to start inserting into document 1202 * @throws IOException - on any I/O error 1203 * @throws BadLocationException - if pos represents an invalid location 1204 * within the document 1205 */ insertHTML(HTMLDocument doc, int offset, String html, int popDepth, int pushDepth, HTML.Tag insertTag)1206 public void insertHTML(HTMLDocument doc, int offset, String html, 1207 int popDepth, int pushDepth, HTML.Tag insertTag) 1208 throws BadLocationException, IOException 1209 { 1210 Parser parser = getParser(); 1211 if (offset < 0 || offset > doc.getLength()) 1212 throw new BadLocationException("Bad location", offset); 1213 if (parser == null) 1214 throw new IOException("Parser is null."); 1215 1216 ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag); 1217 1218 // FIXME: What should ignoreCharSet be set to? 1219 1220 // parser.parse inserts html into the buffer 1221 parser.parse(new StringReader(html), pc, false); 1222 pc.flush(); 1223 } 1224 1225 /** 1226 * Inserts content from the given stream. Inserting HTML into a non-empty 1227 * document must be inside the body Element, if you do not insert into 1228 * the body an exception will be thrown. When inserting into a non-empty 1229 * document all tags outside of the body (head, title) will be dropped. 1230 * 1231 * @param in - the stream to read from 1232 * @param doc - the destination for the insertion 1233 * @param pos - the location in the document to place the content 1234 * @throws IOException - on any I/O error 1235 * @throws BadLocationException - if pos represents an invalid location 1236 * within the document 1237 */ read(Reader in, Document doc, int pos)1238 public void read(Reader in, Document doc, int pos) throws IOException, 1239 BadLocationException 1240 { 1241 if (doc instanceof HTMLDocument) 1242 { 1243 Parser parser = getParser(); 1244 if (pos < 0 || pos > doc.getLength()) 1245 throw new BadLocationException("Bad location", pos); 1246 if (parser == null) 1247 throw new IOException("Parser is null."); 1248 1249 HTMLDocument hd = ((HTMLDocument) doc); 1250 if (editorPane != null) 1251 hd.setBase(editorPane.getPage()); 1252 ParserCallback pc = hd.getReader(pos); 1253 1254 // FIXME: What should ignoreCharSet be set to? 1255 1256 // parser.parse inserts html into the buffer 1257 parser.parse(in, pc, false); 1258 pc.flush(); 1259 } 1260 else 1261 // read in DefaultEditorKit is called. 1262 // the string is inserted in the document as usual. 1263 super.read(in, doc, pos); 1264 } 1265 1266 /** 1267 * Writes content from a document to the given stream in 1268 * an appropriate format. 1269 * 1270 * @param out - the stream to write to 1271 * @param doc - the source for the write 1272 * @param pos - the location in the document to get the content. 1273 * @param len - the amount to write out 1274 * @throws IOException - on any I/O error 1275 * @throws BadLocationException - if pos represents an invalid location 1276 * within the document 1277 */ write(Writer out, Document doc, int pos, int len)1278 public void write(Writer out, Document doc, int pos, int len) 1279 throws IOException, BadLocationException 1280 { 1281 if (doc instanceof HTMLDocument) 1282 { 1283 HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len); 1284 writer.write(); 1285 } 1286 else if (doc instanceof StyledDocument) 1287 { 1288 MinimalHTMLWriter writer = new MinimalHTMLWriter(out, 1289 (StyledDocument) doc, 1290 pos, len); 1291 writer.write(); 1292 } 1293 else 1294 super.write(out, doc, pos, len); 1295 } 1296 1297 /** 1298 * Gets the content type that the kit supports. 1299 * This kit supports the type text/html. 1300 * 1301 * @returns the content type supported. 1302 */ getContentType()1303 public String getContentType() 1304 { 1305 return contentType; 1306 } 1307 1308 /** 1309 * Creates a copy of the editor kit. 1310 * 1311 * @return a copy of this. 1312 */ clone()1313 public Object clone() 1314 { 1315 // FIXME: Need to clone all fields 1316 HTMLEditorKit copy = (HTMLEditorKit) super.clone(); 1317 copy.linkController = new LinkController(); 1318 return copy; 1319 } 1320 1321 /** 1322 * Copies the key/values in elements AttributeSet into set. 1323 * This does not copy component, icon, or element names attributes. 1324 * This is called anytime the caret moves over a different location. 1325 * 1326 * @param element - the element to create the input attributes for. 1327 * @param set - the set to copy the values into. 1328 */ createInputAttributes(Element element, MutableAttributeSet set)1329 protected void createInputAttributes(Element element, 1330 MutableAttributeSet set) 1331 { 1332 set.removeAttributes(set); 1333 set.addAttributes(element.getAttributes()); 1334 // FIXME: Not fully implemented. 1335 } 1336 1337 /** 1338 * Called when this is installed into the JEditorPane. 1339 * 1340 * @param c - the JEditorPane installed into. 1341 */ install(JEditorPane c)1342 public void install(JEditorPane c) 1343 { 1344 super.install(c); 1345 c.addMouseListener(linkController); 1346 c.addMouseMotionListener(linkController); 1347 editorPane = c; 1348 } 1349 1350 /** 1351 * Called when the this is removed from the JEditorPane. 1352 * It unregisters any listeners. 1353 * 1354 * @param c - the JEditorPane being removed from. 1355 */ deinstall(JEditorPane c)1356 public void deinstall(JEditorPane c) 1357 { 1358 super.deinstall(c); 1359 c.removeMouseListener(linkController); 1360 c.removeMouseMotionListener(linkController); 1361 editorPane = null; 1362 } 1363 1364 /** 1365 * Gets the AccessibleContext associated with this. 1366 * 1367 * @return the AccessibleContext for this. 1368 */ getAccessibleContext()1369 public AccessibleContext getAccessibleContext() 1370 { 1371 // FIXME: Should return an instance of 1372 // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext 1373 // Not implemented yet. 1374 return null; 1375 } 1376 1377 /** 1378 * Gets the action list. This list is supported by the superclass 1379 * augmented by the collection of actions defined locally for style 1380 * operations. 1381 * 1382 * @return an array of all the actions 1383 */ getActions()1384 public Action[] getActions() 1385 { 1386 return TextAction.augmentList(super.getActions(), defaultActions); 1387 } 1388 1389 /** 1390 * Returns the default cursor. 1391 * 1392 * @return the default cursor 1393 */ getDefaultCursor()1394 public Cursor getDefaultCursor() 1395 { 1396 if (defaultCursor == null) 1397 defaultCursor = Cursor.getDefaultCursor(); 1398 return defaultCursor; 1399 } 1400 1401 /** 1402 * Returns the cursor for links. 1403 * 1404 * @return the cursor for links. 1405 */ getLinkCursor()1406 public Cursor getLinkCursor() 1407 { 1408 if (linkCursor == null) 1409 linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); 1410 return linkCursor; 1411 } 1412 1413 /** 1414 * Sets the Cursor for links. 1415 * 1416 * @param cursor - the new cursor for links. 1417 */ setLinkCursor(Cursor cursor)1418 public void setLinkCursor(Cursor cursor) 1419 { 1420 linkCursor = cursor; 1421 } 1422 1423 /** 1424 * Sets the default cursor. 1425 * 1426 * @param cursor - the new default cursor. 1427 */ setDefaultCursor(Cursor cursor)1428 public void setDefaultCursor(Cursor cursor) 1429 { 1430 defaultCursor = cursor; 1431 } 1432 1433 /** 1434 * Gets the input attributes used for the styled editing actions. 1435 * 1436 * @return the attribute set 1437 */ getInputAttributes()1438 public MutableAttributeSet getInputAttributes() 1439 { 1440 return inputAttributes; 1441 } 1442 1443 /** 1444 * Get the set of styles currently being used to render the HTML elements. 1445 * By default the resource specified by DEFAULT_CSS gets loaded, and is 1446 * shared by all HTMLEditorKit instances. 1447 * 1448 * @return the style sheet. 1449 */ getStyleSheet()1450 public StyleSheet getStyleSheet() 1451 { 1452 if (styleSheet == null) 1453 { 1454 try 1455 { 1456 styleSheet = new StyleSheet(); 1457 Class<?> c = HTMLEditorKit.class; 1458 InputStream in = c.getResourceAsStream(DEFAULT_CSS); 1459 InputStreamReader r = new InputStreamReader(in); 1460 styleSheet.loadRules(r, null); 1461 r.close(); 1462 } 1463 catch (IOException ex) 1464 { 1465 throw new RuntimeException("No style available.", ex); 1466 } 1467 } 1468 return styleSheet; 1469 } 1470 1471 /** 1472 * Set the set of styles to be used to render the various HTML elements. 1473 * These styles are specified in terms of CSS specifications. Each document 1474 * produced by the kit will have a copy of the sheet which it can add the 1475 * document specific styles to. By default, the StyleSheet specified is shared 1476 * by all HTMLEditorKit instances. 1477 * 1478 * @param s - the new style sheet 1479 */ setStyleSheet(StyleSheet s)1480 public void setStyleSheet(StyleSheet s) 1481 { 1482 styleSheet = s; 1483 } 1484 1485 /** 1486 * Returns <code>true</code> when forms should be automatically submitted 1487 * by the editor kit. Set this to <code>false</code> when you want to 1488 * intercept form submission. In this case you'd want to listen for 1489 * hyperlink events on the document and handle FormSubmitEvents specially. 1490 * 1491 * The default is <code>true</code>. 1492 * 1493 * @return <code>true</code> when forms should be automatically submitted 1494 * by the editor kit, <code>false</code> otherwise 1495 * 1496 * @since 1.5 1497 * 1498 * @see #setAutoFormSubmission(boolean) 1499 * @see FormSubmitEvent 1500 */ isAutoFormSubmission()1501 public boolean isAutoFormSubmission() 1502 { 1503 return autoFormSubmission; 1504 } 1505 1506 /** 1507 * Sets whether or not the editor kit should automatically submit forms. 1508 * 1509 * @param auto <code>true</code> when the editor kit should handle form 1510 * submission, <code>false</code> otherwise 1511 * 1512 * @since 1.5 1513 * 1514 * @see #isAutoFormSubmission() 1515 */ setAutoFormSubmission(boolean auto)1516 public void setAutoFormSubmission(boolean auto) 1517 { 1518 autoFormSubmission = auto; 1519 } 1520 } 1521