Source for javax.swing.text.html.HTMLDocument

   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 &lt;textarea&gt; 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: &lt;input&gt;, &lt;textarea&gt;,
 835:      * &lt;select&gt; and &lt;option&gt;.
 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 (