| GNU Classpath (0.95) | |
| Frames | No Frames |
1: /* HTMLDocument.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: import gnu.classpath.NotImplementedException; 42: 43: import java.io.IOException; 44: import java.io.StringReader; 45: import java.net.MalformedURLException; 46: import java.net.URL; 47: import java.util.ArrayList; 48: import java.util.HashMap; 49: import java.util.Stack; 50: import java.util.Vector; 51: 52: import javax.swing.ButtonGroup; 53: import javax.swing.DefaultButtonModel; 54: import javax.swing.JEditorPane; 55: import javax.swing.ListSelectionModel; 56: import javax.swing.event.DocumentEvent; 57: import javax.swing.event.UndoableEditEvent; 58: import javax.swing.text.AbstractDocument; 59: import javax.swing.text.AttributeSet; 60: import javax.swing.text.BadLocationException; 61: import javax.swing.text.DefaultStyledDocument; 62: import javax.swing.text.Element; 63: import javax.swing.text.ElementIterator; 64: import javax.swing.text.GapContent; 65: import javax.swing.text.MutableAttributeSet; 66: import javax.swing.text.PlainDocument; 67: import javax.swing.text.SimpleAttributeSet; 68: import javax.swing.text.StyleConstants; 69: import javax.swing.text.html.HTML.Tag; 70: 71: /** 72: * Represents the HTML document that is constructed by defining the text and 73: * other components (images, buttons, etc) in HTML language. This class can 74: * becomes the default document for {@link JEditorPane} after setting its 75: * content type to "text/html". HTML document also serves as an intermediate 76: * data structure when it is needed to parse HTML and then obtain the content of 77: * the certain types of tags. This class also has methods for modifying the HTML 78: * content. 79: * 80: * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 81: * @author Anthony Balkissoon (abalkiss@redhat.com) 82: * @author Lillian Angel (langel@redhat.com) 83: */ 84: public class HTMLDocument extends DefaultStyledDocument 85: { 86: /** A key for document properies. The value for the key is 87: * a Vector of Strings of comments not found in the body. 88: */ 89: public static final String AdditionalComments = "AdditionalComments"; 90: URL baseURL = null; 91: boolean preservesUnknownTags = true; 92: int tokenThreshold = Integer.MAX_VALUE; 93: HTMLEditorKit.Parser parser; 94: 95: /** 96: * Indicates whether this document is inside a frame or not. 97: */ 98: private boolean frameDocument; 99: 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 (