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 (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 &lt;isindex&gt; 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 &lt;pre&gt; 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, &lt;p id
2050:    * ='my paragraph &gt;'). 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: }