Source for javax.swing.text.html.HTMLEditorKit

   1: /* HTMLEditorKit.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: 
  42: import java.awt.event.ActionEvent;
  43: import java.awt.event.MouseAdapter;
  44: import java.awt.event.MouseEvent;
  45: import java.awt.event.MouseMotionListener;
  46: import java.awt.Cursor;
  47: 
  48: import java.io.IOException;
  49: import java.io.Reader;
  50: import java.io.Serializable;
  51: import java.io.StringReader;
  52: import java.io.Writer;
  53: 
  54: import javax.accessibility.Accessible;
  55: import javax.accessibility.AccessibleContext;
  56: 
  57: import javax.swing.Action;
  58: import javax.swing.JEditorPane;
  59: import javax.swing.text.AbstractDocument;
  60: import javax.swing.text.BadLocationException;
  61: import javax.swing.text.BoxView;
  62: import javax.swing.text.ComponentView;
  63: import javax.swing.text.Document;
  64: import javax.swing.text.EditorKit;
  65: import javax.swing.text.Element;
  66: import javax.swing.text.IconView;
  67: import javax.swing.text.LabelView;
  68: import javax.swing.text.MutableAttributeSet;
  69: import javax.swing.text.ParagraphView;
  70: import javax.swing.text.StyleConstants;
  71: import javax.swing.text.StyleContext;
  72: import javax.swing.text.StyledEditorKit;
  73: import javax.swing.text.TextAction;
  74: import javax.swing.text.View;
  75: import javax.swing.text.ViewFactory;
  76: import javax.swing.text.html.parser.ParserDelegator;
  77: 
  78: /**
  79:  * @author Lillian Angel (langel at redhat dot com)
  80:  */
  81: public class HTMLEditorKit
  82:   extends StyledEditorKit
  83:   implements Serializable, Cloneable, Accessible
  84: {
  85:   
  86:   /**
  87:    * Fires the hyperlink events on the associated component
  88:    * when needed.
  89:    */
  90:   public static class LinkController
  91:     extends MouseAdapter
  92:     implements MouseMotionListener, Serializable
  93:     {
  94:       
  95:       /**
  96:        * Constructor
  97:        */
  98:       public LinkController() 
  99:       {
 100:         super();
 101:       }
 102:       
 103:       /**
 104:        * Dispatched when the mouse is clicked. If the component
 105:        * is read-only, then the clicked event is used to drive an
 106:        * attempt to follow the reference specified by a link
 107:        * 
 108:        * @param e - the mouse event
 109:        */
 110:       public void mouseClicked(MouseEvent e)
 111:       {
 112:         /*
 113:          These MouseInputAdapter methods generate mouse appropriate events around
 114:          hyperlinks (entering, exiting, and activating).
 115:          */
 116:         // FIXME: Not implemented.
 117:       }
 118:       
 119:       /**
 120:        * Dispatched when the mouse is dragged on a component.
 121:        * 
 122:        * @param e - the mouse event.
 123:        */
 124:       public void mouseDragged(MouseEvent e)
 125:       {
 126:         /*
 127:         These MouseInputAdapter methods generate mouse appropriate events around
 128:         hyperlinks (entering, exiting, and activating).
 129:         */
 130:         // FIXME: Not implemented.     
 131:       }
 132:       
 133:       /**
 134:        * Dispatched when the mouse cursor has moved into the component.
 135:        * 
 136:        * @param e - the mouse event.
 137:        */
 138:       public void mouseMoved(MouseEvent e)
 139:       {
 140:         /*
 141:         These MouseInputAdapter methods generate mouse appropriate events around
 142:         hyperlinks (entering, exiting, and activating).
 143:         */
 144:         // FIXME: Not implemented.
 145:       }
 146:       
 147:       /**
 148:        * If the given position represents a link, then linkActivated is called
 149:        * on the JEditorPane. Implemented to forward to the method with the same
 150:        * name, but pos == editor == -1.
 151:        * 
 152:        * @param pos - the position
 153:        * @param editor - the editor pane
 154:        */
 155:       protected void activateLink(int pos,
 156:                                   JEditorPane editor)
 157:       {
 158:         /*
 159:           This method creates and fires a HyperlinkEvent if the document is an
 160:           instance of HTMLDocument and the href tag of the link is not null.
 161:          */
 162:         // FIXME: Not implemented.
 163:       }
 164:     }
 165:   
 166:   /**
 167:    * This class is used to insert a string of HTML into an existing
 168:    * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
 169:    * identifies the parent in the document to add the elements to. The second, (addTag), 
 170:    * identifies that the first tag should be added to the document as seen in the string.
 171:    * The parser will generate all appropriate (opening/closing tags_ even if they are not
 172:    * in the HTML string passed in.
 173:    */
 174:   public static class InsertHTMLTextAction
 175:     extends HTMLTextAction
 176:     {
 177:       
 178:       /**
 179:        * Tag in HTML to start adding tags from.
 180:        */
 181:       protected HTML.Tag addTag;
 182:       
 183:       /**
 184:        * Alternate tag in HTML to start adding tags from if parentTag is
 185:        * not found and alternateParentTag is not found.
 186:        */      
 187:       protected HTML.Tag alternateAddTag;
 188:       
 189:       /**
 190:        * Alternate tag to check if parentTag is not found.
 191:        */
 192:       protected HTML.Tag alternateParentTag;
 193:       
 194:       /**
 195:        * HTML to insert.
 196:        */
 197:       protected String html;
 198:       
 199:       /**
 200:        * Tag to check for in the document.
 201:        */
 202:       protected HTML.Tag parentTag;
 203:       
 204:       /**
 205:        * Initializes all fields.
 206:        * 
 207:        * @param name - the name of the document.
 208:        * @param html - the html to insert
 209:        * @param parentTag - the parent tag to check for
 210:        * @param addTag - the tag to start adding from
 211:        */
 212:       public InsertHTMLTextAction(String name, String html, 
 213:                                   HTML.Tag parentTag, HTML.Tag addTag)
 214:       {
 215:         this(name, html, parentTag, addTag, null, null);
 216:       }
 217:       
 218:       /**
 219:        * Initializes all fields and calls super
 220:        * 
 221:        * @param name - the name of the document.
 222:        * @param html - the html to insert
 223:        * @param parentTag - the parent tag to check for
 224:        * @param addTag - the tag to start adding from
 225:        * @param alternateParentTag - the alternate parent tag
 226:        * @param alternateAddTag - the alternate add tag
 227:        */
 228:       public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, 
 229:                                   HTML.Tag addTag, HTML.Tag alternateParentTag, 
 230:                                   HTML.Tag alternateAddTag) 
 231:       {
 232:         super(name);
 233:         // Fields are for easy access when the action is applied to an actual
 234:         // document.
 235:         this.html = html;
 236:         this.parentTag = parentTag;
 237:         this.addTag = addTag;
 238:         this.alternateParentTag = alternateParentTag;
 239:         this.alternateAddTag = alternateAddTag;
 240:       }
 241:       
 242:       /**
 243:        * HTMLEditorKit.insertHTML is called. If an exception is
 244:        * thrown, it is wrapped in a RuntimeException and thrown.
 245:        * 
 246:        * @param editor - the editor to use to get the editorkit
 247:        * @param doc -
 248:        *          the Document to insert the HTML into.
 249:        * @param offset -
 250:        *          where to begin inserting the HTML.
 251:        * @param html -
 252:        *          the String to insert
 253:        * @param popDepth -
 254:        *          the number of ElementSpec.EndTagTypes to generate before
 255:        *          inserting
 256:        * @param pushDepth -
 257:        *          the number of ElementSpec.StartTagTypes with a direction of
 258:        *          ElementSpec.JoinNextDirection that should be generated before
 259:        * @param addTag -
 260:        *          the first tag to start inserting into document
 261:        */
 262:       protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
 263:                               String html, int popDepth, int pushDepth,
 264:                               HTML.Tag addTag)
 265:       {
 266:         try
 267:           {
 268:             super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
 269:                                                       popDepth, pushDepth, addTag);
 270:           }
 271:         catch (IOException e)
 272:           {
 273:             throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
 274:           }
 275:         catch (BadLocationException ex)
 276:           {
 277:             throw (RuntimeException) new RuntimeException("BadLocationException: "
 278:                                               + offset).initCause(ex);
 279:           }
 280:       }
 281:       
 282:       /**
 283:        * Invoked when inserting at a boundary. Determines the number of pops,
 284:        * and then the number of pushes that need to be performed. The it calls
 285:        * insertHTML.
 286:        * 
 287:        * @param editor -
 288:        *          the editor to use to get the editorkit
 289:        * @param doc -
 290:        *          the Document to insert the HTML into.
 291:        * @param offset -
 292:        *          where to begin inserting the HTML.
 293:        * @param insertElement -
 294:        *          the element to insert
 295:        * @param html -
 296:        *          the html to insert
 297:        * @param parentTag -
 298:        *          the parent tag
 299:        * @param addTag -
 300:        *          the first tag
 301:        */
 302:       protected void insertAtBoundary(JEditorPane editor,
 303:                                       HTMLDocument doc, int offset,
 304:                                       Element insertElement,
 305:                                       String html, HTML.Tag parentTag,
 306:                                       HTML.Tag addTag)
 307:       {
 308:         /*
 309:         As its name implies, this protected method is used when HTML is inserted at a
 310:         boundary. (A boundary in this case is an offset in doc that exactly matches the
 311:         beginning offset of the parentTag.) It performs the extra work required to keep
 312:         the tag stack in shape and then calls insertHTML(). The editor and doc argu-
 313:         ments are the editor pane and document where the HTML should go. The offset
 314:         argument represents the cursor location or selection start in doc. The insert-
 315:         Element and parentTag arguments are used to calculate the proper number of
 316:         tag pops and pushes before inserting the HTML (via html and addTag, which are
 317:         passed directly to insertHTML()).
 318:         */
 319:         // FIXME: not implemented
 320:       }
 321:       
 322:       /**
 323:        * Invoked when inserting at a boundary. Determines the number of pops, 
 324:        * and then the number of pushes that need to be performed. The it calls
 325:        * insertHTML.
 326:        * 
 327:        * @param editor - the editor to use to get the editorkit
 328:        * @param doc -
 329:        *          the Document to insert the HTML into.
 330:        * @param offset -
 331:        *          where to begin inserting the HTML.
 332:        * @param insertElement - the element to insert
 333:        * @param html - the html to insert
 334:        * @param parentTag - the parent tag
 335:        * @param addTag - the first tag
 336:        * 
 337:        * @deprecated as of v1.3, use insertAtBoundary
 338:        */
 339:       protected void insertAtBoundry(JEditorPane editor,
 340:                                      HTMLDocument doc,
 341:                                      int offset, Element insertElement,
 342:                                      String html, HTML.Tag parentTag,
 343:                                      HTML.Tag addTag)
 344:       {
 345:         insertAtBoundary(editor, doc, offset, insertElement,
 346:                          html, parentTag, addTag);
 347:       }
 348:       
 349:       /**
 350:        * Inserts the HTML.
 351:        * 
 352:        * @param ae - the action performed
 353:        */
 354:       public void actionPerformed(ActionEvent ae)
 355:       {
 356:         Object source = ae.getSource();
 357:         if (source instanceof JEditorPane)
 358:           {
 359:             JEditorPane pane = ((JEditorPane) source);
 360:             Document d = pane.getDocument();
 361:             if (d instanceof HTMLDocument)
 362:               insertHTML(pane, (HTMLDocument) d, 0, html, 0, 0, addTag);
 363:             // FIXME: is this correct parameters?
 364:           }
 365:         // FIXME: else not implemented
 366:       }
 367:   }
 368:   
 369:   /**
 370:    * Abstract Action class that helps inserting HTML into an existing document.
 371:    */
 372:   public abstract static class HTMLTextAction
 373:     extends StyledEditorKit.StyledTextAction
 374:     {
 375:       
 376:       /**
 377:        * Constructor
 378:        */
 379:       public HTMLTextAction(String name) 
 380:       {
 381:         super(name);
 382:       }
 383:       
 384:       /**
 385:        * Gets the HTMLDocument from the JEditorPane.
 386:        * 
 387:        * @param e - the editor pane
 388:        * @return the html document.
 389:        */
 390:       protected HTMLDocument getHTMLDocument(JEditorPane e)
 391:       {
 392:         Document d = e.getDocument();
 393:         if (d instanceof HTMLDocument)
 394:           return (HTMLDocument) d;
 395:         throw new IllegalArgumentException("Document is not a HTMLDocument.");
 396:       }
 397:       
 398:       /**
 399:        * Gets the HTMLEditorKit
 400:        *  
 401:        * @param e - the JEditorPane to get the HTMLEditorKit from.
 402:        * @return the HTMLEditorKit
 403:        */
 404:       protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) 
 405:       {
 406:         EditorKit d = e.getEditorKit();
 407:         if (d instanceof HTMLEditorKit)
 408:           return (HTMLEditorKit) d;
 409:         throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
 410:       }
 411:       
 412:       /**
 413:        * Returns an array of Elements that contain the offset.
 414:        * The first elements corresponds to the roots of the doc.
 415:        * 
 416:        * @param doc - the document to get the Elements from.
 417:        * @param offset - the offset the Elements must contain
 418:        * @return an array of all the elements containing the offset.
 419:        */
 420:       protected Element[] getElementsAt(HTMLDocument doc,
 421:                                         int offset)
 422:       {
 423:         return getElementsAt(doc.getDefaultRootElement(), offset, 0);
 424:       }
 425:       
 426:       /**
 427:        * Helper function to get all elements using recursion.
 428:        */
 429:       private Element[] getElementsAt(Element root, int offset, int depth)
 430:       {
 431:         Element[] elements = null;
 432:         if (root != null)
 433:           {
 434:             if (root.isLeaf())
 435:               {
 436:                 elements = new Element[depth + 1];
 437:                 elements[depth] = root;
 438:                 return elements;
 439:               }
 440:             elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
 441:                                      offset, depth + 1);
 442:             elements[depth] = root;
 443:           }
 444:         return elements;
 445:       }
 446:       
 447:       /**
 448:        * Returns the number of elements, starting at the deepest point, needed
 449:        * to get an element representing tag. -1 if no elements are found, 0 if
 450:        * the parent of the leaf at offset represents the tag.
 451:        * 
 452:        * @param doc -
 453:        *          the document to search
 454:        * @param offset -
 455:        *          the offset to check
 456:        * @param tag -
 457:        *          the tag to look for
 458:        * @return - the number of elements needed to get an element representing
 459:        *         tag.
 460:        */
 461:       protected int elementCountToTag(HTMLDocument doc,
 462:                                       int offset, HTML.Tag tag)
 463:       {
 464:         Element root = doc.getDefaultRootElement();
 465:         int num = -1;
 466:         Element next = root.getElement(root.getElementIndex(offset));
 467:         
 468:         while (!next.isLeaf())
 469:           {
 470:             num++;
 471:             if (next.getAttributes().
 472:                 getAttribute(StyleConstants.NameAttribute).equals(tag))
 473:               return num;
 474:             next = next.getElement(next.getElementIndex(offset));
 475:           }
 476:         return num;
 477:       }
 478:       
 479:       /**
 480:        * Gets the deepest element at offset with the
 481:        * matching tag.
 482:        * 
 483:        * @param doc - the document to search
 484:        * @param offset - the offset to check for
 485:        * @param tag - the tag to match
 486:        * @return - the element that is found, null if not found.
 487:        */
 488:       protected Element findElementMatchingTag(HTMLDocument doc,
 489:                                                int offset, HTML.Tag tag)
 490:       {
 491:         Element element = doc.getDefaultRootElement();
 492:         Element tagElement = null;
 493:         
 494:         while (element != null)
 495:           {
 496:             Object otag = element.getAttributes().getAttribute(
 497:                                      StyleConstants.NameAttribute);
 498:             if (otag instanceof HTML.Tag && otag.equals(tag))
 499:               tagElement = element;
 500:             element = element.getElement(element.getElementIndex(offset));
 501:           }
 502:         
 503:         return tagElement;
 504:       }
 505:     }
 506:   
 507:   /**
 508:    * A {@link ViewFactory} that is able to create {@link View}s for
 509:    * the <code>Element</code>s that are supported.
 510:    */
 511:   public static class HTMLFactory
 512:     implements ViewFactory
 513:   {
 514:     
 515:     /**
 516:      * Constructor
 517:      */
 518:     public HTMLFactory()
 519:     {
 520:       // Do Nothing here.
 521:     }
 522:     
 523:     /**
 524:      * Creates a {@link View} for the specified <code>Element</code>.
 525:      *
 526:      * @param element the <code>Element</code> to create a <code>View</code>
 527:      *        for
 528:      * @return the <code>View</code> for the specified <code>Element</code>
 529:      *         or <code>null</code> if the type of <code>element</code> is
 530:      *         not supported
 531:      */
 532:     public View create(Element element)
 533:     {
 534:       View view = null;
 535:       Object attr = element.getAttributes().getAttribute(
 536:                                 StyleConstants.NameAttribute);
 537:       if (attr instanceof HTML.Tag)
 538:         {
 539:           HTML.Tag tag = (HTML.Tag) attr;
 540: 
 541:           if (tag.equals(HTML.Tag.IMPLIED) || tag.equals(HTML.Tag.P)
 542:               || tag.equals(HTML.Tag.H1) || tag.equals(HTML.Tag.H2)
 543:               || tag.equals(HTML.Tag.H3) || tag.equals(HTML.Tag.H4)
 544:               || tag.equals(HTML.Tag.H5) || tag.equals(HTML.Tag.H6)
 545:               || tag.equals(HTML.Tag.DT))
 546:             view = new ParagraphView(element);
 547:           else if (tag.equals(HTML.Tag.LI) || tag.equals(HTML.Tag.DL)
 548:                    || tag.equals(HTML.Tag.DD) || tag.equals(HTML.Tag.BODY)
 549:                    || tag.equals(HTML.Tag.HTML) || tag.equals(HTML.Tag.CENTER)
 550:                    || tag.equals(HTML.Tag.DIV)
 551:                    || tag.equals(HTML.Tag.BLOCKQUOTE)
 552:                    || tag.equals(HTML.Tag.PRE))
 553:             view = new BlockView(element, View.Y_AXIS);
 554:           
 555:           // FIXME: Uncomment when the views have been implemented
 556:          /* else if (tag.equals(HTML.Tag.CONTENT))
 557:             view = new InlineView(element); 
 558:           else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR)
 559:                    || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL))
 560:             view = new ListView(element);
 561:           else if (tag.equals(HTML.Tag.IMG))
 562:             view = new ImageView(element);
 563:           else if (tag.equals(HTML.Tag.HR))
 564:             view = new HRuleView(element);
 565:           else if (tag.equals(HTML.Tag.BR))
 566:             view = new BRView(element);
 567:           else if (tag.equals(HTML.Tag.TABLE))
 568:             view = new TableView(element);
 569:           else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT)
 570:                    || tag.equals(HTML.Tag.TEXTAREA))
 571:             view = new FormView(element);
 572:           else if (tag.equals(HTML.Tag.OBJECT))
 573:             view = new ObjectView(element);
 574:           else if (tag.equals(HTML.Tag.FRAMESET))
 575:             view = new FrameSetView(element);
 576:           else if (tag.equals(HTML.Tag.FRAME))
 577:             view = new FrameView(element); */
 578:         }      
 579:       
 580:       if (view == null)
 581:         {
 582:           String name = element.getName();
 583:           if (name.equals(AbstractDocument.ContentElementName))
 584:             view = new LabelView(element);
 585:           else if (name.equals(AbstractDocument.ParagraphElementName))
 586:             view = new ParagraphView(element);
 587:           else if (name.equals(AbstractDocument.SectionElementName))
 588:             view = new BoxView(element, View.Y_AXIS);
 589:           else if (name.equals(StyleConstants.ComponentElementName))
 590:             view = new ComponentView(element);
 591:           else if (name.equals(StyleConstants.IconElementName))
 592:             view = new IconView(element);
 593:         }
 594:       return view;
 595:     }
 596:   }
 597:   
 598:   /**
 599:    * The abstract HTML parser declaration.
 600:    */
 601:   public abstract static class Parser
 602:   {
 603:     /**
 604:      * Parse the HTML text, calling various methods of the provided callback
 605:      * in response to the occurence of the corresponding HTML constructions.
 606:      * @param reader The reader to read the source HTML from.
 607:      * @param callback The callback to receive information about the parsed
 608:      * HTML structures
 609:      * @param ignoreCharSet If true, the parser ignores all charset information
 610:      * that may be present in HTML documents.
 611:      * @throws IOException, normally if the reader throws one.
 612:      */
 613:     public abstract void parse(Reader reader, ParserCallback callback,
 614:                                boolean ignoreCharSet) throws IOException;
 615:   }
 616: 
 617:   /**
 618:    * The "hook" that receives all information about the HTML document
 619:    * structure while parsing it. The methods are invoked by parser
 620:    * and should be normally overridden.
 621:    */
 622:   public static class ParserCallback
 623:   {
 624:     /**
 625:      * If the tag does not occurs in the html stream directly, but
 626:      * is supposed by parser, the tag attribute set contains this additional
 627:      * attribute, having value Boolean.True.
 628:      */
 629:     public static final Object IMPLIED = "_implied_";
 630: 
 631:     /**
 632:      * Constructor
 633:      */
 634:     public ParserCallback()
 635:     {
 636:       // Nothing to do here.
 637:     }
 638:     
 639:     /**
 640:      * The parser calls this method after it finishes parsing the document.
 641:      */
 642:     public void flush() throws BadLocationException
 643:     {
 644:       // Nothing to do here.
 645:     }
 646: 
 647:     /**
 648:      * Handle HTML comment, present in the given position.
 649:      * @param comment the comment
 650:      * @position the position of the comment in the text being parsed.
 651:      */
 652:     public void handleComment(char[] comment, int position)
 653:     {
 654:       // Nothing to do here.
 655:     }
 656: 
 657:     /**
 658:      * Notifies about the character sequences, used to separate lines in
 659:      * this document. The parser calls this method after it finishes
 660:      * parsing the document, but before flush().
 661:      * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
 662:      */
 663:     public void handleEndOfLineString(String end_of_line)
 664:     {
 665:       // Nothing to do here.
 666:     }
 667: 
 668:     /**
 669:      * The method is called when the HTML closing tag ((like &lt;/table&gt;)
 670:      * is found or if the parser concludes that the one should be present
 671:      * in the current position.
 672:      * @param tag The tag being handled
 673:      * @param position the tag position in the text being parsed.
 674:      */
 675:     public void handleEndTag(HTML.Tag tag, int position)
 676:     {
 677:       // Nothing to do here.
 678:     }
 679: 
 680:     /**
 681:      * Handle the error.
 682:      * @param message The message, explaining the error.
 683:      * @param position The starting position of the fragment that has caused
 684:      * the error in the html document being parsed.
 685:      */
 686:     public void handleError(String message, int position)
 687:     {
 688:       // Nothing to do here.
 689:     }
 690: 
 691:     /**
 692:      * Handle the tag with no content, like &lt;br&gt;. The method is
 693:      * called for the elements that, in accordance with the current DTD,
 694:      * has an empty content.
 695:      * @param tag The tag being handled.
 696:      * @param position The tag position in the text being parsed.
 697:      */
 698:     public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
 699:                                 int position)
 700:     {
 701:       // Nothing to do here.
 702:     }
 703: 
 704:     /**
 705:      * The method is called when the HTML opening tag ((like &lt;table&gt;)
 706:      * is found or if the parser concludes that the one should be present
 707:      * in the current position.
 708:      * @param tag The tag being handled
 709:      * @param position The tag position in the text being parsed
 710:      */
 711:     public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
 712:                                int position)
 713:     {
 714:       // Nothing to do here.
 715:     }
 716: 
 717:     /**
 718:      * Handle the text section.
 719:      * @param text A section text.
 720:      * @param position The text position in the HTML document text being parsed.
 721:      */
 722:     public void handleText(char[] text, int position)
 723:     {
 724:       // Nothing to do here.
 725:     }
 726:   }
 727: 
 728:   /**
 729:    * Use serialVersionUID (v1.4) for interoperability.
 730:    */
 731:   private static final long serialVersionUID = 8751997116710384592L;
 732: 
 733:   /**
 734:    * Default cascading stylesheed file ("default.css").
 735:    */
 736:   public static final String DEFAULT_CSS = "default.css";
 737: 
 738:   /**
 739:    * The <b>bold</b> action identifier.
 740:    */
 741:   public static final String BOLD_ACTION = "html-bold-action";
 742: 
 743:   /**
 744:    * The <i>italic</i> action identifier.
 745:    */
 746:   public static final String ITALIC_ACTION = "html-italic-action";
 747: 
 748:   /**
 749:    * The <font color="#FF0000">color</font> action indentifier
 750:    * (passing the color as an argument).
 751:    */
 752:   public static final String COLOR_ACTION = "html-color-action";
 753: 
 754:   /**
 755:    * The <font size="+1">increase</font> font action identifier.
 756:    */
 757:   public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
 758: 
 759:   /**
 760:    * The <font size="-1">decrease</font> font action identifier.
 761:    */
 762:   public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
 763: 
 764:   /**
 765:    * Align images at the bottom.
 766:    */
 767:   public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
 768: 
 769:   /**
 770:    * Align images at the middle.
 771:    */
 772:   public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
 773: 
 774:   /**
 775:    * Align images at the top.
 776:    */
 777:   public static final String IMG_ALIGN_TOP = "html-image-align-top";
 778: 
 779:   /**
 780:    * Align images at the border.
 781:    */
 782:   public static final String IMG_BORDER = "html-image-border";
 783: 
 784:   /**
 785:    * The "logical style" action identifier, passing that style as parameter.
 786:    */
 787:   public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
 788: 
 789:   /**
 790:    * The "ident paragraph left" action.
 791:    */
 792:   public static final String PARA_INDENT_LEFT = "html-para-indent-left";
 793: 
 794:   /**
 795:    * The "ident paragraph right" action.
 796:    */
 797:   public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
 798:   
 799:   /**
 800:    * Actions for HTML 
 801:    */
 802:   private static final Action[] defaultActions = {
 803:     // FIXME: Add default actions for html
 804:   };
 805:   
 806:   /**
 807:    * The current style sheet.
 808:    */
 809:   StyleSheet styleSheet;
 810:   
 811:   /**
 812:    * The ViewFactory for HTMLFactory.
 813:    */
 814:   HTMLFactory viewFactory;
 815:   
 816:   /**
 817:    * The Cursor for links.
 818:    */
 819:   Cursor linkCursor;
 820:   
 821:   /**
 822:    * The default cursor.
 823:    */
 824:   Cursor defaultCursor;
 825:   
 826:   /**
 827:    * The parser.
 828:    */
 829:   Parser parser;
 830:   
 831:   /**
 832:    * The mouse listener used for links.
 833:    */
 834:   LinkController mouseListener;
 835:   
 836:   /**
 837:    * Style context for this editor.
 838:    */
 839:   StyleContext styleContext;
 840:   
 841:   /** The content type */
 842:   String contentType = "text/html";
 843:   
 844:   /** The input attributes defined by default.css */
 845:   MutableAttributeSet inputAttributes;
 846:   
 847:   /** The editor pane used. */
 848:   JEditorPane editorPane;
 849:     
 850:   /**
 851:    * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
 852:    */
 853:   public HTMLEditorKit()
 854:   {
 855:     super();    
 856:     styleContext = new StyleContext();
 857:     styleSheet = new StyleSheet();
 858:     styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
 859:     // FIXME: Set inputAttributes with default.css    
 860:   }
 861:   
 862:   /**
 863:    * Gets a factory suitable for producing views of any 
 864:    * models that are produced by this kit.
 865:    * 
 866:    * @return the view factory suitable for producing views.
 867:    */
 868:   public ViewFactory getViewFactory()
 869:   {
 870:     if (viewFactory == null)
 871:       viewFactory = new HTMLFactory();
 872:     return viewFactory;
 873:   }
 874:   
 875:   /**
 876:    * Create a text storage model for this type of editor.
 877:    *
 878:    * @return the model
 879:    */
 880:   public Document createDefaultDocument()
 881:   {
 882:     HTMLDocument document = new HTMLDocument(getStyleSheet());
 883:     document.setParser(getParser());
 884:     return document;
 885:   }
 886: 
 887:   /**
 888:    * Get the parser that this editor kit uses for reading HTML streams. This
 889:    * method can be overridden to use the alternative parser.
 890:    * 
 891:    * @return the HTML parser (by default, {@link ParserDelegator}).
 892:    */
 893:   protected Parser getParser()
 894:   {
 895:     if (parser == null)
 896:       parser = new ParserDelegator();
 897:     return parser;
 898:   }
 899:   
 900:   /**
 901:    * Inserts HTML into an existing document.
 902:    * 
 903:    * @param doc - the Document to insert the HTML into.
 904:    * @param offset - where to begin inserting the HTML.
 905:    * @param html - the String to insert
 906:    * @param popDepth - the number of ElementSpec.EndTagTypes 
 907:    * to generate before inserting
 908:    * @param pushDepth - the number of ElementSpec.StartTagTypes 
 909:    * with a direction of ElementSpec.JoinNextDirection that 
 910:    * should be generated before
 911:    * @param insertTag - the first tag to start inserting into document
 912:    * @throws IOException - on any I/O error
 913:    * @throws BadLocationException - if pos represents an invalid location
 914:    * within the document
 915:    */
 916:   public void insertHTML(HTMLDocument doc, int offset, String html,
 917:                          int popDepth, int pushDepth, HTML.Tag insertTag)
 918:       throws BadLocationException, IOException
 919:   {
 920:     Parser parser = getParser();
 921:     if (offset < 0 || offset > doc.getLength())
 922:       throw new BadLocationException("Bad location", offset);
 923:     if (parser == null)
 924:       throw new IOException("Parser is null.");
 925: 
 926:     ParserCallback pc = ((HTMLDocument) doc).getReader
 927:                           (offset, popDepth, pushDepth, insertTag);
 928: 
 929:     // FIXME: What should ignoreCharSet be set to?
 930:     
 931:     // parser.parse inserts html into the buffer
 932:     parser.parse(new StringReader(html), pc, false);
 933:     pc.flush();
 934:   }
 935:   
 936:   /**
 937:    * Inserts content from the given stream. Inserting HTML into a non-empty 
 938:    * document must be inside the body Element, if you do not insert into 
 939:    * the body an exception will be thrown. When inserting into a non-empty 
 940:    * document all tags outside of the body (head, title) will be dropped.
 941:    * 
 942:    * @param in - the stream to read from
 943:    * @param doc - the destination for the insertion
 944:    * @param pos - the location in the document to place the content
 945:    * @throws IOException - on any I/O error
 946:    * @throws BadLocationException - if pos represents an invalid location
 947:    * within the document
 948:    */
 949:   public void read(Reader in, Document doc, int pos) throws IOException,
 950:       BadLocationException
 951:   {
 952:     if (doc instanceof HTMLDocument)
 953:       {
 954:         Parser parser = getParser();
 955:         if (pos < 0 || pos > doc.getLength())
 956:           throw new BadLocationException("Bad location", pos);
 957:         if (parser == null)
 958:           throw new IOException("Parser is null.");
 959:         
 960:         HTMLDocument hd = ((HTMLDocument) doc);
 961:         hd.setBase(editorPane.getPage());
 962:         ParserCallback pc = hd.getReader(pos);
 963:         
 964:         // FIXME: What should ignoreCharSet be set to?
 965:         
 966:         // parser.parse inserts html into the buffer
 967:         parser.parse(in, pc, false);
 968:         pc.flush();
 969:       }
 970:     else
 971:       // read in DefaultEditorKit is called.
 972:       // the string is inserted in the document as usual.
 973:       super.read(in, doc, pos);
 974:   }
 975:   
 976:   /**
 977:    * Writes content from a document to the given stream in 
 978:    * an appropriate format.
 979:    * 
 980:    * @param out - the stream to write to
 981:    * @param doc - the source for the write
 982:    * @param pos - the location in the document to get the content.
 983:    * @param len - the amount to write out
 984:    * @throws IOException - on any I/O error
 985:    * @throws BadLocationException - if pos represents an invalid location
 986:    * within the document
 987:    */
 988:   public void write(Writer out, Document doc, int pos, int len)
 989:       throws IOException, BadLocationException
 990:   {
 991:     if (doc instanceof HTMLDocument)
 992:       {
 993:         // FIXME: Not implemented. Use HTMLWriter.
 994:         out.write(doc.getText(pos, len));
 995:       }
 996:     else
 997:       super.write(out, doc, pos, len);
 998:   }
 999:   
