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