001 /* HTMLDocument.java -- 002 Copyright (C) 2005 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.swing.text.html; 040 041 import gnu.classpath.NotImplementedException; 042 043 import java.io.IOException; 044 import java.io.StringReader; 045 import java.net.MalformedURLException; 046 import java.net.URL; 047 import java.util.ArrayList; 048 import java.util.HashMap; 049 import java.util.Stack; 050 import java.util.Vector; 051 052 import javax.swing.ButtonGroup; 053 import javax.swing.DefaultButtonModel; 054 import javax.swing.JEditorPane; 055 import javax.swing.ListSelectionModel; 056 import javax.swing.event.DocumentEvent; 057 import javax.swing.event.UndoableEditEvent; 058 import javax.swing.text.AbstractDocument; 059 import javax.swing.text.AttributeSet; 060 import javax.swing.text.BadLocationException; 061 import javax.swing.text.DefaultStyledDocument; 062 import javax.swing.text.Element; 063 import javax.swing.text.ElementIterator; 064 import javax.swing.text.GapContent; 065 import javax.swing.text.MutableAttributeSet; 066 import javax.swing.text.PlainDocument; 067 import javax.swing.text.SimpleAttributeSet; 068 import javax.swing.text.StyleConstants; 069 import javax.swing.text.html.HTML.Tag; 070 071 /** 072 * Represents the HTML document that is constructed by defining the text and 073 * other components (images, buttons, etc) in HTML language. This class can 074 * becomes the default document for {@link JEditorPane} after setting its 075 * content type to "text/html". HTML document also serves as an intermediate 076 * data structure when it is needed to parse HTML and then obtain the content of 077 * the certain types of tags. This class also has methods for modifying the HTML 078 * content. 079 * 080 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 081 * @author Anthony Balkissoon (abalkiss@redhat.com) 082 * @author Lillian Angel (langel@redhat.com) 083 */ 084 public class HTMLDocument extends DefaultStyledDocument 085 { 086 /** A key for document properies. The value for the key is 087 * a Vector of Strings of comments not found in the body. 088 */ 089 public static final String AdditionalComments = "AdditionalComments"; 090 URL baseURL = null; 091 boolean preservesUnknownTags = true; 092 int tokenThreshold = Integer.MAX_VALUE; 093 HTMLEditorKit.Parser parser; 094 095 /** 096 * Indicates whether this document is inside a frame or not. 097 */ 098 private boolean frameDocument; 099 100 /** 101 * Package private to avoid accessor methods. 102 */ 103 String baseTarget; 104 105 /** 106 * Constructs an HTML document using the default buffer size and a default 107 * StyleSheet. 108 */ 109 public HTMLDocument() 110 { 111 this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet()); 112 } 113 114 /** 115 * Constructs an HTML document with the default content storage 116 * implementation and the specified style/attribute storage mechanism. 117 * 118 * @param styles - the style sheet 119 */ 120 public HTMLDocument(StyleSheet styles) 121 { 122 this(new GapContent(BUFFER_SIZE_DEFAULT), styles); 123 } 124 125 /** 126 * Constructs an HTML document with the given content storage implementation 127 * and the given style/attribute storage mechanism. 128 * 129 * @param c - the document's content 130 * @param styles - the style sheet 131 */ 132 public HTMLDocument(AbstractDocument.Content c, StyleSheet styles) 133 { 134 super(c, styles); 135 } 136 137 /** 138 * Gets the style sheet with the document display rules (CSS) that were specified 139 * in the HTML document. 140 * 141 * @return - the style sheet 142 */ 143 public StyleSheet getStyleSheet() 144 { 145 return (StyleSheet) getAttributeContext(); 146 } 147 148 /** 149 * This method creates a root element for the new document. 150 * 151 * @return the new default root 152 */ 153 protected AbstractElement createDefaultRoot() 154 { 155 AbstractDocument.AttributeContext ctx = getAttributeContext(); 156 157 // Create html element. 158 AttributeSet atts = ctx.getEmptySet(); 159 atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.HTML); 160 BranchElement html = (BranchElement) createBranchElement(null, atts); 161 162 // Create body element. 163 atts = ctx.getEmptySet(); 164 atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.BODY); 165 BranchElement body = (BranchElement) createBranchElement(html, atts); 166 html.replace(0, 0, new Element[] { body }); 167 168 // Create p element. 169 atts = ctx.getEmptySet(); 170 atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.P); 171 BranchElement p = (BranchElement) createBranchElement(body, atts); 172 body.replace(0, 0, new Element[] { p }); 173 174 // Create an empty leaf element. 175 atts = ctx.getEmptySet(); 176 atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, 177 HTML.Tag.CONTENT); 178 Element leaf = createLeafElement(p, atts, 0, 1); 179 p.replace(0, 0, new Element[]{ leaf }); 180 181 return html; 182 } 183 184 /** 185 * This method returns an HTMLDocument.RunElement object attached to 186 * parent representing a run of text from p0 to p1. The run has 187 * attributes described by a. 188 * 189 * @param parent - the parent element 190 * @param a - the attributes for the element 191 * @param p0 - the beginning of the range >= 0 192 * @param p1 - the end of the range >= p0 193 * 194 * @return the new element 195 */ 196 protected Element createLeafElement(Element parent, AttributeSet a, int p0, 197 int p1) 198 { 199 return new RunElement(parent, a, p0, p1); 200 } 201 202 /** 203 * This method returns an HTMLDocument.BlockElement object representing the 204 * attribute set a and attached to parent. 205 * 206 * @param parent - the parent element 207 * @param a - the attributes for the element 208 * 209 * @return the new element 210 */ 211 protected Element createBranchElement(Element parent, AttributeSet a) 212 { 213 return new BlockElement(parent, a); 214 } 215 216 /** 217 * Returns the parser used by this HTMLDocument to insert HTML. 218 * 219 * @return the parser used by this HTMLDocument to insert HTML. 220 */ 221 public HTMLEditorKit.Parser getParser() 222 { 223 return parser; 224 } 225 226 /** 227 * Sets the parser used by this HTMLDocument to insert HTML. 228 * 229 * @param p the parser to use 230 */ 231 public void setParser (HTMLEditorKit.Parser p) 232 { 233 parser = p; 234 } 235 /** 236 * Sets the number of tokens to buffer before trying to display the 237 * Document. 238 * 239 * @param n the number of tokens to buffer 240 */ 241 public void setTokenThreshold (int n) 242 { 243 tokenThreshold = n; 244 } 245 246 /** 247 * Returns the number of tokens that are buffered before the document 248 * is rendered. 249 * 250 * @return the number of tokens buffered 251 */ 252 public int getTokenThreshold () 253 { 254 return tokenThreshold; 255 } 256 257 /** 258 * Returns the location against which to resolve relative URLs. 259 * This is the document's URL if the document was loaded from a URL. 260 * If a <code>base</code> tag is found, it will be used. 261 * @return the base URL 262 */ 263 public URL getBase() 264 { 265 return baseURL; 266 } 267 268 /** 269 * Sets the location against which to resolve relative URLs. 270 * @param u the new base URL 271 */ 272 public void setBase(URL u) 273 { 274 baseURL = u; 275 getStyleSheet().setBase(u); 276 } 277 278 /** 279 * Returns whether or not the parser preserves unknown HTML tags. 280 * @return true if the parser preserves unknown tags 281 */ 282 public boolean getPreservesUnknownTags() 283 { 284 return preservesUnknownTags; 285 } 286 287 /** 288 * Sets the behaviour of the parser when it encounters unknown HTML tags. 289 * @param preservesTags true if the parser should preserve unknown tags. 290 */ 291 public void setPreservesUnknownTags(boolean preservesTags) 292 { 293 preservesUnknownTags = preservesTags; 294 } 295 296 /** 297 * An iterator to iterate through LeafElements in the document. 298 */ 299 class LeafIterator extends Iterator 300 { 301 HTML.Tag tag; 302 HTMLDocument doc; 303 ElementIterator it; 304 305 public LeafIterator (HTML.Tag t, HTMLDocument d) 306 { 307 doc = d; 308 tag = t; 309 it = new ElementIterator(doc); 310 } 311 312 /** 313 * Return the attributes for the tag associated with this iteartor 314 * @return the AttributeSet 315 */ 316 public AttributeSet getAttributes() 317 { 318 if (it.current() != null) 319 return it.current().getAttributes(); 320 return null; 321 } 322 323 /** 324 * Get the end of the range for the current occurrence of the tag 325 * being defined and having the same attributes. 326 * @return the end of the range 327 */ 328 public int getEndOffset() 329 { 330 if (it.current() != null) 331 return it.current().getEndOffset(); 332 return -1; 333 } 334 335 /** 336 * Get the start of the range for the current occurrence of the tag 337 * being defined and having the same attributes. 338 * @return the start of the range (-1 if it can't be found). 339 */ 340 341 public int getStartOffset() 342 { 343 if (it.current() != null) 344 return it.current().getStartOffset(); 345 return -1; 346 } 347 348 /** 349 * Advance the iterator to the next LeafElement . 350 */ 351 public void next() 352 { 353 it.next(); 354 while (it.current()!= null && !it.current().isLeaf()) 355 it.next(); 356 } 357 358 /** 359 * Indicates whether or not the iterator currently represents an occurrence 360 * of the tag. 361 * @return true if the iterator currently represents an occurrence of the 362 * tag. 363 */ 364 public boolean isValid() 365 { 366 return it.current() != null; 367 } 368 369 /** 370 * Type of tag for this iterator. 371 */ 372 public Tag getTag() 373 { 374 return tag; 375 } 376 377 } 378 379 public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) 380 { 381 String target = event.getTarget(); 382 Element el = event.getSourceElement(); 383 URL url = event.getURL(); 384 if (target.equals("_self")) 385 { 386 updateFrame(el, url); 387 } 388 else if (target.equals("_parent")) 389 { 390 updateFrameSet(el.getParentElement(), url); 391 } 392 else 393 { 394 Element targetFrame = findFrame(target); 395 if (targetFrame != null) 396 updateFrame(targetFrame, url); 397 } 398 } 399 400 /** 401 * Finds the named frame inside this document. 402 * 403 * @param target the name to look for 404 * 405 * @return the frame if there is a matching frame, <code>null</code> 406 * otherwise 407 */ 408 private Element findFrame(String target) 409 { 410 ElementIterator i = new ElementIterator(this); 411 Element next = null; 412 while ((next = i.next()) != null) 413 { 414 AttributeSet atts = next.getAttributes(); 415 if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME) 416 { 417 String name = (String) atts.getAttribute(HTML.Attribute.NAME); 418 if (name != null && name.equals(target)) 419 break; 420 } 421 } 422 return next; 423 } 424 425 /** 426 * Updates the frame that is represented by the specified element to 427 * refer to the specified URL. 428 * 429 * @param el the element 430 * @param url the new url 431 */ 432 private void updateFrame(Element el, URL url) 433 { 434 try 435 { 436 writeLock(); 437 DefaultDocumentEvent ev = 438 new DefaultDocumentEvent(el.getStartOffset(), 1, 439 DocumentEvent.EventType.CHANGE); 440 AttributeSet elAtts = el.getAttributes(); 441 AttributeSet copy = elAtts.copyAttributes(); 442 MutableAttributeSet matts = (MutableAttributeSet) elAtts; 443 ev.addEdit(new AttributeUndoableEdit(el, copy, false)); 444 matts.removeAttribute(HTML.Attribute.SRC); 445 matts.addAttribute(HTML.Attribute.SRC, url.toString()); 446 ev.end(); 447 fireChangedUpdate(ev); 448 fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); 449 } 450 finally 451 { 452 writeUnlock(); 453 } 454 } 455 456 /** 457 * Updates the frameset that is represented by the specified element 458 * to create a frame that refers to the specified URL. 459 * 460 * @param el the element 461 * @param url the url 462 */ 463 private void updateFrameSet(Element el, URL url) 464 { 465 int start = el.getStartOffset(); 466 int end = el.getEndOffset(); 467 468 StringBuilder html = new StringBuilder(); 469 html.append("<frame"); 470 if (url != null) 471 { 472 html.append(" src=\""); 473 html.append(url.toString()); 474 html.append("\""); 475 } 476 html.append('>'); 477 if (getParser() == null) 478 setParser(new HTMLEditorKit().getParser()); 479 try 480 { 481 setOuterHTML(el, html.toString()); 482 } 483 catch (BadLocationException ex) 484 { 485 ex.printStackTrace(); 486 } 487 catch (IOException ex) 488 { 489 ex.printStackTrace(); 490 } 491 } 492 493 /** 494 * Gets an iterator for the given HTML.Tag. 495 * @param t the requested HTML.Tag 496 * @return the Iterator 497 */ 498 public HTMLDocument.Iterator getIterator (HTML.Tag t) 499 { 500 return new HTMLDocument.LeafIterator(t, this); 501 } 502 503 /** 504 * An iterator over a particular type of tag. 505 */ 506 public abstract static class Iterator 507 { 508 /** 509 * Return the attribute set for this tag. 510 * @return the <code>AttributeSet</code> (null if none found). 511 */ 512 public abstract AttributeSet getAttributes(); 513 514 /** 515 * Get the end of the range for the current occurrence of the tag 516 * being defined and having the same attributes. 517 * @return the end of the range 518 */ 519 public abstract int getEndOffset(); 520 521 /** 522 * Get the start of the range for the current occurrence of the tag 523 * being defined and having the same attributes. 524 * @return the start of the range (-1 if it can't be found). 525 */ 526 public abstract int getStartOffset(); 527 528 /** 529 * Move the iterator forward. 530 */ 531 public abstract void next(); 532 533 /** 534 * Indicates whether or not the iterator currently represents an occurrence 535 * of the tag. 536 * @return true if the iterator currently represents an occurrence of the 537 * tag. 538 */ 539 public abstract boolean isValid(); 540 541 /** 542 * Type of tag this iterator represents. 543 * @return the tag. 544 */ 545 public abstract HTML.Tag getTag(); 546 } 547 548 public class BlockElement extends AbstractDocument.BranchElement 549 { 550 public BlockElement (Element parent, AttributeSet a) 551 { 552 super(parent, a); 553 } 554 555 /** 556 * Gets the resolving parent. Since HTML attributes are not 557 * inherited at the model level, this returns null. 558 */ 559 public AttributeSet getResolveParent() 560 { 561 return null; 562 } 563 564 /** 565 * Gets the name of the element. 566 * 567 * @return the name of the element if it exists, null otherwise. 568 */ 569 public String getName() 570 { 571 Object tag = getAttribute(StyleConstants.NameAttribute); 572 String name = null; 573 if (tag != null) 574 name = tag.toString(); 575 if (name == null) 576 name = super.getName(); 577 return name; 578 } 579 } 580 581 /** 582 * RunElement represents a section of text that has a set of 583 * HTML character level attributes assigned to it. 584 */ 585 public class RunElement extends AbstractDocument.LeafElement 586 { 587 588 /** 589 * Constructs an element that has no children. It represents content 590 * within the document. 591 * 592 * @param parent - parent of this 593 * @param a - elements attributes 594 * @param start - the start offset >= 0 595 * @param end - the end offset 596 */ 597 public RunElement(Element parent, AttributeSet a, int start, int end) 598 { 599 super(parent, a, start, end); 600 } 601 602 /** 603 * Gets the name of the element. 604 * 605 * @return the name of the element if it exists, null otherwise. 606 */ 607 public String getName() 608 { 609 Object tag = getAttribute(StyleConstants.NameAttribute); 610 String name = null; 611 if (tag != null) 612 name = tag.toString(); 613 if (name == null) 614 name = super.getName(); 615 return name; 616 } 617 618 /** 619 * Gets the resolving parent. HTML attributes do not inherit at the 620 * model level, so this method returns null. 621 * 622 * @return null 623 */ 624 public AttributeSet getResolveParent() 625 { 626 return null; 627 } 628 } 629 630 /** 631 * A reader to load an HTMLDocument with HTML structure. 632 * 633 * @author Anthony Balkissoon abalkiss at redhat dot com 634 */ 635 public class HTMLReader extends HTMLEditorKit.ParserCallback 636 { 637 /** 638 * The maximum token threshold. We don't grow it larger than this. 639 */ 640 private static final int MAX_THRESHOLD = 10000; 641 642 /** 643 * The threshold growth factor. 644 */ 645 private static final int GROW_THRESHOLD = 5; 646 647 /** 648 * Holds the current character attribute set * 649 */ 650 protected MutableAttributeSet charAttr = new SimpleAttributeSet(); 651 652 protected Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>(); 653 654 /** 655 * The parse stack. It holds the current element tree path. 656 */ 657 private Stack<HTML.Tag> parseStack = new Stack<HTML.Tag>(); 658 659 /** 660 * A stack for character attribute sets * 661 */ 662 Stack charAttrStack = new Stack(); 663 664 /** A mapping between HTML.Tag objects and the actions that handle them **/ 665 HashMap tagToAction; 666 667 /** Tells us whether we've received the '</html>' tag yet **/ 668 boolean endHTMLEncountered = false; 669 670 /** 671 * Related to the constructor with explicit insertTag 672 */ 673 int popDepth; 674 675 /** 676 * Related to the constructor with explicit insertTag 677 */ 678 int pushDepth; 679 680 /** 681 * Related to the constructor with explicit insertTag 682 */ 683 int offset; 684 685 /** 686 * The tag (inclusve), after that the insertion should start. 687 */ 688 HTML.Tag insertTag; 689 690 /** 691 * This variable becomes true after the insert tag has been encountered. 692 */ 693 boolean insertTagEncountered; 694 695 696 /** A temporary variable that helps with the printing out of debug information **/ 697 boolean debug = false; 698 699 /** 700 * This is true when we are inside a pre tag. 701 */ 702 boolean inPreTag = false; 703 704 /** 705 * This is true when we are inside a style tag. This will add text 706 * content inside this style tag beeing parsed as CSS. 707 * 708 * This is package private to avoid accessor methods. 709 */ 710 boolean inStyleTag = false; 711 712 /** 713 * This is true when we are inside a <textarea> tag. Any text 714 * content will then be added to the text area. 715 * 716 * This is package private to avoid accessor methods. 717 */ 718 boolean inTextArea = false; 719 720 /** 721 * This contains all stylesheets that are somehow read, either 722 * via embedded style tags, or via linked stylesheets. The 723 * elements will be String objects containing a stylesheet each. 724 */ 725 ArrayList styles; 726 727 /** 728 * The document model for a textarea. 729 * 730 * This is package private to avoid accessor methods. 731 */ 732 ResetablePlainDocument textAreaDocument; 733 734 /** 735 * The current model of a select tag. Can be a ComboBoxModel or a 736 * ListModel depending on the type of the select box. 737 */ 738 Object selectModel; 739 740 /** 741 * The current option beeing read. 742 */ 743 Option option; 744 745 /** 746 * The current number of options in the current select model. 747 */ 748 int numOptions; 749 750 /** 751 * The current button groups mappings. 752 */ 753 HashMap buttonGroups; 754 755 /** 756 * The token threshold. This gets increased while loading. 757 */ 758 private int threshold; 759 760 public class TagAction 761 { 762 /** 763 * This method is called when a start tag is seen for one of the types 764 * of tags associated with this Action. By default this does nothing. 765 */ 766 public void start(HTML.Tag t, MutableAttributeSet a) 767 { 768 // Nothing to do here. 769 } 770 771 /** 772 * Called when an end tag is seen for one of the types of tags associated 773 * with this Action. By default does nothing. 774 */ 775 public void end(HTML.Tag t) 776 { 777 // Nothing to do here. 778 } 779 } 780 781 public class BlockAction extends TagAction 782 { 783 /** 784 * This method is called when a start tag is seen for one of the types 785 * of tags associated with this Action. 786 */ 787 public void start(HTML.Tag t, MutableAttributeSet a) 788 { 789 // Tell the parse buffer to open a new block for this tag. 790 blockOpen(t, a); 791 } 792 793 /** 794 * Called when an end tag is seen for one of the types of tags associated 795 * with this Action. 796 */ 797 public void end(HTML.Tag t) 798 { 799 // Tell the parse buffer to close this block. 800 blockClose(t); 801 } 802 } 803 804 public class CharacterAction extends TagAction 805 { 806 /** 807 * This method is called when a start tag is seen for one of the types 808 * of tags associated with this Action. 809 */ 810 public void start(HTML.Tag t, MutableAttributeSet a) 811 { 812 // Put the old attribute set on the stack. 813 pushCharacterStyle(); 814 815 // Initialize with link pseudo class. 816 if (t == HTML.Tag.A) 817 a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link"); 818 819 // Just add the attributes in <code>a</code>. 820 charAttr.addAttribute(t, a.copyAttributes()); 821 } 822 823 /** 824 * Called when an end tag is seen for one of the types of tags associated 825 * with this Action. 826 */ 827 public void end(HTML.Tag t) 828 { 829 popCharacterStyle(); 830 } 831 } 832 833 /** 834 * Processes elements that make up forms: <input>, <textarea>, 835 * <select> and <option>. 836 */ 837 public class FormAction extends SpecialAction 838 { 839 /** 840 * This method is called when a start tag is seen for one of the types 841 * of tags associated with this Action. 842 */ 843 public void start(HTML.Tag t, MutableAttributeSet a) 844 { 845 if (t == HTML.Tag.INPUT) 846 { 847 String type = (String) a.getAttribute(HTML.Attribute.TYPE); 848 if (type == null) 849 { 850 type = "text"; // Default to 'text' when nothing was specified. 851 a.addAttribute(HTML.Attribute.TYPE, type); 852 } 853 setModel(type, a); 854 } 855 else if (t == HTML.Tag.TEXTAREA) 856 { 857 inTextArea = true; 858 textAreaDocument = new ResetablePlainDocument(); 859 a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument); 860 } 861 else if (t == HTML.Tag.SELECT) 862 { 863 int size = HTML.getIntegerAttributeValue(a, HTML.Attribute.SIZE, 864 1); 865 boolean multi = a.getAttribute(HTML.Attribute.MULTIPLE) != null; 866 if (size > 1 || multi) 867 { 868 SelectListModel m = new SelectListModel(); 869 if (multi) 870 m.getSelectionModel().setSelectionMode(ListSelectionModel 871 .MULTIPLE_INTERVAL_SELECTION); 872 selectModel = m; 873 } 874 else 875 { 876 selectModel = new SelectComboBoxModel(); 877 } 878 a.addAttribute(StyleConstants.ModelAttribute, selectModel); 879 } 880 if (t == HTML.Tag.OPTION) 881 { 882 option = new Option(a); 883 if (selectModel instanceof SelectListModel) 884 { 885 SelectListModel m = (SelectListModel) selectModel; 886 m.addElement(option); 887 if (option.isSelected()) 888 { 889 m.getSelectionModel().addSelectionInterval(numOptions, 890 numOptions); 891 m.addInitialSelection(numOptions); 892 } 893 } 894 else if (selectModel instanceof SelectComboBoxModel) 895 { 896 SelectComboBoxModel m = (SelectComboBoxModel) selectModel; 897 m.addElement(option); 898 if (option.isSelected()) 899 { 900 m.setSelectedItem(option); 901 m.setInitialSelection(option); 902 } 903 } 904 numOptions++; 905 } 906 else 907 { 908 // Build the element. 909 super.start(t, a); 910 } 911 } 912 913 /** 914 * Called when an end tag is seen for one of the types of tags associated 915 * with this Action. 916 */ 917 public void end(HTML.Tag t) 918 { 919 if (t == HTML.Tag.OPTION) 920 { 921 option = null; 922 } 923 else 924 { 925 if (t == HTML.Tag.TEXTAREA) 926 { 927 inTextArea = false; 928 } 929 else if (t == HTML.Tag.SELECT) 930 { 931 selectModel = null; 932 numOptions = 0; 933 } 934 // Finish the element. 935 super.end(t); 936 } 937 } 938 939 private void setModel(String type, MutableAttributeSet attrs) 940 { 941 if (type.equals("submit") || type.equals("reset") 942 || type.equals("image")) 943 { 944 // Create button. 945 attrs.addAttribute(StyleConstants.ModelAttribute, 946 new DefaultButtonModel()); 947 } 948 else if (type.equals("text") || type.equals("password")) 949 { 950 String text = (String) attrs.getAttribute(HTML.Attribute.VALUE); 951 ResetablePlainDocument doc = new ResetablePlainDocument(); 952 if (text != null) 953 { 954 doc.setInitialText(text); 955 try 956 { 957 doc.insertString(0, text, null); 958 } 959 catch (BadLocationException ex) 960 { 961 // Shouldn't happen. 962 assert false; 963 } 964 } 965 attrs.addAttribute(StyleConstants.ModelAttribute, doc); 966 } 967 else if (type.equals("file")) 968 { 969 attrs.addAttribute(StyleConstants.ModelAttribute, 970 new PlainDocument()); 971 } 972 else if (type.equals("checkbox") || type.equals("radio")) 973 { 974 ResetableToggleButtonModel model = 975 new ResetableToggleButtonModel(); 976 if (attrs.getAttribute(HTML.Attribute.SELECTED) != null) 977 { 978 model.setSelected(true); 979 model.setInitial(true); 980 } 981 if (type.equals("radio")) 982 { 983 String name = (String) attrs.getAttribute(HTML.Attribute.NAME); 984 if (name != null) 985 { 986 if (buttonGroups == null) 987 buttonGroups = new HashMap(); 988 ButtonGroup group = (ButtonGroup) buttonGroups.get(name); 989 if (group == null) 990 { 991 group = new ButtonGroup(); 992 buttonGroups.put(name, group); 993 } 994 model.setGroup(group); 995 } 996 } 997 attrs.addAttribute(StyleConstants.ModelAttribute, model); 998 } 999 } 1000 } 1001 1002 /** 1003 * Called for form tags. 1004 */ 1005 class FormTagAction 1006 extends BlockAction 1007 { 1008 /** 1009 * Clears the button group mapping. 1010 */ 1011 public void end(HTML.Tag t) 1012 { 1013 super.end(t); 1014 buttonGroups = null; 1015 } 1016 } 1017 1018 /** 1019 * This action indicates that the content between starting and closing HTML 1020 * elements (like script - /script) should not be visible. The content is 1021 * still inserted and can be accessed when iterating the HTML document. The 1022 * parser will only fire 1023 * {@link javax.swing.text.html.HTMLEditorKit.ParserCallback#handleText} for 1024 * the hidden tags, regardless from that html tags the hidden section may 1025 * contain. 1026 */ 1027 public class HiddenAction 1028 extends TagAction 1029 { 1030 /** 1031 * This method is called when a start tag is seen for one of the types 1032 * of tags associated with this Action. 1033 */ 1034 public void start(HTML.Tag t, MutableAttributeSet a) 1035 { 1036 blockOpen(t, a); 1037 } 1038 1039 /** 1040 * Called when an end tag is seen for one of the types of tags associated 1041 * with this Action. 1042 */ 1043 public void end(HTML.Tag t) 1044 { 1045 blockClose(t); 1046 } 1047 } 1048 1049 /** 1050 * Handles <isindex> tags. 1051 */ 1052 public class IsindexAction extends TagAction 1053 { 1054 /** 1055 * This method is called when a start tag is seen for one of the types 1056 * of tags associated with this Action. 1057 */ 1058 public void start(HTML.Tag t, MutableAttributeSet a) 1059 { 1060 blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); 1061 addSpecialElement(t, a); 1062 blockClose(HTML.Tag.IMPLIED); 1063 } 1064 } 1065 1066 public class ParagraphAction extends BlockAction 1067 { 1068 /** 1069 * This method is called when a start tag is seen for one of the types 1070 * of tags associated with this Action. 1071 */ 1072 public void start(HTML.Tag t, MutableAttributeSet a) 1073 { 1074 super.start(t, a); 1075 } 1076 1077 /** 1078 * Called when an end tag is seen for one of the types of tags associated 1079 * with this Action. 1080 */ 1081 public void end(HTML.Tag t) 1082 { 1083 super.end(t); 1084 } 1085 } 1086 1087 /** 1088 * This action is performed when a <pre> tag is parsed. 1089 */ 1090 public class PreAction extends BlockAction 1091 { 1092 /** 1093 * This method is called when a start tag is seen for one of the types 1094 * of tags associated with this Action. 1095 */ 1096 public void start(HTML.Tag t, MutableAttributeSet a) 1097 { 1098 inPreTag = true; 1099 blockOpen(t, a); 1100 a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); 1101 blockOpen(HTML.Tag.IMPLIED, a); 1102 } 1103 1104 /** 1105 * Called when an end tag is seen for one of the types of tags associated 1106 * with this Action. 1107 */ 1108 public void end(HTML.Tag t) 1109 { 1110 blockClose(HTML.Tag.IMPLIED); 1111 inPreTag = false; 1112 blockClose(t); 1113 } 1114 } 1115 1116 /** 1117 * Inserts the elements that are represented by ths single tag with 1118 * attributes (only). The closing tag, even if present, mut follow 1119 * immediately after the starting tag without providing any additional 1120 * information. Hence the {@link TagAction#end} method need not be 1121 * overridden and still does nothing. 1122 */ 1123 public class SpecialAction extends TagAction 1124 { 1125 /** 1126 * The functionality is delegated to {@link HTMLReader#addSpecialElement} 1127 */ 1128 public void start(HTML.Tag t, MutableAttributeSet a) 1129 { 1130 addSpecialElement(t, a); 1131 } 1132 } 1133 1134 class AreaAction extends TagAction 1135 { 1136 /** 1137 * This method is called when a start tag is seen for one of the types 1138 * of tags associated with this Action. 1139 */ 1140 public void start(HTML.Tag t, MutableAttributeSet a) 1141 throws NotImplementedException 1142 { 1143 // FIXME: Implement. 1144 } 1145 1146 /** 1147 * Called when an end tag is seen for one of the types of tags associated 1148 * with this Action. 1149 */ 1150 public void end(HTML.Tag t) 1151 throws NotImplementedException 1152 { 1153 // FIXME: Implement. 1154 } 1155 } 1156 1157 /** 1158 * Converts HTML tags to CSS attributes. 1159 */ 1160 class ConvertAction 1161 extends TagAction 1162 { 1163 1164 public void start(HTML.Tag tag, MutableAttributeSet atts) 1165 { 1166 pushCharacterStyle(); 1167 charAttr.addAttribute(tag, atts.copyAttributes()); 1168 StyleSheet styleSheet = getStyleSheet(); 1169 // TODO: Add other tags here. 1170 if (tag == HTML.Tag.FONT) 1171 { 1172 String color = (String) atts.getAttribute(HTML.Attribute.COLOR); 1173 if (color != null) 1174 styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color); 1175 String face = (String) atts.getAttribute(HTML.Attribute.FACE); 1176 if (face != null) 1177 styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY, 1178 face); 1179 String size = (String) atts.getAttribute(HTML.Attribute.SIZE); 1180 if (size != null) 1181 styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE, 1182 size); 1183 } 1184 } 1185 1186 public void end(HTML.Tag tag) 1187 { 1188 popCharacterStyle(); 1189 } 1190 } 1191 1192 class BaseAction extends TagAction 1193 { 1194 /** 1195 * This method is called when a start tag is seen for one of the types 1196 * of tags associated with this Action. 1197 */ 1198 public void start(HTML.Tag t, MutableAttributeSet a) 1199 { 1200 baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET); 1201 } 1202 } 1203 1204 class HeadAction extends BlockAction 1205 { 1206 /** 1207 * This method is called when a start tag is seen for one of the types 1208 * of tags associated with this Action. 1209 */ 1210 public void start(HTML.Tag t, MutableAttributeSet a) 1211 throws NotImplementedException 1212 { 1213 // FIXME: Implement. 1214 super.start(t, a); 1215 } 1216 1217 /** 1218 * Called when an end tag is seen for one of the types of tags associated 1219 * with this Action. 1220 */ 1221 public void end(HTML.Tag t) 1222 { 1223 // We read in all the stylesheets that are embedded or referenced 1224 // inside the header. 1225 if (styles != null) 1226 { 1227 int numStyles = styles.size(); 1228 for (int i = 0; i < numStyles; i++) 1229 { 1230 String style = (String) styles.get(i); 1231 getStyleSheet().addRule(style); 1232 } 1233 } 1234 super.end(t); 1235 } 1236 } 1237 1238 class LinkAction extends HiddenAction 1239 { 1240 /** 1241 * This method is called when a start tag is seen for one of the types 1242 * of tags associated with this Action. 1243 */ 1244 public void start(HTML.Tag t, MutableAttributeSet a) 1245 { 1246 super.start(t, a); 1247 String type = (String) a.getAttribute(HTML.Attribute.TYPE); 1248 if (type == null) 1249 type = "text/css"; 1250 if (type.equals("text/css")) 1251 { 1252 String rel = (String) a.getAttribute(HTML.Attribute.REL); 1253 String media = (String) a.getAttribute(HTML.Attribute.MEDIA); 1254 String title = (String) a.getAttribute(HTML.Attribute.TITLE); 1255 if (media == null) 1256 media = "all"; 1257 else 1258 media = media.toLowerCase(); 1259 if (rel != null) 1260 { 1261 rel = rel.toLowerCase(); 1262 if ((media.indexOf("all") != -1 1263 || media.indexOf("screen") != -1) 1264 && (rel.equals("stylesheet"))) 1265 { 1266 String href = (String) a.getAttribute(HTML.Attribute.HREF); 1267 URL url = null; 1268 try 1269 { 1270 url = new URL(baseURL, href); 1271 } 1272 catch (MalformedURLException ex) 1273 { 1274 try 1275 { 1276 url = new URL(href); 1277 } 1278 catch (MalformedURLException ex2) 1279 { 1280 url = null; 1281 } 1282 } 1283 if (url != null) 1284 { 1285 try 1286 { 1287 getStyleSheet().importStyleSheet(url); 1288 } 1289 catch (Exception ex) 1290 { 1291 // Don't let exceptions and runtime exceptions 1292 // in CSS parsing disprupt the HTML parsing 1293 // process. But inform the user/developer 1294 // on the console about it. 1295 ex.printStackTrace(); 1296 } 1297 } 1298 } 1299 } 1300 } 1301 } 1302 1303 } 1304 1305 class MapAction extends TagAction 1306 { 1307 /** 1308 * This method is called when a start tag is seen for one of the types 1309 * of tags associated with this Action. 1310 */ 1311 public void start(HTML.Tag t, MutableAttributeSet a) 1312 throws NotImplementedException 1313 { 1314 // FIXME: Implement. 1315 } 1316 1317 /** 1318 * Called when an end tag is seen for one of the types of tags associated 1319 * with this Action. 1320 */ 1321 public void end(HTML.Tag t) 1322 throws NotImplementedException 1323 { 1324 // FIXME: Implement. 1325 } 1326 } 1327 1328 class MetaAction extends TagAction 1329 { 1330 /** 1331 * This method is called when a start tag is seen for one of the types 1332 * of tags associated with this Action. 1333 */ 1334 public void start(HTML.Tag t, MutableAttributeSet a) 1335 throws NotImplementedException 1336 { 1337 // FIXME: Implement. 1338 } 1339 1340 /** 1341 * Called when an end tag is seen for one of the types of tags associated 1342 * with this Action. 1343 */ 1344 public void end(HTML.Tag t) 1345 throws NotImplementedException 1346 { 1347 // FIXME: Implement. 1348 } 1349 } 1350 1351 class StyleAction extends TagAction 1352 { 1353 /** 1354 * This method is called when a start tag is seen for one of the types 1355 * of tags associated with this Action. 1356 */ 1357 public void start(HTML.Tag t, MutableAttributeSet a) 1358 { 1359 inStyleTag = true; 1360 } 1361 1362 /** 1363 * Called when an end tag is seen for one of the types of tags associated 1364 * with this Action. 1365 */ 1366 public void end(HTML.Tag t) 1367 { 1368 inStyleTag = false; 1369 } 1370 } 1371 1372 class TitleAction extends TagAction 1373 { 1374 /** 1375 * This method is called when a start tag is seen for one of the types 1376 * of tags associated with this Action. 1377 */ 1378 public void start(HTML.Tag t, MutableAttributeSet a) 1379 throws NotImplementedException 1380 { 1381 // FIXME: Implement. 1382 } 1383 1384 /** 1385 * Called when an end tag is seen for one of the types of tags associated 1386 * with this Action. 1387 */ 1388 public void end(HTML.Tag t) 1389 throws NotImplementedException 1390 { 1391 // FIXME: Implement. 1392 } 1393 } 1394 1395 public HTMLReader(int offset) 1396 { 1397 this (offset, 0, 0, null); 1398 } 1399 1400 public HTMLReader(int offset, int popDepth, int pushDepth, 1401 HTML.Tag insertTag) 1402 { 1403 this.insertTag = insertTag; 1404 this.offset = offset; 1405 this.popDepth = popDepth; 1406 this.pushDepth = pushDepth; 1407 threshold = getTokenThreshold(); 1408 initTags(); 1409 } 1410 1411 void initTags() 1412 { 1413 tagToAction = new HashMap(72); 1414 CharacterAction characterAction = new CharacterAction(); 1415 HiddenAction hiddenAction = new HiddenAction(); 1416 AreaAction areaAction = new AreaAction(); 1417 BaseAction baseAction = new BaseAction(); 1418 BlockAction blockAction = new BlockAction(); 1419 SpecialAction specialAction = new SpecialAction(); 1420 ParagraphAction paragraphAction = new ParagraphAction(); 1421 HeadAction headAction = new HeadAction(); 1422 FormAction formAction = new FormAction(); 1423 IsindexAction isindexAction = new IsindexAction(); 1424 LinkAction linkAction = new LinkAction(); 1425 MapAction mapAction = new MapAction(); 1426 PreAction preAction = new PreAction(); 1427 MetaAction metaAction = new MetaAction(); 1428 StyleAction styleAction = new StyleAction(); 1429 TitleAction titleAction = new TitleAction(); 1430 1431 ConvertAction convertAction = new ConvertAction(); 1432 tagToAction.put(HTML.Tag.A, characterAction); 1433 tagToAction.put(HTML.Tag.ADDRESS, characterAction); 1434 tagToAction.put(HTML.Tag.APPLET, hiddenAction); 1435 tagToAction.put(HTML.Tag.AREA, areaAction); 1436 tagToAction.put(HTML.Tag.B, characterAction); 1437 tagToAction.put(HTML.Tag.BASE, baseAction); 1438 tagToAction.put(HTML.Tag.BASEFONT, characterAction); 1439 tagToAction.put(HTML.Tag.BIG, characterAction); 1440 tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction); 1441 tagToAction.put(HTML.Tag.BODY, blockAction); 1442 tagToAction.put(HTML.Tag.BR, specialAction); 1443 tagToAction.put(HTML.Tag.CAPTION, blockAction); 1444 tagToAction.put(HTML.Tag.CENTER, blockAction); 1445 tagToAction.put(HTML.Tag.CITE, characterAction); 1446 tagToAction.put(HTML.Tag.CODE, characterAction); 1447 tagToAction.put(HTML.Tag.DD, blockAction); 1448 tagToAction.put(HTML.Tag.DFN, characterAction); 1449 tagToAction.put(HTML.Tag.DIR, blockAction); 1450 tagToAction.put(HTML.Tag.DIV, blockAction); 1451 tagToAction.put(HTML.Tag.DL, blockAction); 1452 tagToAction.put(HTML.Tag.DT, paragraphAction); 1453 tagToAction.put(HTML.Tag.EM, characterAction); 1454 tagToAction.put(HTML.Tag.FONT, convertAction); 1455 tagToAction.put(HTML.Tag.FORM, new FormTagAction()); 1456 tagToAction.put(HTML.Tag.FRAME, specialAction); 1457 tagToAction.put(HTML.Tag.FRAMESET, blockAction); 1458 tagToAction.put(HTML.Tag.H1, paragraphAction); 1459 tagToAction.put(HTML.Tag.H2, paragraphAction); 1460 tagToAction.put(HTML.Tag.H3, paragraphAction); 1461 tagToAction.put(HTML.Tag.H4, paragraphAction); 1462 tagToAction.put(HTML.Tag.H5, paragraphAction); 1463 tagToAction.put(HTML.Tag.H6, paragraphAction); 1464 tagToAction.put(HTML.Tag.HEAD, headAction); 1465 tagToAction.put(HTML.Tag.HR, specialAction); 1466 tagToAction.put(HTML.Tag.HTML, blockAction); 1467 tagToAction.put(HTML.Tag.I, characterAction); 1468 tagToAction.put(HTML.Tag.IMG, specialAction); 1469 tagToAction.put(HTML.Tag.INPUT, formAction); 1470 tagToAction.put(HTML.Tag.ISINDEX, isindexAction); 1471 tagToAction.put(HTML.Tag.KBD, characterAction); 1472 tagToAction.put(HTML.Tag.LI, blockAction); 1473 tagToAction.put(HTML.Tag.LINK, linkAction); 1474 tagToAction.put(HTML.Tag.MAP, mapAction); 1475 tagToAction.put(HTML.Tag.MENU, blockAction); 1476 tagToAction.put(HTML.Tag.META, metaAction); 1477 tagToAction.put(HTML.Tag.NOFRAMES, blockAction); 1478 tagToAction.put(HTML.Tag.OBJECT, specialAction); 1479 tagToAction.put(HTML.Tag.OL, blockAction); 1480 tagToAction.put(HTML.Tag.OPTION, formAction); 1481 tagToAction.put(HTML.Tag.P, paragraphAction); 1482 tagToAction.put(HTML.Tag.PARAM, hiddenAction); 1483 tagToAction.put(HTML.Tag.PRE, preAction); 1484 tagToAction.put(HTML.Tag.SAMP, characterAction); 1485 tagToAction.put(HTML.Tag.SCRIPT, hiddenAction); 1486 tagToAction.put(HTML.Tag.SELECT, formAction); 1487 tagToAction.put(HTML.Tag.SMALL, characterAction); 1488 tagToAction.put(HTML.Tag.STRIKE, characterAction); 1489 tagToAction.put(HTML.Tag.S, characterAction); 1490 tagToAction.put(HTML.Tag.STRONG, characterAction); 1491 tagToAction.put(HTML.Tag.STYLE, styleAction); 1492 tagToAction.put(HTML.Tag.SUB, characterAction); 1493 tagToAction.put(HTML.Tag.SUP, characterAction); 1494 tagToAction.put(HTML.Tag.TABLE, blockAction); 1495 tagToAction.put(HTML.Tag.TD, blockAction); 1496 tagToAction.put(HTML.Tag.TEXTAREA, formAction); 1497 tagToAction.put(HTML.Tag.TH, blockAction); 1498 tagToAction.put(HTML.Tag.TITLE, titleAction); 1499 tagToAction.put(HTML.Tag.TR, blockAction); 1500 tagToAction.put(HTML.Tag.TT, characterAction); 1501 tagToAction.put(HTML.Tag.U, characterAction); 1502 tagToAction.put(HTML.Tag.UL, blockAction); 1503 tagToAction.put(HTML.Tag.VAR, characterAction); 1504 } 1505 1506 /** 1507 * Pushes the current character style onto the stack. 1508 * 1509 */ 1510 protected void pushCharacterStyle() 1511 { 1512 charAttrStack.push(charAttr.copyAttributes()); 1513 } 1514 1515 /** 1516 * Pops a character style off of the stack and uses it as the 1517 * current character style. 1518 * 1519 */ 1520 protected void popCharacterStyle() 1521 { 1522 if (!charAttrStack.isEmpty()) 1523 charAttr = (MutableAttributeSet) charAttrStack.pop(); 1524 } 1525 1526 /** 1527 * Registers a given tag with a given Action. All of the well-known tags 1528 * are registered by default, but this method can change their behaviour 1529 * or add support for custom or currently unsupported tags. 1530 * 1531 * @param t the Tag to register 1532 * @param a the Action for the Tag 1533 */ 1534 protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a) 1535 { 1536 tagToAction.put (t, a); 1537 } 1538 1539 /** 1540 * This is the last method called on the HTMLReader, allowing any pending 1541 * changes to be flushed to the HTMLDocument. 1542 */ 1543 public void flush() throws BadLocationException 1544 { 1545 flushImpl(); 1546 } 1547 1548 /** 1549 * Flushes the buffer and handle partial inserts. 1550 * 1551 */ 1552 private void flushImpl() 1553 throws BadLocationException 1554 { 1555 int oldLen = getLength(); 1556 int size = parseBuffer.size(); 1557 ElementSpec[] elems = new ElementSpec[size]; 1558 parseBuffer.copyInto(elems); 1559 if (oldLen == 0) 1560 create(elems); 1561 else 1562 insert(offset, elems); 1563 parseBuffer.removeAllElements(); 1564 offset += getLength() - oldLen; 1565 } 1566 1567 /** 1568 * This method is called by the parser to indicate a block of 1569 * text was encountered. Should insert the text appropriately. 1570 * 1571 * @param data the text that was inserted 1572 * @param pos the position at which the text was inserted 1573 */ 1574 public void handleText(char[] data, int pos) 1575 { 1576 if (shouldInsert() && data != null && data.length > 0) 1577 { 1578 if (inTextArea) 1579 textAreaContent(data); 1580 else if (inPreTag) 1581 preContent(data); 1582 else if (option != null) 1583 option.setLabel(new String(data)); 1584 else if (inStyleTag) 1585 { 1586 if (styles == null) 1587 styles = new ArrayList(); 1588 styles.add(new String(data)); 1589 } 1590 else 1591 addContent(data, 0, data.length); 1592 1593 } 1594 } 1595 1596 /** 1597 * Checks if the HTML tag should be inserted. The tags before insert tag (if 1598 * specified) are not inserted. Also, the tags after the end of the html are 1599 * not inserted. 1600 * 1601 * @return true if the tag should be inserted, false otherwise. 1602 */ 1603 private boolean shouldInsert() 1604 { 1605 return ! endHTMLEncountered 1606 && (insertTagEncountered || insertTag == null); 1607 } 1608 1609 /** 1610 * This method is called by the parser and should route the call to the 1611 * proper handler for the tag. 1612 * 1613 * @param t the HTML.Tag 1614 * @param a the attribute set 1615 * @param pos the position at which the tag was encountered 1616 */ 1617 public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) 1618 { 1619 if (t == insertTag) 1620 insertTagEncountered = true; 1621 1622 if (shouldInsert()) 1623 { 1624 TagAction action = (TagAction) tagToAction.get(t); 1625 if (action != null) 1626 action.start(t, a); 1627 } 1628 } 1629 1630 /** 1631 * This method called by parser to handle a comment block. 1632 * 1633 * @param data the comment 1634 * @param pos the position at which the comment was encountered 1635 */ 1636 public void handleComment(char[] data, int pos) 1637 { 1638 if (shouldInsert()) 1639 { 1640 TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); 1641 if (action != null) 1642 { 1643 action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); 1644 action.end(HTML.Tag.COMMENT); 1645 } 1646 } 1647 } 1648 1649 /** 1650 * This method is called by the parser and should route the call to the 1651 * proper handler for the tag. 1652 * 1653 * @param t the HTML.Tag 1654 * @param pos the position at which the tag was encountered 1655 */ 1656 public void handleEndTag(HTML.Tag t, int pos) 1657 { 1658 if (shouldInsert()) 1659 { 1660 // If this is the </html> tag we need to stop calling the Actions 1661 if (t == HTML.Tag.HTML) 1662 endHTMLEncountered = true; 1663 1664 TagAction action = (TagAction) tagToAction.get(t); 1665 if (action != null) 1666 action.end(t); 1667 } 1668 } 1669 1670 /** 1671 * This is a callback from the parser that should be routed to the 1672 * appropriate handler for the tag. 1673 * 1674 * @param t the HTML.Tag that was encountered 1675 * @param a the attribute set 1676 * @param pos the position at which the tag was encountered 1677 */ 1678 public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) 1679 { 1680 if (t == insertTag) 1681 insertTagEncountered = true; 1682 1683 if (shouldInsert()) 1684 { 1685 TagAction action = (TagAction) tagToAction.get(t); 1686 if (action != null) 1687 { 1688 action.start(t, a); 1689 action.end(t); 1690 } 1691 } 1692 } 1693 1694 /** 1695 * This is invoked after the stream has been parsed but before it has been 1696 * flushed. 1697 * 1698 * @param eol one of \n, \r, or \r\n, whichever was encountered the most in 1699 * parsing the stream 1700 * @since 1.3 1701 */ 1702 public void handleEndOfLineString(String eol) 1703 { 1704 // FIXME: Implement. 1705 } 1706 1707 /** 1708 * Adds the given text to the textarea document. Called only when we are 1709 * within a textarea. 1710 * 1711 * @param data the text to add to the textarea 1712 */ 1713 protected void textAreaContent(char[] data) 1714 { 1715 try 1716 { 1717 int offset = textAreaDocument.getLength(); 1718 String text = new String(data); 1719 textAreaDocument.setInitialText(text); 1720 textAreaDocument.insertString(offset, text, null); 1721 } 1722 catch (BadLocationException ex) 1723 { 1724 // Must not happen as we insert at a model location that we 1725 // got from the document itself. 1726 assert false; 1727 } 1728 } 1729 1730 /** 1731 * Adds the given text that was encountered in a <PRE> element. 1732 * This adds synthesized lines to hold the text runs. 1733 * 1734 * @param data the text 1735 */ 1736 protected void preContent(char[] data) 1737 { 1738 int start = 0; 1739 for (int i = 0; i < data.length; i++) 1740 { 1741 if (data[i] == '\n') 1742 { 1743 addContent(data, start, i - start + 1); 1744 blockClose(HTML.Tag.IMPLIED); 1745 MutableAttributeSet atts = new SimpleAttributeSet(); 1746 atts.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); 1747 blockOpen(HTML.Tag.IMPLIED, atts); 1748 start = i + 1; 1749 } 1750 } 1751 if (start < data.length) 1752 { 1753 // Add remaining last line. 1754 addContent(data, start, data.length - start); 1755 } 1756 } 1757 1758 /** 1759 * Instructs the parse buffer to create a block element with the given 1760 * attributes. 1761 * 1762 * @param t the tag that requires opening a new block 1763 * @param attr the attribute set for the new block 1764 */ 1765 protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) 1766 { 1767 if (inImpliedParagraph()) 1768 blockClose(HTML.Tag.IMPLIED); 1769 1770 // Push the new tag on top of the stack. 1771 parseStack.push(t); 1772 1773 DefaultStyledDocument.ElementSpec element; 1774 1775 AbstractDocument.AttributeContext ctx = getAttributeContext(); 1776 AttributeSet copy = attr.copyAttributes(); 1777 copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t); 1778 element = new DefaultStyledDocument.ElementSpec(copy, 1779 DefaultStyledDocument.ElementSpec.StartTagType); 1780 parseBuffer.addElement(element); 1781 } 1782 1783 /** 1784 * Returns true when we are currently inside a paragraph, either 1785 * a real one or an implied, false otherwise. 1786 * 1787 * @return 1788 */ 1789 private boolean inParagraph() 1790 { 1791 boolean inParagraph = false; 1792 if (! parseStack.isEmpty()) 1793 { 1794 HTML.Tag top = parseStack.peek(); 1795 inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED; 1796 } 1797 return inParagraph; 1798 } 1799 1800 private boolean inImpliedParagraph() 1801 { 1802 boolean inParagraph = false; 1803 if (! parseStack.isEmpty()) 1804 { 1805 HTML.Tag top = parseStack.peek(); 1806 inParagraph = top == HTML.Tag.IMPLIED; 1807 } 1808 return inParagraph; 1809 } 1810 1811 /** 1812 * Instructs the parse buffer to close the block element associated with 1813 * the given HTML.Tag 1814 * 1815 * @param t the HTML.Tag that is closing its block 1816 */ 1817 protected void blockClose(HTML.Tag t) 1818 { 1819 DefaultStyledDocument.ElementSpec element; 1820 1821 if (inImpliedParagraph() && t != HTML.Tag.IMPLIED) 1822 blockClose(HTML.Tag.IMPLIED); 1823 1824 // Pull the token from the stack. 1825 if (! parseStack.isEmpty()) // Just to be sure. 1826 parseStack.pop(); 1827 1828 // If the previous tag is a start tag then we insert a synthetic 1829 // content tag. 1830 DefaultStyledDocument.ElementSpec prev; 1831 prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec) 1832 parseBuffer.get(parseBuffer.size() - 1) : null; 1833 if (prev != null && 1834 prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType) 1835 { 1836 addContent(new char[]{' '}, 0, 1); 1837 } 1838 1839 element = new DefaultStyledDocument.ElementSpec(null, 1840 DefaultStyledDocument.ElementSpec.EndTagType); 1841 parseBuffer.addElement(element); 1842 } 1843 1844 /** 1845 * Adds text to the appropriate context using the current character 1846 * attribute set. 1847 * 1848 * @param data the text to add 1849 * @param offs the offset at which to add it 1850 * @param length the length of the text to add 1851 */ 1852 protected void addContent(char[] data, int offs, int length) 1853 { 1854 addContent(data, offs, length, true); 1855 } 1856 1857 /** 1858 * Adds text to the appropriate context using the current character 1859 * attribute set, and possibly generating an IMPLIED Tag if necessary. 1860 * 1861 * @param data the text to add 1862 * @param offs the offset at which to add it 1863 * @param length the length of the text to add 1864 * @param generateImpliedPIfNecessary whether or not we should generate 1865 * an HTML.Tag.IMPLIED tag if necessary 1866 */ 1867 protected void addContent(char[] data, int offs, int length, 1868 boolean generateImpliedPIfNecessary) 1869 { 1870 if (generateImpliedPIfNecessary && ! inParagraph()) 1871 { 1872 blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); 1873 } 1874 1875 AbstractDocument.AttributeContext ctx = getAttributeContext(); 1876 DefaultStyledDocument.ElementSpec element; 1877 AttributeSet attributes = null; 1878 1879 // Copy the attribute set, don't use the same object because 1880 // it may change 1881 if (charAttr != null) 1882 attributes = charAttr.copyAttributes(); 1883 else 1884 attributes = ctx.getEmptySet(); 1885 attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute, 1886 HTML.Tag.CONTENT); 1887 element = new DefaultStyledDocument.ElementSpec(attributes, 1888 DefaultStyledDocument.ElementSpec.ContentType, 1889 data, offs, length); 1890 1891 // Add the element to the buffer 1892 parseBuffer.addElement(element); 1893 1894 if (parseBuffer.size() > threshold) 1895 { 1896 if (threshold <= MAX_THRESHOLD) 1897 threshold *= GROW_THRESHOLD; 1898 try 1899 { 1900 flushImpl(); 1901 } 1902 catch (BadLocationException ble) 1903 { 1904 // TODO: what to do here? 1905 } 1906 } 1907 } 1908 1909 /** 1910 * Adds content that is specified in the attribute set. 1911 * 1912 * @param t the HTML.Tag 1913 * @param a the attribute set specifying the special content 1914 */ 1915 protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a) 1916 { 1917 if (t != HTML.Tag.FRAME && ! inParagraph()) 1918 { 1919 blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); 1920 } 1921 1922 a.addAttribute(StyleConstants.NameAttribute, t); 1923 1924 // The two spaces are required because some special elements like HR 1925 // must be broken. At least two characters are needed to break into the 1926 // two parts. 1927 DefaultStyledDocument.ElementSpec spec = 1928 new DefaultStyledDocument.ElementSpec(a.copyAttributes(), 1929 DefaultStyledDocument.ElementSpec.ContentType, 1930 new char[] {' '}, 0, 1 ); 1931 parseBuffer.add(spec); 1932 } 1933 1934 } 1935 1936 /** 1937 * Gets the reader for the parser to use when loading the document with HTML. 1938 * 1939 * @param pos - the starting position 1940 * @return - the reader 1941 */ 1942 public HTMLEditorKit.ParserCallback getReader(int pos) 1943 { 1944 return new HTMLReader(pos); 1945 } 1946 1947 /** 1948 * Gets the reader for the parser to use when loading the document with HTML. 1949 * 1950 * @param pos - the starting position 1951 * @param popDepth - the number of EndTagTypes to generate before inserting 1952 * @param pushDepth - the number of StartTagTypes with a direction 1953 * of JoinNextDirection that should be generated before inserting, 1954 * but after the end tags have been generated. 1955 * @param insertTag - the first tag to start inserting into document 1956 * @return - the reader 1957 */ 1958 public HTMLEditorKit.ParserCallback getReader(int pos, 1959 int popDepth, 1960 int pushDepth, 1961 HTML.Tag insertTag) 1962 { 1963 return new HTMLReader(pos, popDepth, pushDepth, insertTag); 1964 } 1965 1966 /** 1967 * Gets the reader for the parser to use when inserting the HTML fragment into 1968 * the document. Checks if the parser is present, sets the parent in the 1969 * element stack and removes any actions for BODY (it can be only one body in 1970 * a HTMLDocument). 1971 * 1972 * @param pos - the starting position 1973 * @param popDepth - the number of EndTagTypes to generate before inserting 1974 * @param pushDepth - the number of StartTagTypes with a direction of 1975 * JoinNextDirection that should be generated before inserting, but 1976 * after the end tags have been generated. 1977 * @param insertTag - the first tag to start inserting into document 1978 * @param parent the element that will be the parent in the document. HTML 1979 * parsing includes checks for the parent, so it must be available. 1980 * @return - the reader 1981 * @throws IllegalStateException if the parsert is not set. 1982 */ 1983 public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth, 1984 int pushDepth, 1985 HTML.Tag insertTag, 1986 final Element parent) 1987 throws IllegalStateException 1988 { 1989 if (parser == null) 1990 throw new IllegalStateException("Parser has not been set"); 1991 1992 HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag) 1993 { 1994 /** 1995 * Ignore BODY. 1996 */ 1997 public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) 1998 { 1999 if (t != HTML.Tag.BODY) 2000 super.handleStartTag(t, a, pos); 2001 } 2002 2003 /** 2004 * Ignore BODY. 2005 */ 2006 public void handleEndTag(HTML.Tag t, int pos) 2007 { 2008 if (t != HTML.Tag.BODY) 2009 super.handleEndTag(t, pos); 2010 } 2011 }; 2012 2013 return reader; 2014 } 2015 2016 /** 2017 * Gets the child element that contains the attribute with the value or null. 2018 * Not thread-safe. 2019 * 2020 * @param e - the element to begin search at 2021 * @param attribute - the desired attribute 2022 * @param value - the desired value 2023 * @return the element found with the attribute and value specified or null if 2024 * it is not found. 2025 */ 2026 public Element getElement(Element e, Object attribute, Object value) 2027 { 2028 if (e != null) 2029 { 2030 if (e.getAttributes().containsAttribute(attribute, value)) 2031 return e; 2032 2033 int count = e.getElementCount(); 2034 for (int j = 0; j < count; j++) 2035 { 2036 Element child = e.getElement(j); 2037 if (child.getAttributes().containsAttribute(attribute, value)) 2038 return child; 2039 2040 Element grandChild = getElement(child, attribute, value); 2041 if (grandChild != null) 2042 return grandChild; 2043 } 2044 } 2045 return null; 2046 } 2047 2048 /** 2049 * Returns the element that has the given id Attribute (for instance, <p id 2050 * ='my paragraph >'). If it is not found, null is returned. The HTML tag, 2051 * having this attribute, is not checked by this method and can be any. The 2052 * method is not thread-safe. 2053 * 2054 * @param attrId - the value of the attribute id to look for 2055 * @return the element that has the given id. 2056 */ 2057 public Element getElement(String attrId) 2058 { 2059 return getElement(getDefaultRootElement(), HTML.Attribute.ID, 2060 attrId); 2061 } 2062 2063 /** 2064 * Replaces the children of the given element with the contents of 2065 * the string. The document must have an HTMLEditorKit.Parser set. 2066 * This will be seen as at least two events, n inserts followed by a remove. 2067 * 2068 * @param elem - the brance element whose children will be replaced 2069 * @param htmlText - the string to be parsed and assigned to element. 2070 * @throws BadLocationException 2071 * @throws IOException 2072 * @throws IllegalArgumentException - if elem is a leaf 2073 * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set 2074 */ 2075 public void setInnerHTML(Element elem, String htmlText) 2076 throws BadLocationException, IOException 2077 { 2078 if (elem.isLeaf()) 2079 throw new IllegalArgumentException("Element is a leaf"); 2080 2081 int start = elem.getStartOffset(); 2082 int end = elem.getEndOffset(); 2083 2084 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2085 end, 0, 0, HTML.Tag.BODY, elem); 2086 2087 // TODO charset 2088 getParser().parse(new StringReader(htmlText), reader, true); 2089 2090 // Remove the previous content 2091 remove(start, end - start); 2092 } 2093 2094 /** 2095 * Replaces the given element in the parent with the string. When replacing a 2096 * leaf, this will attempt to make sure there is a newline present if one is 2097 * needed. This may result in an additional element being inserted. This will 2098 * be seen as at least two events, n inserts followed by a remove. The 2099 * HTMLEditorKit.Parser must be set. 2100 * 2101 * @param elem - the branch element whose parent will be replaced 2102 * @param htmlText - the string to be parsed and assigned to elem 2103 * @throws BadLocationException 2104 * @throws IOException 2105 * @throws IllegalStateException - if parser is not set 2106 */ 2107 public void setOuterHTML(Element elem, String htmlText) 2108 throws BadLocationException, IOException 2109 { 2110 // Remove the current element: 2111 int start = elem.getStartOffset(); 2112 int end = elem.getEndOffset(); 2113 2114 remove(start, end-start); 2115 2116 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2117 start, 0, 0, HTML.Tag.BODY, elem); 2118 2119 // TODO charset 2120 getParser().parse(new StringReader(htmlText), reader, true); 2121 } 2122 2123 /** 2124 * Inserts the string before the start of the given element. The parser must 2125 * be set. 2126 * 2127 * @param elem - the element to be the root for the new text. 2128 * @param htmlText - the string to be parsed and assigned to elem 2129 * @throws BadLocationException 2130 * @throws IOException 2131 * @throws IllegalStateException - if parser has not been set 2132 */ 2133 public void insertBeforeStart(Element elem, String htmlText) 2134 throws BadLocationException, IOException 2135 { 2136 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2137 elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); 2138 2139 // TODO charset 2140 getParser().parse(new StringReader(htmlText), reader, true); 2141 } 2142 2143 /** 2144 * Inserts the string at the end of the element. If elem's children are 2145 * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it 2146 * will be inserted before the newline. The parser must be set. 2147 * 2148 * @param elem - the element to be the root for the new text 2149 * @param htmlText - the text to insert 2150 * @throws BadLocationException 2151 * @throws IOException 2152 * @throws IllegalStateException - if parser is not set 2153 */ 2154 public void insertBeforeEnd(Element elem, String htmlText) 2155 throws BadLocationException, IOException 2156 { 2157 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2158 elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); 2159 2160 // TODO charset 2161 getParser().parse(new StringReader(htmlText), reader, true); 2162 2163 } 2164 2165 /** 2166 * Inserts the string after the end of the given element. 2167 * The parser must be set. 2168 * 2169 * @param elem - the element to be the root for the new text 2170 * @param htmlText - the text to insert 2171 * @throws BadLocationException 2172 * @throws IOException 2173 * @throws IllegalStateException - if parser is not set 2174 */ 2175 public void insertAfterEnd(Element elem, String htmlText) 2176 throws BadLocationException, IOException 2177 { 2178 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2179 elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); 2180 2181 // TODO charset 2182 getParser().parse(new StringReader(htmlText), reader, true); 2183 } 2184 2185 /** 2186 * Inserts the string at the start of the element. 2187 * The parser must be set. 2188 * 2189 * @param elem - the element to be the root for the new text 2190 * @param htmlText - the text to insert 2191 * @throws BadLocationException 2192 * @throws IOException 2193 * @throws IllegalStateException - if parser is not set 2194 */ 2195 public void insertAfterStart(Element elem, String htmlText) 2196 throws BadLocationException, IOException 2197 { 2198 HTMLEditorKit.ParserCallback reader = getInsertingReader( 2199 elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); 2200 2201 // TODO charset 2202 getParser().parse(new StringReader(htmlText), reader, true); 2203 } 2204 2205 /** 2206 * Overridden to tag content with the synthetic HTML.Tag.CONTENT 2207 * tag. 2208 */ 2209 protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att) 2210 { 2211 if (att == null) 2212 { 2213 SimpleAttributeSet sas = new SimpleAttributeSet(); 2214 sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); 2215 att = sas; 2216 } 2217 super.insertUpdate(evt, att); 2218 } 2219 2220 /** 2221 * Returns <code>true</code> when this document is inside a frame, 2222 * <code>false</code> otherwise. 2223 * 2224 * @return <code>true</code> when this document is inside a frame, 2225 * <code>false</code> otherwise 2226 */ 2227 boolean isFrameDocument() 2228 { 2229 return frameDocument; 2230 } 2231 2232 /** 2233 * Set <code>true</code> when this document is inside a frame, 2234 * <code>false</code> otherwise. 2235 * 2236 * @param frameDoc <code>true</code> when this document is inside a frame, 2237 * <code>false</code> otherwise 2238 */ 2239 void setFrameDocument(boolean frameDoc) 2240 { 2241 frameDocument = frameDoc; 2242 } 2243 2244 /** 2245 * Returns the target that is specified in the base tag, if this is the case. 2246 * 2247 * @return the target that is specified in the base tag, if this is the case 2248 */ 2249 String getBaseTarget() 2250 { 2251 return baseTarget; 2252 } 2253 2254 /** 2255 * Updates the A tag's pseudo class value in response to a hyperlink 2256 * action. 2257 * 2258 * @param el the corresponding element 2259 * @param value the new value 2260 */ 2261 void updateSpecialClass(Element el, HTML.Attribute cl, String value) 2262 { 2263 try 2264 { 2265 writeLock(); 2266 DefaultDocumentEvent ev = 2267 new DefaultDocumentEvent(el.getStartOffset(), 1, 2268 DocumentEvent.EventType.CHANGE); 2269 AttributeSet elAtts = el.getAttributes(); 2270 AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A); 2271 if (anchorAtts != null) 2272 { 2273 AttributeSet copy = elAtts.copyAttributes(); 2274 StyleSheet ss = getStyleSheet(); 2275 if (value != null) 2276 { 2277 anchorAtts = ss.addAttribute(anchorAtts, cl, value); 2278 } 2279 else 2280 { 2281 anchorAtts = ss.removeAttribute(anchorAtts, cl); 2282 } 2283 MutableAttributeSet matts = (MutableAttributeSet) elAtts; 2284 ev.addEdit(new AttributeUndoableEdit(el, copy, false)); 2285 matts.removeAttribute(HTML.Tag.A); 2286 matts.addAttribute(HTML.Tag.A, anchorAtts); 2287 ev.end(); 2288 fireChangedUpdate(ev); 2289 fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); 2290 } 2291 } 2292 finally 2293 { 2294 writeUnlock(); 2295 } 2296 } 2297 2298 }