1000:   /**
1001:    * Gets the content type that the kit supports.
1002:    * This kit supports the type text/html.
1003:    * 
1004:    * @returns the content type supported.
1005:    */
1006:   public String getContentType()
1007:   {
1008:     return contentType;
1009:   } 
1010:   
1011:   /**
1012:    * Creates a copy of the editor kit.
1013:    * 
1014:    * @return a copy of this.
1015:    */
1016:   public Object clone()
1017:   {
1018:     // FIXME: Need to clone all fields
1019:     return (HTMLEditorKit) super.clone();
1020:   }
1021:   
1022:   /**
1023:    * Copies the key/values in elements AttributeSet into set. 
1024:    * This does not copy component, icon, or element names attributes.
1025:    * This is called anytime the caret moves over a different location. 
1026:    * 
1027:    * @param element - the element to create the input attributes for.
1028:    * @param set - the set to copy the values into.
1029:    */
1030:   protected void createInputAttributes(Element element,
1031:                                        MutableAttributeSet set)
1032:   {
1033:     set.removeAttributes(set);
1034:     set.addAttributes(element.getAttributes());
1035:     // FIXME: Not fully implemented.
1036:   }
1037:   
1038:   /**
1039:    * Called when this is installed into the JEditorPane.
1040:    * 
1041:    * @param c - the JEditorPane installed into.
1042:    */
1043:   public void install(JEditorPane c)
1044:   {
1045:     super.install(c);
1046:     mouseListener = new LinkController();
1047:     c.addMouseListener(mouseListener);
1048:     editorPane = c;
1049:     // FIXME: need to set up hyperlinklistener object
1050:   }
1051:   
1052:   /**
1053:    * Called when the this is removed from the JEditorPane.
1054:    * It unregisters any listeners.
1055:    * 
1056:    * @param c - the JEditorPane being removed from.
1057:    */
1058:   public void deinstall(JEditorPane c)
1059:   {
1060:     super.deinstall(c);
1061:     c.removeMouseListener(mouseListener);
1062:     mouseListener = null;
1063:     editorPane = null;
1064:   }
1065:   
1066:   /**
1067:    * Gets the AccessibleContext associated with this.
1068:    * 
1069:    * @return the AccessibleContext for this.
1070:    */
1071:   public AccessibleContext getAccessibleContext()
1072:   {
1073:     // FIXME: Should return an instance of 
1074:     // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
1075:     // Not implemented yet.
1076:     return null;
1077:   }
1078:   
1079:   /**
1080:    * Gets the action list. This list is supported by the superclass
1081:    * augmented by the collection of actions defined locally for style
1082:    * operations.
1083:    * 
1084:    * @return an array of all the actions
1085:    */
1086:   public Action[] getActions()
1087:   {
1088:     return TextAction.augmentList(super.getActions(), defaultActions);
1089:   }
1090:   
1091:   /**
1092:    * Returns the default cursor.
1093:    * 
1094:    * @return the default cursor
1095:    */
1096:   public Cursor getDefaultCursor()
1097:   {
1098:     if (defaultCursor == null)
1099:       defaultCursor = Cursor.getDefaultCursor();
1100:     return defaultCursor;
1101:   }
1102:   
1103:   /**
1104:    * Returns the cursor for links.
1105:    * 
1106:    * @return the cursor for links.
1107:    */
1108:   public Cursor getLinkCursor()
1109:   {
1110:     if (linkCursor == null)
1111:       linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
1112:     return linkCursor;
1113:   }
1114:   
1115:   /**
1116:    * Sets the Cursor for links.
1117:    * 
1118:    * @param cursor - the new cursor for links.
1119:    */
1120:   public void setLinkCursor(Cursor cursor)
1121:   {
1122:     linkCursor = cursor;
1123:   }
1124:   
1125:   /**
1126:    * Sets the default cursor.
1127:    * 
1128:    * @param cursor - the new default cursor.
1129:    */
1130:   public void setDefaultCursor(Cursor cursor)
1131:   {
1132:     defaultCursor = cursor;
1133:   }
1134:   
1135:   /**
1136:    * Gets the input attributes used for the styled editing actions.
1137:    * 
1138:    * @return the attribute set
1139:    */
1140:   public MutableAttributeSet getInputAttributes()
1141:   {
1142:     return inputAttributes;
1143:   }
1144:   
1145:   /**
1146:    * Get the set of styles currently being used to render the HTML elements. 
1147:    * By default the resource specified by DEFAULT_CSS gets loaded, and is 
1148:    * shared by all HTMLEditorKit instances.
1149:    * 
1150:    * @return the style sheet.
1151:    */
1152:   public StyleSheet getStyleSheet()
1153:   {
1154:     if (styleSheet == null)
1155:       {
1156:         styleSheet = new StyleSheet();
1157:         styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
1158:       }
1159:     return styleSheet;
1160:   }
1161:   
1162:   /**
1163:    * Set the set of styles to be used to render the various HTML elements. 
1164:    * These styles are specified in terms of CSS specifications. Each document 
1165:    * produced by the kit will have a copy of the sheet which it can add the 
1166:    * document specific styles to. By default, the StyleSheet specified is shared 
1167:    * by all HTMLEditorKit instances. 
1168:    * 
1169:    * @param s - the new style sheet
1170:    */
1171:   public void setStyleSheet(StyleSheet s)
1172:   {
1173:     styleSheet = s;
1174:   }
1175: }