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: import gnu.javax.swing.text.html.CharacterAttributeTranslator;
  43: import gnu.javax.swing.text.html.parser.htmlAttributeSet;
  44: 
  45: import java.io.IOException;
  46: import java.io.StringReader;
  47: import java.net.URL;
  48: import java.util.HashMap;
  49: import java.util.Stack;
  50: import java.util.Vector;
  51: 
  52: import javax.swing.JEditorPane;
  53: import javax.swing.event.DocumentEvent;
  54: import javax.swing.event.HyperlinkEvent.EventType;
  55: import javax.swing.text.AbstractDocument;
  56: import javax.swing.text.AttributeSet;
  57: import javax.swing.text.BadLocationException;
  58: import javax.swing.text.DefaultStyledDocument;
  59: import javax.swing.text.Element;
  60: import javax.swing.text.ElementIterator;
  61: import javax.swing.text.GapContent;
  62: import javax.swing.text.MutableAttributeSet;
  63: import javax.swing.text.SimpleAttributeSet;
  64: import javax.swing.text.StyleConstants;
  65: import javax.swing.text.html.HTML.Tag;
  66: 
  67: /**
  68:  * Represents the HTML document that is constructed by defining the text and
  69:  * other components (images, buttons, etc) in HTML language. This class can
  70:  * becomes the default document for {@link JEditorPane} after setting its
  71:  * content type to "text/html". HTML document also serves as an intermediate
  72:  * data structure when it is needed to parse HTML and then obtain the content of
  73:  * the certain types of tags. This class also has methods for modifying the HTML
  74:  * content.
  75:  * 
  76:  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  77:  * @author Anthony Balkissoon (abalkiss@redhat.com)
  78:  * @author Lillian Angel (langel@redhat.com)
  79:  */
  80: public class HTMLDocument extends DefaultStyledDocument
  81: {
  82:   /** A key for document properies.  The value for the key is
  83:    * a Vector of Strings of comments not found in the body.
  84:    */  
  85:   public static final String AdditionalComments = "AdditionalComments";
  86:   URL baseURL = null;
  87:   boolean preservesUnknownTags = true;
  88:   int tokenThreshold = Integer.MAX_VALUE;
  89:   HTMLEditorKit.Parser parser;
  90:   StyleSheet styleSheet;
  91:   AbstractDocument.Content content;
  92:   
  93:   /**
  94:    * Constructs an HTML document using the default buffer size and a default
  95:    * StyleSheet.
  96:    */
  97:   public HTMLDocument()
  98:   {
  99:     this(null);
 100:   }
 101:   
 102:   /**
 103:    * Constructs an HTML document with the default content storage 
 104:    * implementation and the specified style/attribute storage mechanism.
 105:    * 
 106:    * @param styles - the style sheet
 107:    */
 108:   public HTMLDocument(StyleSheet styles)
 109:   {
 110:    this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
 111:   }
 112:   
 113:   /**
 114:    * Constructs an HTML document with the given content storage implementation 
 115:    * and the given style/attribute storage mechanism.
 116:    * 
 117:    * @param c - the document's content
 118:    * @param styles - the style sheet
 119:    */
 120:   public HTMLDocument(AbstractDocument.Content c, StyleSheet styles)
 121:   {
 122:     this.content = c;
 123:     if (styles == null)
 124:       {
 125:         styles = new StyleSheet();
 126:         styles.importStyleSheet(getClass().getResource(HTMLEditorKit.
 127:                                                        DEFAULT_CSS));
 128:       }
 129:     this.styleSheet = styles;
 130:   }
 131:   
 132:   /**
 133:    * Gets the style sheet with the document display rules (CSS) that were specified 
 134:    * in the HTML document.
 135:    * 
 136:    * @return - the style sheet
 137:    */
 138:   public StyleSheet getStyleSheet()
 139:   {
 140:     return styleSheet;
 141:   }
 142:   
 143:   /**
 144:    * This method creates a root element for the new document.
 145:    * 
 146:    * @return the new default root
 147:    */
 148:   protected AbstractElement createDefaultRoot()
 149:   {
 150:     AbstractDocument.AttributeContext ctx = getAttributeContext();
 151: 
 152:     // Create html element.
 153:     AttributeSet atts = ctx.getEmptySet();
 154:     atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.HTML);
 155:     BranchElement html = (BranchElement) createBranchElement(null, atts);
 156: 
 157:     // Create body element.
 158:     atts = ctx.getEmptySet();
 159:     atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.BODY);
 160:     BranchElement body = (BranchElement) createBranchElement(html, atts);
 161:     html.replace(0, 0, new Element[] { body });
 162: 
 163:     // Create p element.
 164:     atts = ctx.getEmptySet();
 165:     atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.P);
 166:     BranchElement p = (BranchElement) createBranchElement(body, atts);
 167:     body.replace(0, 0, new Element[] { p });
 168: 
 169:     // Create an empty leaf element.
 170:     atts = ctx.getEmptySet();
 171:     atts = ctx.addAttribute(atts, StyleConstants.NameAttribute,
 172:                             HTML.Tag.CONTENT);
 173:     Element leaf = createLeafElement(p, atts, 0, 1);
 174:     p.replace(0, 0, new Element[]{ leaf });
 175: 
 176:     return html;
 177:   }
 178:   
 179:   /**
 180:    * This method returns an HTMLDocument.RunElement object attached to
 181:    * parent representing a run of text from p0 to p1. The run has 
 182:    * attributes described by a.
 183:    * 
 184:    * @param parent - the parent element
 185:    * @param a - the attributes for the element
 186:    * @param p0 - the beginning of the range >= 0
 187:    * @param p1 - the end of the range >= p0
 188:    *
 189:    * @return the new element
 190:    */
 191:   protected Element createLeafElement(Element parent, AttributeSet a, int p0,
 192:                                       int p1)
 193:   {
 194:     RunElement el = new RunElement(parent, a, p0, p1);
 195:     el.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
 196:     return new RunElement(parent, a, p0, p1);
 197:   }
 198: 
 199:   /**
 200:    * This method returns an HTMLDocument.BlockElement object representing the
 201:    * attribute set a and attached to parent.
 202:    * 
 203:    * @param parent - the parent element
 204:    * @param a - the attributes for the element
 205:    *
 206:    * @return the new element
 207:    */
 208:   protected Element createBranchElement(Element parent, AttributeSet a)
 209:   {
 210:     return new BlockElement(parent, a);
 211:   }
 212:   
 213:   /**
 214:    * Returns the parser used by this HTMLDocument to insert HTML.
 215:    * 
 216:    * @return the parser used by this HTMLDocument to insert HTML.
 217:    */
 218:   public HTMLEditorKit.Parser getParser()
 219:   {
 220:     return parser; 
 221:   }
 222:   
 223:   /**
 224:    * Sets the parser used by this HTMLDocument to insert HTML.
 225:    * 
 226:    * @param p the parser to use
 227:    */
 228:   public void setParser (HTMLEditorKit.Parser p)
 229:   {
 230:     parser = p;
 231:   }
 232:   /**
 233:    * Sets the number of tokens to buffer before trying to display the
 234:    * Document.
 235:    * 
 236:    * @param n the number of tokens to buffer
 237:    */
 238:   public void setTokenThreshold (int n)
 239:   {
 240:     tokenThreshold = n;
 241:   }
 242:   
 243:   /**
 244:    * Returns the number of tokens that are buffered before the document
 245:    * is rendered.
 246:    * 
 247:    * @return the number of tokens buffered
 248:    */
 249:   public int getTokenThreshold ()
 250:   {
 251:     return tokenThreshold;
 252:   }
 253:   
 254:   /**
 255:    * Returns the location against which to resolve relative URLs.
 256:    * This is the document's URL if the document was loaded from a URL.
 257:    * If a <code>base</code> tag is found, it will be used.
 258:    * @return the base URL
 259:    */
 260:   public URL getBase()
 261:   {
 262:     return baseURL;
 263:   }
 264:   
 265:   /**
 266:    * Sets the location against which to resolve relative URLs.
 267:    * @param u the new base URL
 268:    */
 269:   public void setBase(URL u)
 270:   {
 271:     baseURL = u;
 272:     styleSheet.setBase(u);
 273:   }
 274:   
 275:   /**
 276:    * Returns whether or not the parser preserves unknown HTML tags.
 277:    * @return true if the parser preserves unknown tags
 278:    */
 279:   public boolean getPreservesUnknownTags()
 280:   {
 281:     return preservesUnknownTags;
 282:   }
 283:   
 284:   /**
 285:    * Sets the behaviour of the parser when it encounters unknown HTML tags.
 286:    * @param preservesTags true if the parser should preserve unknown tags.
 287:    */
 288:   public void setPreservesUnknownTags(boolean preservesTags)
 289:   {
 290:     preservesUnknownTags = preservesTags;
 291:   }
 292:   
 293:   /**
 294:    * An iterator to iterate through LeafElements in the document.
 295:    */
 296:   class LeafIterator extends Iterator
 297:   {
 298:     HTML.Tag tag;
 299:     HTMLDocument doc;
 300:     ElementIterator it;
 301: 
 302:     public LeafIterator (HTML.Tag t, HTMLDocument d)
 303:     {
 304:       doc = d;
 305:       tag = t;
 306:       it = new ElementIterator(doc);
 307:     }
 308:     
 309:     /**
 310:      * Return the attributes for the tag associated with this iteartor
 311:      * @return the AttributeSet
 312:      */
 313:     public AttributeSet getAttributes()
 314:     {
 315:       if (it.current() != null)
 316:         return it.current().getAttributes();
 317:       return null;
 318:     }
 319: 
 320:     /**
 321:      * Get the end of the range for the current occurrence of the tag
 322:      * being defined and having the same attributes.
 323:      * @return the end of the range
 324:      */
 325:     public int getEndOffset()
 326:     {
 327:       if (it.current() != null)
 328:         return it.current().getEndOffset();
 329:       return -1;
 330:     }
 331: 
 332:     /**
 333:      * Get the start of the range for the current occurrence of the tag
 334:      * being defined and having the same attributes.
 335:      * @return the start of the range (-1 if it can't be found).
 336:      */
 337: 
 338:     public int getStartOffset()
 339:     {
 340:       if (it.current() != null)
 341:         return it.current().getStartOffset();
 342:       return -1;
 343:     }
 344: 
 345:     /**
 346:      * Advance the iterator to the next LeafElement .
 347:      */
 348:     public void next()
 349:     {
 350:       it.next();
 351:       while (it.current()!= null && !it.current().isLeaf())
 352:         it.next();
 353:     }
 354: 
 355:     /**
 356:      * Indicates whether or not the iterator currently represents an occurrence
 357:      * of the tag.
 358:      * @return true if the iterator currently represents an occurrence of the
 359:      * tag.
 360:      */
 361:     public boolean isValid()
 362:     {
 363:       return it.current() != null;
 364:     }
 365: 
 366:     /**
 367:      * Type of tag for this iterator.
 368:      */
 369:     public Tag getTag()
 370:     {
 371:       return tag;
 372:     }
 373: 
 374:   }
 375: 
 376:   public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event)
 377:   throws NotImplementedException
 378:   {
 379:     // TODO: Implement this properly.
 380:   }
 381:   
 382:   /**
 383:    * Gets an iterator for the given HTML.Tag.
 384:    * @param t the requested HTML.Tag
 385:    * @return the Iterator
 386:    */
 387:   public HTMLDocument.Iterator getIterator (HTML.Tag t)
 388:   {
 389:     return new HTMLDocument.LeafIterator(t, this);
 390:   }
 391:   
 392:   /**
 393:    * An iterator over a particular type of tag.
 394:    */
 395:   public abstract static class Iterator
 396:   {
 397:     /**
 398:      * Return the attribute set for this tag.
 399:      * @return the <code>AttributeSet</code> (null if none found).
 400:      */
 401:     public abstract AttributeSet getAttributes();
 402:     
 403:     /**
 404:      * Get the end of the range for the current occurrence of the tag
 405:      * being defined and having the same attributes.
 406:      * @return the end of the range
 407:      */
 408:     public abstract int getEndOffset();
 409:     
 410:     /**
 411:      * Get the start of the range for the current occurrence of the tag
 412:      * being defined and having the same attributes.
 413:      * @return the start of the range (-1 if it can't be found).
 414:      */
 415:     public abstract int getStartOffset();
 416:     
 417:     /**
 418:      * Move the iterator forward.
 419:      */
 420:     public abstract void next();
 421:     
 422:     /**
 423:      * Indicates whether or not the iterator currently represents an occurrence
 424:      * of the tag.
 425:      * @return true if the iterator currently represents an occurrence of the
 426:      * tag.
 427:      */
 428:     public abstract boolean isValid();
 429:     
 430:     /**
 431:      * Type of tag this iterator represents.
 432:      * @return the tag.
 433:      */
 434:     public abstract HTML.Tag getTag();
 435:   }
 436:   
 437:   public class BlockElement extends AbstractDocument.BranchElement
 438:   {
 439:     public BlockElement (Element parent, AttributeSet a)
 440:     {
 441:       super(parent, a);
 442:     }
 443:     
 444:     /**
 445:      * Gets the resolving parent.  Since HTML attributes are not 
 446:      * inherited at the model level, this returns null.
 447:      */
 448:     public AttributeSet getResolveParent()
 449:     {
 450:       return null;
 451:     }
 452:     
 453:     /**
 454:      * Gets the name of the element.
 455:      * 
 456:      * @return the name of the element if it exists, null otherwise.
 457:      */
 458:     public String getName()
 459:     {
 460:       Object tag = getAttribute(StyleConstants.NameAttribute);
 461:       String name = null;
 462:       if (tag != null)
 463:         name = tag.toString();
 464:       return name;
 465:     }
 466:   }
 467: 
 468:   /**
 469:    * RunElement represents a section of text that has a set of 
 470:    * HTML character level attributes assigned to it.
 471:    */
 472:   public class RunElement extends AbstractDocument.LeafElement
 473:   {
 474:     
 475:     /**
 476:      * Constructs an element that has no children. It represents content
 477:      * within the document.
 478:      * 
 479:      * @param parent - parent of this
 480:      * @param a - elements attributes
 481:      * @param start - the start offset >= 0
 482:      * @param end - the end offset 
 483:      */
 484:     public RunElement(Element parent, AttributeSet a, int start, int end)
 485:     {
 486:       super(parent, a, start, end);
 487:     }
 488:     
 489:     /**
 490:      * Gets the name of the element.
 491:      * 
 492:      * @return the name of the element if it exists, null otherwise.
 493:      */
 494:     public String getName()
 495:     {
 496:       Object tag = getAttribute(StyleConstants.NameAttribute);
 497:       String name = null;
 498:       if (tag != null)
 499:         name = tag.toString();
 500:       return name;
 501:     }
 502:     
 503:     /**
 504:      * Gets the resolving parent. HTML attributes do not inherit at the
 505:      * model level, so this method returns null.
 506:      * 
 507:      * @return null
 508:      */
 509:     public AttributeSet getResolveParent()
 510:     {
 511:       return null;
 512:     }
 513:   }
 514:   
 515:   /**
 516:    * A reader to load an HTMLDocument with HTML structure.
 517:    * 
 518:    * @author Anthony Balkissoon abalkiss at redhat dot com
 519:    */
 520:   public class HTMLReader extends HTMLEditorKit.ParserCallback
 521:   {    
 522:     /**
 523:      * Holds the current character attribute set *
 524:      */
 525:     protected MutableAttributeSet charAttr = new SimpleAttributeSet();
 526:     
 527:     protected Vector parseBuffer = new Vector();
 528:     
 529:     /** 
 530:      * A stack for character attribute sets *
 531:      */
 532:     Stack charAttrStack = new Stack();
 533: 
 534:     /**
 535:      * The parse stack. This stack holds HTML.Tag objects that reflect the
 536:      * current position in the parsing process.
 537:      */
 538:     Stack parseStack = new Stack();
 539:    
 540:     /** A mapping between HTML.Tag objects and the actions that handle them **/
 541:     HashMap tagToAction;
 542:     
 543:     /** Tells us whether we've received the '</html>' tag yet **/
 544:     boolean endHTMLEncountered = false;
 545:     
 546:     /** 
 547:      * Related to the constructor with explicit insertTag 
 548:      */
 549:     int popDepth;
 550:     
 551:     /**
 552:      * Related to the constructor with explicit insertTag
 553:      */    
 554:     int pushDepth;
 555:     
 556:     /** 
 557:      * Related to the constructor with explicit insertTag
 558:      */    
 559:     int offset;
 560:     
 561:     /**
 562:      * The tag (inclusve), after that the insertion should start.
 563:      */
 564:     HTML.Tag insertTag;
 565:     
 566:     /**
 567:      * This variable becomes true after the insert tag has been encountered.
 568:      */
 569:     boolean insertTagEncountered;
 570: 
 571:     
 572:     /** A temporary variable that helps with the printing out of debug information **/
 573:     boolean debug = false;
 574:     
 575:     void print (String line)
 576:     {
 577:       if (debug)
 578:         System.out.println (line);
 579:     }
 580:     
 581:     public class TagAction
 582:     {
 583:       /**
 584:        * This method is called when a start tag is seen for one of the types
 585:        * of tags associated with this Action.  By default this does nothing.
 586:        */
 587:       public void start(HTML.Tag t, MutableAttributeSet a)
 588:       {
 589:         // Nothing to do here.
 590:       }
 591:       
 592:       /**
 593:        * Called when an end tag is seen for one of the types of tags associated
 594:        * with this Action.  By default does nothing.
 595:        */
 596:       public void end(HTML.Tag t)
 597:       {
 598:         // Nothing to do here.
 599:       }
 600:     }
 601: 
 602:     public class BlockAction extends TagAction
 603:     {      
 604:       /**
 605:        * This method is called when a start tag is seen for one of the types
 606:        * of tags associated with this Action.
 607:        */
 608:       public void start(HTML.Tag t, MutableAttributeSet a)
 609:       {
 610:         // Tell the parse buffer to open a new block for this tag.
 611:         blockOpen(t, a);
 612:       }
 613:       
 614:       /**
 615:        * Called when an end tag is seen for one of the types of tags associated
 616:        * with this Action.
 617:        */
 618:       public void end(HTML.Tag t)
 619:       {
 620:         // Tell the parse buffer to close this block.
 621:         blockClose(t);
 622:       }
 623:     }
 624:     
 625:     public class CharacterAction extends TagAction
 626:     {
 627:       /**
 628:        * This method is called when a start tag is seen for one of the types
 629:        * of tags associated with this Action.
 630:        */
 631:       public void start(HTML.Tag t, MutableAttributeSet a)
 632:       {
 633:         // Put the old attribute set on the stack.
 634:         pushCharacterStyle();
 635: 
 636:     // Translate tag.. return if succesful.
 637:     if(CharacterAttributeTranslator.translateTag(charAttr, t, a))
 638:       return;
 639: 
 640:         // Just add the attributes in <code>a</code>.
 641:      if (a != null)
 642:        charAttr.addAttribute(t, a.copyAttributes());          
 643:       }
 644: 
 645:       /**
 646:        * Called when an end tag is seen for one of the types of tags associated
 647:        * with this Action.
 648:        */
 649:       public void end(HTML.Tag t)
 650:       {
 651:         popCharacterStyle();
 652:       } 
 653:     }
 654:     
 655:     public class FormAction extends SpecialAction
 656:     {
 657:       /**
 658:        * This method is called when a start tag is seen for one of the types
 659:        * of tags associated with this Action.
 660:        */
 661:       public void start(HTML.Tag t, MutableAttributeSet a)
 662:         throws NotImplementedException
 663:       {
 664:         // FIXME: Implement.
 665:         print ("FormAction.start not implemented");
 666:       }
 667:       
 668:       /**
 669:        * Called when an end tag is seen for one of the types of tags associated
 670:        * with this Action.
 671:        */
 672:       public void end(HTML.Tag t)
 673:         throws NotImplementedException
 674:       {
 675:         // FIXME: Implement.
 676:         print ("FormAction.end not implemented");
 677:       } 
 678:     }
 679:     
 680:     /**
 681:      * This action indicates that the content between starting and closing HTML
 682:      * elements (like script - /script) should not be visible. The content is
 683:      * still inserted and can be accessed when iterating the HTML document. The
 684:      * parser will only fire
 685:      * {@link javax.swing.text.html.HTMLEditorKit.ParserCallback#handleText} for
 686:      * the hidden tags, regardless from that html tags the hidden section may
 687:      * contain.
 688:      */
 689:     public class HiddenAction
 690:         extends TagAction
 691:     {
 692:       /**
 693:        * This method is called when a start tag is seen for one of the types
 694:        * of tags associated with this Action.
 695:        */
 696:       public void start(HTML.Tag t, MutableAttributeSet a)
 697:       {
 698:         blockOpen(t, a);
 699:       }
 700:       
 701:       /**
 702:        * Called when an end tag is seen for one of the types of tags associated
 703:        * with this Action.
 704:        */
 705:       public void end(HTML.Tag t)
 706:       {
 707:         blockClose(t);
 708:       } 
 709:     }
 710:     
 711:     public class IsindexAction extends TagAction
 712:     {
 713:       /**
 714:        * This method is called when a start tag is seen for one of the types
 715:        * of tags associated with this Action.
 716:        */
 717:       public void start(HTML.Tag t, MutableAttributeSet a)
 718:         throws NotImplementedException
 719:       {
 720:         // FIXME: Implement.
 721:         print ("IsindexAction.start not implemented");
 722:       }
 723:     }
 724:     
 725:     public class ParagraphAction extends BlockAction
 726:     {
 727:       /**
 728:        * This method is called when a start tag is seen for one of the types
 729:        * of tags associated with this Action.
 730:        */
 731:       public void start(HTML.Tag t, MutableAttributeSet a)
 732:       {
 733:         blockOpen(t, a);
 734:       }
 735:       
 736:       /**
 737:        * Called when an end tag is seen for one of the types of tags associated
 738:        * with this Action.
 739:        */
 740:       public void end(HTML.Tag t)
 741:       {
 742:         blockClose(t);
 743:       } 
 744:     }
 745:     
 746:     public class PreAction extends BlockAction
 747:     {
 748:       /**
 749:        * This method is called when a start tag is seen for one of the types
 750:        * of tags associated with this Action.
 751:        */
 752:       public void start(HTML.Tag t, MutableAttributeSet a)
 753:         throws NotImplementedException
 754:       {
 755:         // FIXME: Implement.
 756:         print ("PreAction.start not implemented");
 757:         super.start(t, a);
 758:       }
 759:       
 760:       /**
 761:        * Called when an end tag is seen for one of the types of tags associated
 762:        * with this Action.
 763:        */
 764:       public void end(HTML.Tag t)
 765:         throws NotImplementedException
 766:       {
 767:         // FIXME: Implement.
 768:         print ("PreAction.end not implemented");
 769:         super.end(t);
 770:       } 
 771:     }
 772:     
 773:     /**
 774:      * Inserts the elements that are represented by ths single tag with 
 775:      * attributes (only). The closing tag, even if present, mut follow
 776:      * immediately after the starting tag without providing any additional
 777:      * information. Hence the {@link TagAction#end} method need not be
 778:      * overridden and still does nothing.
 779:      */
 780:     public class SpecialAction extends TagAction
 781:     {
 782:       /**
 783:        * The functionality is delegated to {@link HTMLReader#addSpecialElement}
 784:        */
 785:       public void start(HTML.Tag t, MutableAttributeSet a)
 786:       {
 787:         addSpecialElement(t, a);
 788:       }
 789:     }
 790:     
 791:     class AreaAction extends TagAction
 792:     {
 793:       /**
 794:        * This method is called when a start tag is seen for one of the types
 795:        * of tags associated with this Action.
 796:        */
 797:       public void start(HTML.Tag t, MutableAttributeSet a)
 798:         throws NotImplementedException
 799:       {
 800:         // FIXME: Implement.
 801:         print ("AreaAction.start not implemented");
 802:       }
 803:       
 804:       /**
 805:        * Called when an end tag is seen for one of the types of tags associated
 806:        * with this Action.
 807:        */
 808:       public void end(HTML.Tag t)
 809:         throws NotImplementedException
 810:       {
 811:         // FIXME: Implement.
 812:         print ("AreaAction.end not implemented");
 813:       } 
 814:     }
 815:     
 816:     class BaseAction extends TagAction
 817:     {
 818:       /**
 819:        * This method is called when a start tag is seen for one of the types
 820:        * of tags associated with this Action.
 821:        */
 822:       public void start(HTML.Tag t, MutableAttributeSet a)
 823:         throws NotImplementedException
 824:       {
 825:         // FIXME: Implement.
 826:         print ("BaseAction.start not implemented");
 827:       }
 828:       
 829:       /**
 830:        * Called when an end tag is seen for one of the types of tags associated
 831:        * with this Action.
 832:        */
 833:       public void end(HTML.Tag t)
 834:         throws NotImplementedException
 835:       {
 836:         // FIXME: Implement.
 837:         print ("BaseAction.end not implemented");
 838:       } 
 839:     }
 840:     
 841:     class HeadAction extends BlockAction
 842:     {
 843:       /**
 844:        * This method is called when a start tag is seen for one of the types
 845:        * of tags associated with this Action.
 846:        */
 847:       public void start(HTML.Tag t, MutableAttributeSet a)
 848:         throws NotImplementedException
 849:       {
 850:         // FIXME: Implement.
 851:         print ("HeadAction.start not implemented: "+t);
 852:         super.start(t, a);
 853:       }
 854:       
 855:       /**
 856:        * Called when an end tag is seen for one of the types of tags associated
 857:        * with this Action.
 858:        */
 859:       public void end(HTML.Tag t)
 860:         throws NotImplementedException
 861:       {
 862:         // FIXME: Implement.
 863:         print ("HeadAction.end not implemented: "+t);
 864:         super.end(t);
 865:       } 
 866:     }
 867:     
 868:     class LinkAction extends TagAction
 869:     {
 870:       /**
 871:        * This method is called when a start tag is seen for one of the types
 872:        * of tags associated with this Action.
 873:        */
 874:       public void start(HTML.Tag t, MutableAttributeSet a)
 875:         throws NotImplementedException
 876:       {
 877:         // FIXME: Implement.
 878:         print ("LinkAction.start not implemented");
 879:       }
 880:       
 881:       /**
 882:        * Called when an end tag is seen for one of the types of tags associated
 883:        * with this Action.
 884:        */
 885:       public void end(HTML.Tag t)
 886:         throws NotImplementedException
 887:       {
 888:         // FIXME: Implement.
 889:         print ("LinkAction.end not implemented");
 890:       } 
 891:     }
 892:     
 893:     class MapAction extends TagAction
 894:     {
 895:       /**
 896:        * This method is called when a start tag is seen for one of the types
 897:        * of tags associated with this Action.
 898:        */
 899:       public void start(HTML.Tag t, MutableAttributeSet a)
 900:         throws NotImplementedException
 901:       {
 902:         // FIXME: Implement.
 903:         print ("MapAction.start not implemented");
 904:       }
 905:       
 906:       /**
 907:        * Called when an end tag is seen for one of the types of tags associated
 908:        * with this Action.
 909:        */
 910:       public void end(HTML.Tag t)
 911:         throws NotImplementedException
 912:       {
 913:         // FIXME: Implement.
 914:         print ("MapAction.end not implemented");
 915:       } 
 916:     }
 917:     
 918:     class MetaAction extends TagAction
 919:     {
 920:       /**
 921:        * This method is called when a start tag is seen for one of the types
 922:        * of tags associated with this Action.
 923:        */
 924:       public void start(HTML.Tag t, MutableAttributeSet a)
 925:         throws NotImplementedException
 926:       {
 927:         // FIXME: Implement.
 928:         print ("MetaAction.start not implemented");
 929:       }
 930:       
 931:       /**
 932:        * Called when an end tag is seen for one of the types of tags associated
 933:        * with this Action.
 934:        */
 935:       public void end(HTML.Tag t)
 936:         throws NotImplementedException
 937:       {
 938:         // FIXME: Implement.
 939:         print ("MetaAction.end not implemented");
 940:       } 
 941:     }
 942:     
 943:     class StyleAction extends TagAction
 944:     {
 945:       /**
 946:        * This method is called when a start tag is seen for one of the types
 947:        * of tags associated with this Action.
 948:        */
 949:       public void start(HTML.Tag t, MutableAttributeSet a)
 950:         throws NotImplementedException
 951:       {
 952:         // FIXME: Implement.
 953:         print ("StyleAction.start not implemented");
 954:       }
 955:       
 956:       /**
 957:        * Called when an end tag is seen for one of the types of tags associated
 958:        * with this Action.
 959:        */
 960:       public void end(HTML.Tag t)
 961:         throws NotImplementedException
 962:       {
 963:         // FIXME: Implement.
 964:         print ("StyleAction.end not implemented");
 965:       } 
 966:     }
 967:     
 968:     class TitleAction extends TagAction
 969:     {
 970:       /**
 971:        * This method is called when a start tag is seen for one of the types
 972:        * of tags associated with this Action.
 973:        */
 974:       public void start(HTML.Tag t, MutableAttributeSet a)
 975:         throws NotImplementedException
 976:       {
 977:         // FIXME: Implement.
 978:         print ("TitleAction.start not implemented");
 979:       }
 980:       
 981:       /**
 982:        * Called when an end tag is seen for one of the types of tags associated
 983:        * with this Action.
 984:        */
 985:       public void end(HTML.Tag t)
 986:         throws NotImplementedException
 987:       {
 988:         // FIXME: Implement.
 989:         print ("TitleAction.end not implemented");
 990:       } 
 991:     }    
 992:     
 993:     public HTMLReader(int offset)
 994:     {
 995:       this (offset, 0, 0, null);
 996:     }
 997:     
 998:     public HTMLReader(int offset, int popDepth, int pushDepth,
 999:                       HTML.Tag insertTag)
1000:     {
1001:       print ("HTMLReader created with pop: "+popDepth
1002:                           + " push: "+pushDepth + " offset: "+offset
1003:                           + " tag: "+insertTag);
1004:       this.insertTag = insertTag;
1005:       this.offset = offset;
1006:       this.popDepth = popDepth;
1007:       this.pushDepth = pushDepth;
1008:       initTags();
1009:     }
1010:     
1011:     void initTags()
1012:     {
1013:       tagToAction = new HashMap(72);
1014:       CharacterAction characterAction = new CharacterAction();
1015:       HiddenAction hiddenAction = new HiddenAction();
1016:       AreaAction areaAction = new AreaAction();
1017:       BaseAction baseAction = new BaseAction();
1018:       BlockAction blockAction = new BlockAction();
1019:       SpecialAction specialAction = new SpecialAction();
1020:       ParagraphAction paragraphAction = new ParagraphAction();
1021:       HeadAction headAction = new HeadAction();
1022:       FormAction formAction = new FormAction();
1023:       IsindexAction isindexAction = new IsindexAction();
1024:       LinkAction linkAction = new LinkAction();
1025:       MapAction mapAction = new MapAction();
1026:       PreAction preAction = new PreAction();
1027:       MetaAction metaAction = new MetaAction();
1028:       StyleAction styleAction = new StyleAction();
1029:       TitleAction titleAction = new TitleAction();
1030:       
1031:       
1032:       tagToAction.put(HTML.Tag.A, characterAction);
1033:       tagToAction.put(HTML.Tag.ADDRESS, characterAction);
1034:       tagToAction.put(HTML.Tag.APPLET, hiddenAction);
1035:       tagToAction.put(HTML.Tag.AREA, areaAction);
1036:       tagToAction.put(HTML.Tag.B, characterAction);
1037:       tagToAction.put(HTML.Tag.BASE, baseAction);
1038:       tagToAction.put(HTML.Tag.BASEFONT, characterAction);
1039:       tagToAction.put(HTML.Tag.BIG, characterAction);
1040:       tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction);
1041:       tagToAction.put(HTML.Tag.BODY, blockAction);
1042:       tagToAction.put(HTML.Tag.BR, specialAction);
1043:       tagToAction.put(HTML.Tag.CAPTION, blockAction);
1044:       tagToAction.put(HTML.Tag.CENTER, blockAction);
1045:       tagToAction.put(HTML.Tag.CITE, characterAction);
1046:       tagToAction.put(HTML.Tag.CODE, characterAction);
1047:       tagToAction.put(HTML.Tag.DD, blockAction);
1048:       tagToAction.put(HTML.Tag.DFN, characterAction);
1049:       tagToAction.put(HTML.Tag.DIR, blockAction);
1050:       tagToAction.put(HTML.Tag.DIV, blockAction);
1051:       tagToAction.put(HTML.Tag.DL, blockAction);
1052:       tagToAction.put(HTML.Tag.DT, paragraphAction);
1053:       tagToAction.put(HTML.Tag.EM, characterAction);
1054:       tagToAction.put(HTML.Tag.FONT, characterAction);
1055:       tagToAction.put(HTML.Tag.FORM, blockAction);
1056:       tagToAction.put(HTML.Tag.FRAME, specialAction);
1057:       tagToAction.put(HTML.Tag.FRAMESET, blockAction);
1058:       tagToAction.put(HTML.Tag.H1, paragraphAction);
1059:       tagToAction.put(HTML.Tag.H2, paragraphAction);
1060:       tagToAction.put(HTML.Tag.H3, paragraphAction);
1061:       tagToAction.put(HTML.Tag.H4, paragraphAction);
1062:       tagToAction.put(HTML.Tag.H5, paragraphAction);
1063:       tagToAction.put(HTML.Tag.H6, paragraphAction);
1064:       tagToAction.put(HTML.Tag.HEAD, headAction);
1065:       tagToAction.put(HTML.Tag.HR, specialAction);
1066:       tagToAction.put(HTML.Tag.HTML, blockAction);
1067:       tagToAction.put(HTML.Tag.I, characterAction);
1068:       tagToAction.put(HTML.Tag.IMG, specialAction);
1069:       tagToAction.put(HTML.Tag.INPUT, formAction);
1070:       tagToAction.put(HTML.Tag.ISINDEX, isindexAction);
1071:       tagToAction.put(HTML.Tag.KBD, characterAction);
1072:       tagToAction.put(HTML.Tag.LI, blockAction);
1073:       tagToAction.put(HTML.Tag.LINK, linkAction);
1074:       tagToAction.put(HTML.Tag.MAP, mapAction);
1075:       tagToAction.put(HTML.Tag.MENU, blockAction);
1076:       tagToAction.put(HTML.Tag.META, metaAction);
1077:       tagToAction.put(HTML.Tag.NOFRAMES, blockAction);
1078:       tagToAction.put(HTML.Tag.OBJECT, specialAction);
1079:       tagToAction.put(HTML.Tag.OL, blockAction);
1080:       tagToAction.put(HTML.Tag.OPTION, formAction);
1081:       tagToAction.put(HTML.Tag.P, paragraphAction);
1082:       tagToAction.put(HTML.Tag.PARAM, hiddenAction);
1083:       tagToAction.put(HTML.Tag.PRE, preAction);
1084:       tagToAction.put(HTML.Tag.SAMP, characterAction);
1085:       tagToAction.put(HTML.Tag.SCRIPT, hiddenAction);
1086:       tagToAction.put(HTML.Tag.SELECT, formAction);
1087:       tagToAction.put(HTML.Tag.SMALL, characterAction);
1088:       tagToAction.put(HTML.Tag.STRIKE, characterAction);
1089:       tagToAction.put(HTML.Tag.S, characterAction);      
1090:       tagToAction.put(HTML.Tag.STRONG, characterAction);
1091:       tagToAction.put(HTML.Tag.STYLE, styleAction);
1092:       tagToAction.put(HTML.Tag.SUB, characterAction);
1093:       tagToAction.put(HTML.Tag.SUP, characterAction);
1094:       tagToAction.put(HTML.Tag.TABLE, blockAction);
1095:       tagToAction.put(HTML.Tag.TD, blockAction);
1096:       tagToAction.put(HTML.Tag.TEXTAREA, formAction);
1097:       tagToAction.put(HTML.Tag.TH, blockAction);
1098:       tagToAction.put(HTML.Tag.TITLE, titleAction);
1099:       tagToAction.put(HTML.Tag.TR, blockAction);
1100:       tagToAction.put(HTML.Tag.TT, characterAction);
1101:       tagToAction.put(HTML.Tag.U, characterAction);
1102:       tagToAction.put(HTML.Tag.UL, blockAction);
1103:       tagToAction.put(HTML.Tag.VAR, characterAction);
1104:     }
1105:     
1106:     /**
1107:      * Pushes the current character style onto the stack.
1108:      *
1109:      */
1110:     protected void pushCharacterStyle()
1111:     {
1112:       charAttrStack.push(charAttr.copyAttributes());
1113:     }
1114:     
1115:     /**
1116:      * Pops a character style off of the stack and uses it as the 
1117:      * current character style.
1118:      *
1119:      */
1120:     protected void popCharacterStyle()
1121:     {
1122:       if (!charAttrStack.isEmpty())
1123:         charAttr = (MutableAttributeSet) charAttrStack.pop();
1124:     }
1125:     
1126:     /**
1127:      * Registers a given tag with a given Action.  All of the well-known tags
1128:      * are registered by default, but this method can change their behaviour
1129:      * or add support for custom or currently unsupported tags.
1130:      * 
1131:      * @param t the Tag to register
1132:      * @param a the Action for the Tag
1133:      */
1134:     protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a)
1135:     {
1136:       tagToAction.put (t, a);
1137:     }
1138:     
1139:     /**
1140:      * This is the last method called on the HTMLReader, allowing any pending
1141:      * changes to be flushed to the HTMLDocument.
1142:      */
1143:     public void flush() throws BadLocationException
1144:     {
1145:       DefaultStyledDocument.ElementSpec[] elements;
1146:       elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()];
1147:       parseBuffer.copyInto(elements);
1148:       parseBuffer.removeAllElements();
1149:       if (offset == 0)
1150:         create(elements);
1151:       else
1152:         insert(offset, elements);
1153: 
1154:       offset += HTMLDocument.this.getLength() - offset;
1155:     }
1156:     
1157:     /**
1158:      * This method is called by the parser to indicate a block of 
1159:      * text was encountered.  Should insert the text appropriately.
1160:      * 
1161:      * @param data the text that was inserted
1162:      * @param pos the position at which the text was inserted
1163:      */
1164:     public void handleText(char[] data, int pos)
1165:     {
1166:       if (data != null && data.length > 0)
1167:         addContent(data, 0, data.length);
1168:     }
1169:     
1170:     /**
1171:      * Checks if the HTML tag should be inserted. The tags before insert tag (if
1172:      * specified) are not inserted. Also, the tags after the end of the html are
1173:      * not inserted.
1174:      * 
1175:      * @return true if the tag should be inserted, false otherwise.
1176:      */
1177:     private boolean shouldInsert()
1178:     {
1179:       return ! endHTMLEncountered
1180:              && (insertTagEncountered || insertTag == null);
1181:     }
1182:     
1183:     /**
1184:      * This method is called by the parser and should route the call to the
1185:      * proper handler for the tag.
1186:      * 
1187:      * @param t the HTML.Tag
1188:      * @param a the attribute set
1189:      * @param pos the position at which the tag was encountered
1190:      */
1191:     public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
1192:     {
1193:       if (t == insertTag)
1194:         insertTagEncountered = true;
1195: 
1196:       if (shouldInsert())
1197:         {
1198:           TagAction action = (TagAction) tagToAction.get(t);
1199:           if (action != null)
1200:             action.start(t, a);
1201:         }
1202:     }
1203:     
1204:     /**
1205:      * This method called by parser to handle a comment block.
1206:      * 
1207:      * @param data the comment
1208:      * @param pos the position at which the comment was encountered
1209:      */
1210:     public void handleComment(char[] data, int pos)
1211:     {
1212:       if (shouldInsert())
1213:         {
1214:           TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT);
1215:           if (action != null)
1216:             {
1217:               action.start(HTML.Tag.COMMENT, 
1218:                            htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET);
1219:               action.end(HTML.Tag.COMMENT);
1220:             }
1221:         }
1222:     }
1223:     
1224:     /**
1225:      * This method is called by the parser and should route the call to the
1226:      * proper handler for the tag.
1227:      * 
1228:      * @param t the HTML.Tag
1229:      * @param pos the position at which the tag was encountered
1230:      */
1231:     public void handleEndTag(HTML.Tag t, int pos)
1232:     {
1233:       if (shouldInsert())
1234:         {
1235:           // If this is the </html> tag we need to stop calling the Actions
1236:           if (t == HTML.Tag.HTML)
1237:             endHTMLEncountered = true;
1238: 
1239:           TagAction action = (TagAction) tagToAction.get(t);
1240:           if (action != null)
1241:             action.end(t);
1242:         }
1243:     }
1244:     
1245:     /**
1246:      * This is a callback from the parser that should be routed to the
1247:      * appropriate handler for the tag.
1248:      * 
1249:      * @param t the HTML.Tag that was encountered
1250:      * @param a the attribute set
1251:      * @param pos the position at which the tag was encountered
1252:      */
1253:     public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos)
1254:     {
1255:       if (t == insertTag)
1256:         insertTagEncountered = true;
1257: 
1258:       if (shouldInsert())
1259:         {
1260:           TagAction action = (TagAction) tagToAction.get(t);
1261:           if (action != null)
1262:             {
1263:               action.start(t, a);
1264:               action.end(t);
1265:             }
1266:         }
1267:     }
1268:     
1269:     /**
1270:      * This is invoked after the stream has been parsed but before it has been
1271:      * flushed.
1272:      * 
1273:      * @param eol one of \n, \r, or \r\n, whichever was encountered the most in 
1274:      * parsing the stream
1275:      * @since 1.3
1276:      */
1277:     public void handleEndOfLineString(String eol)
1278:     {
1279:       // FIXME: Implement.
1280:       print ("HTMLReader.handleEndOfLineString not implemented yet");
1281:     }
1282:     
1283:     /**
1284:      * Adds the given text to the textarea document.  Called only when we are
1285:      * within a textarea.  
1286:      * 
1287:      * @param data the text to add to the textarea
1288:      */
1289:     protected void textAreaContent(char[] data)
1290:       throws NotImplementedException
1291:     {
1292:       // FIXME: Implement.
1293:       print ("HTMLReader.textAreaContent not implemented yet");
1294:     }
1295:     
1296:     /**
1297:      * Adds the given text that was encountered in a <PRE> element.
1298:      * 
1299:      * @param data the text
1300:      */
1301:     protected void preContent(char[] data)
1302:       throws NotImplementedException
1303:     {
1304:       // FIXME: Implement
1305:       print ("HTMLReader.preContent not implemented yet");
1306:     }
1307:     
1308:     /**
1309:      * Instructs the parse buffer to create a block element with the given 
1310:      * attributes.
1311:      * 
1312:      * @param t the tag that requires opening a new block
1313:      * @param attr the attribute set for the new block
1314:      */
1315:     protected void blockOpen(HTML.Tag t, MutableAttributeSet attr)
1316:     {
1317:       printBuffer();
1318:       DefaultStyledDocument.ElementSpec element;
1319: 
1320:       parseStack.push(t);
1321:       AbstractDocument.AttributeContext ctx = getAttributeContext();
1322:       AttributeSet copy = attr.copyAttributes();
1323:       copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t);
1324:       element = new DefaultStyledDocument.ElementSpec(copy,
1325:                                DefaultStyledDocument.ElementSpec.StartTagType);
1326:       parseBuffer.addElement(element);
1327:       printBuffer();
1328:     }
1329: 
1330:     /**
1331:      * Instructs the parse buffer to close the block element associated with 
1332:      * the given HTML.Tag
1333:      * 
1334:      * @param t the HTML.Tag that is closing its block
1335:      */
1336:     protected void blockClose(HTML.Tag t)
1337:     {
1338:       printBuffer();
1339:       DefaultStyledDocument.ElementSpec element;
1340: 
1341:       // If the previous tag is a start tag then we insert a synthetic
1342:       // content tag.
1343:       DefaultStyledDocument.ElementSpec prev;
1344:       prev = (DefaultStyledDocument.ElementSpec)
1345:           parseBuffer.get(parseBuffer.size() - 1);
1346:       if (prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
1347:         {
1348:           AbstractDocument.AttributeContext ctx = getAttributeContext();
1349:           AttributeSet attributes = ctx.getEmptySet();
1350:           attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
1351:                                         HTML.Tag.CONTENT);
1352:           element = new DefaultStyledDocument.ElementSpec(attributes,
1353:               DefaultStyledDocument.ElementSpec.ContentType,
1354:                                     new char[0], 0, 0);
1355:           parseBuffer.add(element);
1356:         }
1357: 
1358:       element = new DefaultStyledDocument.ElementSpec(null,
1359:                 DefaultStyledDocument.ElementSpec.EndTagType);
1360:       parseBuffer.addElement(element);
1361:       printBuffer();
1362:       if (parseStack.size() > 0)
1363:         parseStack.pop();
1364:     }
1365:     
1366:     /**
1367:      * Adds text to the appropriate context using the current character
1368:      * attribute set.
1369:      * 
1370:      * @param data the text to add
1371:      * @param offs the offset at which to add it
1372:      * @param length the length of the text to add
1373:      */
1374:     protected void addContent(char[] data, int offs, int length)
1375:     {
1376:       addContent(data, offs, length, true);
1377:     }
1378:     
1379:     /**
1380:      * Adds text to the appropriate context using the current character
1381:      * attribute set, and possibly generating an IMPLIED Tag if necessary.
1382:      * 
1383:      * @param data the text to add
1384:      * @param offs the offset at which to add it
1385:      * @param length the length of the text to add
1386:      * @param generateImpliedPIfNecessary whether or not we should generate
1387:      * an HTML.Tag.IMPLIED tag if necessary
1388:      */
1389:     protected void addContent(char[] data, int offs, int length,
1390:                               boolean generateImpliedPIfNecessary)
1391:     {
1392:       AbstractDocument.AttributeContext ctx = getAttributeContext();
1393:       DefaultStyledDocument.ElementSpec element;
1394:       AttributeSet attributes = null;
1395: 
1396:       // Copy the attribute set, don't use the same object because 
1397:       // it may change
1398:       if (charAttr != null)
1399:         attributes = charAttr.copyAttributes();
1400:       else
1401:         attributes = ctx.getEmptySet();
1402:       attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
1403:                                     HTML.Tag.CONTENT);
1404:       element = new DefaultStyledDocument.ElementSpec(attributes,
1405:                                 DefaultStyledDocument.ElementSpec.ContentType,
1406:                                 data, offs, length);
1407:       
1408:       printBuffer();
1409:       // Add the element to the buffer
1410:       parseBuffer.addElement(element);
1411:       printBuffer();
1412: 
1413:       if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold())
1414:         {
1415:           try
1416:             {
1417:               flush();
1418:             }
1419:           catch (BadLocationException ble)
1420:             {
1421:               // TODO: what to do here?
1422:             }
1423:         }
1424:     }
1425:     
1426:     /**
1427:      * Adds content that is specified in the attribute set.
1428:      * 
1429:      * @param t the HTML.Tag
1430:      * @param a the attribute set specifying the special content
1431:      */
1432:     protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a)
1433:     {
1434:       a.addAttribute(StyleConstants.NameAttribute, t);
1435:       
1436:       // Migrate from the rather htmlAttributeSet to the faster, lighter and 
1437:       // unchangeable alternative implementation.
1438:       AttributeSet copy = a.copyAttributes();
1439: 
1440:       // The two spaces are required because some special elements like HR
1441:       // must be broken. At least two characters are needed to break into the
1442:       // two parts.
1443:       DefaultStyledDocument.ElementSpec spec =
1444:         new DefaultStyledDocument.ElementSpec(copy,
1445:       DefaultStyledDocument.ElementSpec.ContentType, 
1446:           new char[] {' ', ' '}, 0, 2 );
1447:       parseBuffer.add(spec);
1448:     }
1449:     
1450:     void printBuffer()
1451:     {      
1452:       print ("\n*********BUFFER**********");
1453:       for (int i = 0; i < parseBuffer.size(); i ++)
1454:         print ("  "+parseBuffer.get(i));
1455:       print ("***************************");
1456:     }
1457:   }
1458:   
1459:   /**
1460:    * Gets the reader for the parser to use when loading the document with HTML. 
1461:    * 
1462:    * @param pos - the starting position
1463:    * @return - the reader
1464:    */
1465:   public HTMLEditorKit.ParserCallback getReader(int pos)
1466:   {
1467:     return new HTMLReader(pos);
1468:   }  
1469:   
1470:   /**
1471:    * Gets the reader for the parser to use when loading the document with HTML. 
1472:    * 
1473:    * @param pos - the starting position
1474:    * @param popDepth - the number of EndTagTypes to generate before inserting
1475:    * @param pushDepth - the number of StartTagTypes with a direction 
1476:    * of JoinNextDirection that should be generated before inserting, 
1477:    * but after the end tags have been generated.
1478:    * @param insertTag - the first tag to start inserting into document
1479:    * @return - the reader
1480:    */
1481:   public HTMLEditorKit.ParserCallback getReader(int pos,
1482:                                                 int popDepth,
1483:                                                 int pushDepth,
1484:                                                 HTML.Tag insertTag)
1485:   {
1486:     return new HTMLReader(pos, popDepth, pushDepth, insertTag);
1487:   }
1488:   
1489:   /**
1490:    * Gets the reader for the parser to use when inserting the HTML fragment into
1491:    * the document. Checks if the parser is present, sets the parent in the
1492:    * element stack and removes any actions for BODY (it can be only one body in
1493:    * a HTMLDocument).
1494:    * 
1495:    * @param pos - the starting position
1496:    * @param popDepth - the number of EndTagTypes to generate before inserting
1497:    * @param pushDepth - the number of StartTagTypes with a direction of
1498:    *          JoinNextDirection that should be generated before inserting, but
1499:    *          after the end tags have been generated.
1500:    * @param insertTag - the first tag to start inserting into document
1501:    * @param parent the element that will be the parent in the document. HTML
1502:    *          parsing includes checks for the parent, so it must be available.
1503:    * @return - the reader
1504:    * @throws IllegalStateException if the parsert is not set.
1505:    */
1506:   public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth,
1507:                                                          int pushDepth,
1508:                                                          HTML.Tag insertTag,
1509:                                                          final Element parent)
1510:       throws IllegalStateException
1511:   {
1512:     if (parser == null)
1513:       throw new IllegalStateException("Parser has not been set");
1514: 
1515:     HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag)
1516:     {
1517:       /**
1518:        * Ignore BODY.
1519:        */
1520:       public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
1521:       {
1522:         if (t != HTML.Tag.BODY)
1523:           super.handleStartTag(t, a, pos);
1524:       }
1525: 
1526:       /**
1527:        * Ignore BODY.
1528:        */
1529:       public void handleEndTag(HTML.Tag t, int pos)
1530:       {
1531:         if (t != HTML.Tag.BODY)
1532:           super.handleEndTag(t, pos);
1533:       }
1534:     };
1535:       
1536:     // Set the parent HTML tag.
1537:     reader.parseStack.push(parent.getAttributes().getAttribute(
1538:       StyleConstants.NameAttribute));
1539: 
1540:     return reader;
1541:   }   
1542:   
1543:   /**
1544:    * Gets the child element that contains the attribute with the value or null.
1545:    * Not thread-safe.
1546:    * 
1547:    * @param e - the element to begin search at
1548:    * @param attribute - the desired attribute
1549:    * @param value - the desired value
1550:    * @return the element found with the attribute and value specified or null if
1551:    *         it is not found.
1552:    */
1553:   public Element getElement(Element e, Object attribute, Object value)
1554:   {
1555:     if (e != null)
1556:       {
1557:         if (e.getAttributes().containsAttribute(attribute, value))
1558:           return e;
1559:         
1560:         int count = e.getElementCount();
1561:         for (int j = 0; j < count; j++)
1562:           {
1563:             Element child = e.getElement(j);
1564:             if (child.getAttributes().containsAttribute(attribute, value))
1565:               return child;
1566:             
1567:             Element grandChild = getElement(child, attribute, value);
1568:             if (grandChild != null)
1569:               return grandChild;
1570:           }
1571:       }
1572:     return null;
1573:   }
1574:   
1575:   /**
1576:    * Returns the element that has the given id Attribute (for instance, &lt;p id
1577:    * ='my paragraph &gt;'). If it is not found, null is returned. The HTML tag,
1578:    * having this attribute, is not checked by this method and can be any. The
1579:    * method is not thread-safe.
1580:    * 
1581:    * @param attrId - the value of the attribute id to look for
1582:    * @return the element that has the given id.
1583:    */
1584:   public Element getElement(String attrId)
1585:   {
1586:     return getElement(getDefaultRootElement(), HTML.Attribute.ID,
1587:                       attrId);
1588:   }
1589:   
1590:   /**
1591:    * Replaces the children of the given element with the contents of
1592:    * the string. The document must have an HTMLEditorKit.Parser set.
1593:    * This will be seen as at least two events, n inserts followed by a remove.
1594:    * 
1595:    * @param elem - the brance element whose children will be replaced
1596:    * @param htmlText - the string to be parsed and assigned to element.
1597:    * @throws BadLocationException
1598:    * @throws IOException
1599:    * @throws IllegalArgumentException - if elem is a leaf 
1600:    * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set
1601:    */
1602:   public void setInnerHTML(Element elem, String htmlText) 
1603:     throws BadLocationException, IOException
1604:   {
1605:     if (elem.isLeaf())
1606:       throw new IllegalArgumentException("Element is a leaf");
1607:     
1608:     int start = elem.getStartOffset();
1609:     int end = elem.getEndOffset();
1610: 
1611:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1612:       end, 0, 0, HTML.Tag.BODY, elem);
1613: 
1614:     // TODO charset
1615:     getParser().parse(new StringReader(htmlText), reader, true);
1616:     
1617:     // Remove the previous content
1618:     remove(start, end - start);
1619:   }
1620:   
1621:   /**
1622:    * Replaces the given element in the parent with the string. When replacing a
1623:    * leaf, this will attempt to make sure there is a newline present if one is
1624:    * needed. This may result in an additional element being inserted. This will
1625:    * be seen as at least two events, n inserts followed by a remove. The
1626:    * HTMLEditorKit.Parser must be set.
1627:    * 
1628:    * @param elem - the branch element whose parent will be replaced
1629:    * @param htmlText - the string to be parsed and assigned to elem
1630:    * @throws BadLocationException
1631:    * @throws IOException
1632:    * @throws IllegalStateException - if parser is not set
1633:    */
1634: public void setOuterHTML(Element elem, String htmlText)
1635:       throws BadLocationException, IOException
1636:   {
1637:     // Remove the current element:
1638:     int start = elem.getStartOffset();
1639:     int end = elem.getEndOffset();
1640: 
1641:     remove(start, end-start);
1642:        
1643:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1644:       start, 0, 0, HTML.Tag.BODY, elem);
1645: 
1646:     // TODO charset
1647:     getParser().parse(new StringReader(htmlText), reader, true);
1648:   }
1649:   
1650:   /**
1651:    * Inserts the string before the start of the given element. The parser must
1652:    * be set.
1653:    * 
1654:    * @param elem - the element to be the root for the new text.
1655:    * @param htmlText - the string to be parsed and assigned to elem
1656:    * @throws BadLocationException
1657:    * @throws IOException
1658:    * @throws IllegalStateException - if parser has not been set
1659:    */
1660:   public void insertBeforeStart(Element elem, String htmlText)
1661:       throws BadLocationException, IOException
1662:   {
1663:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1664:       elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
1665: 
1666:     // TODO charset
1667:     getParser().parse(new StringReader(htmlText), reader, true);
1668:   }
1669:   
1670:   /**
1671:    * Inserts the string at the end of the element. If elem's children are
1672:    * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it
1673:    * will be inserted before the newline. The parser must be set.
1674:    * 
1675:    * @param elem - the element to be the root for the new text
1676:    * @param htmlText - the text to insert
1677:    * @throws BadLocationException
1678:    * @throws IOException
1679:    * @throws IllegalStateException - if parser is not set
1680:    */
1681:   public void insertBeforeEnd(Element elem, String htmlText)
1682:       throws BadLocationException, IOException
1683:   {
1684:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1685:       elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
1686: 
1687:     // TODO charset
1688:     getParser().parse(new StringReader(htmlText), reader, true);
1689: 
1690:   }
1691:   
1692:   /**
1693:    * Inserts the string after the end of the given element.
1694:    * The parser must be set.
1695:    * 
1696:    * @param elem - the element to be the root for the new text
1697:    * @param htmlText - the text to insert
1698:    * @throws BadLocationException
1699:    * @throws IOException
1700:    * @throws IllegalStateException - if parser is not set
1701:    */
1702:   public void insertAfterEnd(Element elem, String htmlText)
1703:       throws BadLocationException, IOException
1704:   {
1705:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1706:       elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
1707: 
1708:     // TODO charset
1709:     getParser().parse(new StringReader(htmlText), reader, true);
1710:   }
1711:   
1712:   /**
1713:    * Inserts the string at the start of the element.
1714:    * The parser must be set.
1715:    * 
1716:    * @param elem - the element to be the root for the new text
1717:    * @param htmlText - the text to insert
1718:    * @throws BadLocationException
1719:    * @throws IOException
1720:    * @throws IllegalStateException - if parser is not set
1721:    */
1722:   public void insertAfterStart(Element elem, String htmlText)
1723:       throws BadLocationException, IOException
1724:   {
1725:     HTMLEditorKit.ParserCallback reader = getInsertingReader(
1726:       elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
1727: 
1728:     // TODO charset
1729:     getParser().parse(new StringReader(htmlText), reader, true);
1730:   }
1731: }