OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / html / HTMLEditorKit.java
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 import java.awt.Point;
48
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.InputStreamReader;
52 import java.io.Reader;
53 import java.io.Serializable;
54 import java.io.StringReader;
55 import java.io.Writer;
56 import java.net.MalformedURLException;
57 import java.net.URL;
58
59 import javax.accessibility.Accessible;
60 import javax.accessibility.AccessibleContext;
61
62 import javax.swing.Action;
63 import javax.swing.JEditorPane;
64 import javax.swing.SwingUtilities;
65 import javax.swing.event.HyperlinkEvent;
66 import javax.swing.text.AttributeSet;
67 import javax.swing.text.BadLocationException;
68 import javax.swing.text.Document;
69 import javax.swing.text.EditorKit;
70 import javax.swing.text.Element;
71 import javax.swing.text.MutableAttributeSet;
72 import javax.swing.text.StyleConstants;
73 import javax.swing.text.StyledDocument;
74 import javax.swing.text.StyledEditorKit;
75 import javax.swing.text.TextAction;
76 import javax.swing.text.View;
77 import javax.swing.text.ViewFactory;
78 import javax.swing.text.html.parser.ParserDelegator;
79
80 /* Move these imports here after javax.swing.text.html to make it compile
81    with jikes.  */
82 import gnu.javax.swing.text.html.parser.GnuParserDelegator;
83 import gnu.javax.swing.text.html.parser.HTML_401F;
84
85 /**
86  * @author Lillian Angel (langel at redhat dot com)
87  */
88 public class HTMLEditorKit
89   extends StyledEditorKit
90   implements Serializable, Cloneable, Accessible
91 {
92   
93   /**
94    * Fires the hyperlink events on the associated component
95    * when needed.
96    */
97   public static class LinkController
98     extends MouseAdapter
99     implements MouseMotionListener, Serializable
100     {
101
102       /**
103        * The element of the last anchor tag.
104        */
105       private Element lastAnchorElement;
106
107       /**
108        * Constructor
109        */
110       public LinkController() 
111       {
112         super();
113       }
114       
115       /**
116        * Dispatched when the mouse is clicked. If the component
117        * is read-only, then the clicked event is used to drive an
118        * attempt to follow the reference specified by a link
119        * 
120        * @param e - the mouse event
121        */
122       public void mouseClicked(MouseEvent e)
123       {
124         JEditorPane editor = (JEditorPane) e.getSource();
125         if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
126           {
127             Point loc = e.getPoint();
128             int pos = editor.viewToModel(loc);
129             if (pos >= 0)
130               activateLink(pos, editor, e.getX(), e.getY());
131           }
132       }
133       
134       /**
135        * Dispatched when the mouse is dragged on a component.
136        * 
137        * @param e - the mouse event.
138        */
139       public void mouseDragged(MouseEvent e)
140       {
141         // Nothing to do here.
142       }
143       
144       /**
145        * Dispatched when the mouse cursor has moved into the component.
146        * 
147        * @param e - the mouse event.
148        */
149       public void mouseMoved(MouseEvent e)
150       {
151         JEditorPane editor = (JEditorPane) e.getSource();
152         HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
153         if (! editor.isEditable())
154           {
155             Document doc = editor.getDocument();
156             if (doc instanceof HTMLDocument)
157               {
158                 Cursor newCursor = kit.getDefaultCursor();
159                 HTMLDocument htmlDoc = (HTMLDocument) doc;
160                 Point loc = e.getPoint();
161                 int pos = editor.viewToModel(loc);
162                 Element el = htmlDoc.getCharacterElement(pos);
163                 if (pos < el.getStartOffset() || pos >= el.getEndOffset())
164                   el = null;
165                 if (el != null)
166                   {
167                     AttributeSet aAtts = (AttributeSet)
168                                    el.getAttributes().getAttribute(HTML.Tag.A);
169                     if (aAtts != null)
170                       {
171                         if (el != lastAnchorElement)
172                           {
173                             if (lastAnchorElement != null)
174                               htmlDoc.updateSpecialClass(lastAnchorElement,
175                                                   HTML.Attribute.DYNAMIC_CLASS,
176                                                   null);
177                             lastAnchorElement = el;
178                             htmlDoc.updateSpecialClass(el,
179                                                   HTML.Attribute.DYNAMIC_CLASS,
180                                                   "hover");
181                           }
182                         newCursor = kit.getLinkCursor();
183                       }
184                     else
185                       {
186                         if (lastAnchorElement != null)
187                           htmlDoc.updateSpecialClass(lastAnchorElement,
188                                               HTML.Attribute.DYNAMIC_CLASS,
189                                               null);
190                         lastAnchorElement = null;
191                       }
192                   }
193                 else
194                   {
195                     if (lastAnchorElement != null)
196                       htmlDoc.updateSpecialClass(lastAnchorElement,
197                                           HTML.Attribute.DYNAMIC_CLASS,
198                                           null);
199                     lastAnchorElement = null;
200                   }
201                 if (editor.getCursor() != newCursor)
202                   {
203                     editor.setCursor(newCursor);
204                   }
205               }
206           }
207       }
208
209       /**
210        * If the given position represents a link, then linkActivated is called
211        * on the JEditorPane.
212        *
213        * @param pos the position
214        * @param editor the editor pane
215        */
216       protected void activateLink(int pos, JEditorPane editor)
217       {
218         activateLink(pos, editor);
219       }
220
221       private void activateLink(int pos, JEditorPane editor, int x, int y)
222       {
223         // TODO: This is here for future extension for mapped links support.
224         // For the time beeing we implement simple hyperlinks.
225         Document doc = editor.getDocument();
226         if (doc instanceof HTMLDocument)
227           {
228             HTMLDocument htmlDoc = (HTMLDocument) doc;
229             Element el = htmlDoc.getCharacterElement(pos);
230             AttributeSet atts = el.getAttributes();
231             AttributeSet anchorAtts =
232               (AttributeSet) atts.getAttribute(HTML.Tag.A);
233             String href = null;
234             if (anchorAtts != null)
235               {
236                 href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
237                 htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
238                                            "visited");
239               }
240             else
241               {
242                 // TODO: Implement link maps here.
243               }
244             HyperlinkEvent event = null;
245             if (href != null)
246               event = createHyperlinkEvent(editor, htmlDoc, href,
247                                            anchorAtts, el);
248             if (event != null)
249               editor.fireHyperlinkUpdate(event);
250           }
251         
252       }
253
254       /**
255        * Creates a HyperlinkEvent for the specified href and anchor if
256        * possible. If for some reason this won't work, return null.
257        *
258        * @param editor the editor
259        * @param doc the document
260        * @param href the href link
261        * @param anchor the anchor
262        * @param el the element
263        *
264        * @return the hyperlink event, or <code>null</code> if we couldn't
265        *         create one
266        */
267       private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
268                                                   HTMLDocument doc,
269                                                   String href,
270                                                   AttributeSet anchor,
271                                                   Element el)
272       {
273         URL url;
274         try
275           {
276             URL base = doc.getBase();
277             url = new URL(base, href);
278             
279           }
280         catch (MalformedURLException ex)
281           {
282             url = null;
283           }
284         HyperlinkEvent ev;
285         if (doc.isFrameDocument())
286           {
287             String target = null;
288             if (anchor != null)
289               target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
290             if (target == null || target.equals(""))
291               target = doc.getBaseTarget();
292             if (target == null || target.equals(""))
293               target = "_self";
294             ev = new HTMLFrameHyperlinkEvent(editor,
295                                             HyperlinkEvent.EventType.ACTIVATED,
296                                             url, href, el, target);
297           }
298         else
299           {
300             ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
301                                     url, href, el);
302           }
303         return ev;
304       }
305     }
306   
307   /**
308    * This class is used to insert a string of HTML into an existing
309    * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
310    * identifies the parent in the document to add the elements to. The second, (addTag), 
311    * identifies that the first tag should be added to the document as seen in the string.
312    * The parser will generate all appropriate (opening/closing tags_ even if they are not
313    * in the HTML string passed in.
314    */
315   public static class InsertHTMLTextAction
316     extends HTMLTextAction
317     {
318       
319       /**
320        * Tag in HTML to start adding tags from.
321        */
322       protected HTML.Tag addTag;
323       
324       /**
325        * Alternate tag in HTML to start adding tags from if parentTag is
326        * not found and alternateParentTag is not found.
327        */      
328       protected HTML.Tag alternateAddTag;
329       
330       /**
331        * Alternate tag to check if parentTag is not found.
332        */
333       protected HTML.Tag alternateParentTag;
334       
335       /**
336        * HTML to insert.
337        */
338       protected String html;
339       
340       /**
341        * Tag to check for in the document.
342        */
343       protected HTML.Tag parentTag;
344
345       /**
346        * Initializes all fields.
347        * 
348        * @param name - the name of the document.
349        * @param html - the html to insert
350        * @param parentTag - the parent tag to check for
351        * @param addTag - the tag to start adding from
352        */
353       public InsertHTMLTextAction(String name, String html, 
354                                   HTML.Tag parentTag, HTML.Tag addTag)
355       {
356         this(name, html, parentTag, addTag, null, null);
357       }
358       
359       /**
360        * Initializes all fields and calls super
361        * 
362        * @param name - the name of the document.
363        * @param html - the html to insert
364        * @param parentTag - the parent tag to check for
365        * @param addTag - the tag to start adding from
366        * @param alternateParentTag - the alternate parent tag
367        * @param alternateAddTag - the alternate add tag
368        */
369       public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, 
370                                   HTML.Tag addTag, HTML.Tag alternateParentTag, 
371                                   HTML.Tag alternateAddTag) 
372       {
373         super(name);
374         // Fields are for easy access when the action is applied to an actual
375         // document.
376         this.html = html;
377         this.parentTag = parentTag;
378         this.addTag = addTag;
379         this.alternateParentTag = alternateParentTag;
380         this.alternateAddTag = alternateAddTag;
381       }
382       
383       /**
384        * HTMLEditorKit.insertHTML is called. If an exception is
385        * thrown, it is wrapped in a RuntimeException and thrown.
386        * 
387        * @param editor - the editor to use to get the editorkit
388        * @param doc -
389        *          the Document to insert the HTML into.
390        * @param offset -
391        *          where to begin inserting the HTML.
392        * @param html -
393        *          the String to insert
394        * @param popDepth -
395        *          the number of ElementSpec.EndTagTypes to generate before
396        *          inserting
397        * @param pushDepth -
398        *          the number of ElementSpec.StartTagTypes with a direction of
399        *          ElementSpec.JoinNextDirection that should be generated before
400        * @param addTag -
401        *          the first tag to start inserting into document
402        */
403       protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
404                               String html, int popDepth, int pushDepth,
405                               HTML.Tag addTag)
406       {
407         try
408           {
409             super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
410                                                       popDepth, pushDepth, addTag);
411           }
412         catch (IOException e)
413           {
414             throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
415           }
416         catch (BadLocationException ex)
417           {
418             throw (RuntimeException) new RuntimeException("BadLocationException: "
419                                               + offset).initCause(ex);
420           }
421       }
422       
423       /**
424        * Invoked when inserting at a boundary. Determines the number of pops,
425        * and then the number of pushes that need to be performed. The it calls
426        * insertHTML.
427        * 
428        * @param editor -
429        *          the editor to use to get the editorkit
430        * @param doc -
431        *          the Document to insert the HTML into.
432        * @param offset -
433        *          where to begin inserting the HTML.
434        * @param insertElement -
435        *          the element to insert
436        * @param html -
437        *          the html to insert
438        * @param parentTag -
439        *          the parent tag
440        * @param addTag -
441        *          the first tag
442        */
443       protected void insertAtBoundary(JEditorPane editor,
444                                       HTMLDocument doc, int offset,
445                                       Element insertElement,
446                                       String html, HTML.Tag parentTag,
447                                       HTML.Tag addTag)
448       {
449         insertAtBoundry(editor, doc, offset, insertElement,
450                         html, parentTag, addTag);
451       }
452       
453       /**
454        * Invoked when inserting at a boundary. Determines the number of pops, 
455        * and then the number of pushes that need to be performed. The it calls
456        * insertHTML.
457        * 
458        * @param editor - the editor to use to get the editorkit
459        * @param doc -
460        *          the Document to insert the HTML into.
461        * @param offset -
462        *          where to begin inserting the HTML.
463        * @param insertElement - the element to insert
464        * @param html - the html to insert
465        * @param parentTag - the parent tag
466        * @param addTag - the first tag
467        * 
468        * @deprecated as of v1.3, use insertAtBoundary
469        */
470       protected void insertAtBoundry(JEditorPane editor,
471                                      HTMLDocument doc,
472                                      int offset, Element insertElement,
473                                      String html, HTML.Tag parentTag,
474                                      HTML.Tag addTag)
475       {
476         Element parent = insertElement;
477         Element el;
478         // Find common parent element.
479         if (offset > 0 || insertElement == null)
480           {
481             el = doc.getDefaultRootElement();
482             while (el != null && el.getStartOffset() != offset
483                    && ! el.isLeaf())
484               el = el.getElement(el.getElementIndex(offset));
485             parent = el != null ? el.getParentElement() : null;
486           }
487         if (parent != null)
488           {
489             int pops = 0;
490             int pushes = 0;
491             if (offset == 0 && insertElement != null)
492               {
493                 el = parent;
494                 while (el != null && ! el.isLeaf())
495                   {
496                     el = el.getElement(el.getElementIndex(offset));
497                     pops++;
498                   }
499               }
500             else
501               {
502                 el = parent;
503                 offset--;
504                 while (el != null && ! el.isLeaf())
505                   {
506                     el = el.getElement(el.getElementIndex(offset));
507                     pops++;
508                   }
509                 el = parent;
510                 offset++;
511                 while (el != null && el != insertElement)
512                   {
513                     el = el.getElement(el.getElementIndex(offset));
514                     pushes++;
515                   }
516               }
517             pops = Math.max(0, pops - 1);
518             insertHTML(editor, doc, offset, html, pops, pushes, addTag);
519           }
520       }
521       
522       /**
523        * Inserts the HTML.
524        * 
525        * @param ae - the action performed
526        */
527       public void actionPerformed(ActionEvent ae)
528       {
529         JEditorPane source = getEditor(ae);
530         if (source != null)
531           {
532             HTMLDocument d = getHTMLDocument(source);
533             int offset = source.getSelectionStart();
534             int length = d.getLength();
535             boolean inserted = true;
536             if (! tryInsert(source, d, offset, parentTag, addTag))
537               {
538                 inserted = tryInsert(source, d, offset, alternateParentTag,
539                                      alternateAddTag);
540               }
541             if (inserted)
542               adjustSelection(source, d, offset, length);
543           }
544       }
545
546       /**
547        * Tries to insert the html chunk to the specified <code>addTag</code>.
548        *
549        * @param pane the editor
550        * @param doc the document
551        * @param offset the offset at which to insert
552        * @param tag the tag at which to insert
553        * @param addTag the add tag
554        *
555        * @return <code>true</code> when the html has been inserted successfully,
556        *         <code>false</code> otherwise
557        */
558       private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
559                                 HTML.Tag tag, HTML.Tag addTag)
560       {
561         boolean inserted = false;
562         Element el = findElementMatchingTag(doc, offset, tag);
563         if (el != null && el.getStartOffset() == offset)
564           {
565             insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
566             inserted = true;
567           }
568         else if (offset > 0)
569           {
570             int depth = elementCountToTag(doc, offset - 1, tag);
571             if (depth != -1)
572               {
573                 insertHTML(pane, doc, offset, html, depth, 0, addTag);
574                 inserted = true;
575               }
576           }
577         return inserted;
578       }
579
580       /**
581        * Adjusts the selection after an insertion has been performed.
582        *
583        * @param pane the editor pane
584        * @param doc the document
585        * @param offset the insert offset
586        * @param oldLen the old document length
587        */
588       private void adjustSelection(JEditorPane pane, HTMLDocument doc,
589                                    int offset, int oldLen)
590       {
591         int newLen = doc.getLength();
592         if (newLen != oldLen && offset < newLen)
593           {
594             if (offset > 0)
595               {
596                 String text;
597                 try
598                   {
599                     text = doc.getText(offset - 1, 1);
600                   }
601                 catch (BadLocationException ex)
602                   {
603                     text = null;
604                   }
605                 if (text != null && text.length() > 0
606                     && text.charAt(0) == '\n')
607                   {
608                     pane.select(offset, offset);
609                   }
610                 else
611                   {
612                     pane.select(offset + 1, offset + 1);
613                   }
614               }
615             else
616               {
617                 pane.select(1, 1);
618               }
619           }
620       }
621   }
622   
623   /**
624    * Abstract Action class that helps inserting HTML into an existing document.
625    */
626   public abstract static class HTMLTextAction
627     extends StyledEditorKit.StyledTextAction
628     {
629       
630       /**
631        * Constructor
632        */
633       public HTMLTextAction(String name) 
634       {
635         super(name);
636       }
637       
638       /**
639        * Gets the HTMLDocument from the JEditorPane.
640        * 
641        * @param e - the editor pane
642        * @return the html document.
643        */
644       protected HTMLDocument getHTMLDocument(JEditorPane e)
645       {
646         Document d = e.getDocument();
647         if (d instanceof HTMLDocument)
648           return (HTMLDocument) d;
649         throw new IllegalArgumentException("Document is not a HTMLDocument.");
650       }
651       
652       /**
653        * Gets the HTMLEditorKit
654        *  
655        * @param e - the JEditorPane to get the HTMLEditorKit from.
656        * @return the HTMLEditorKit
657        */
658       protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) 
659       {
660         EditorKit d = e.getEditorKit();
661         if (d instanceof HTMLEditorKit)
662           return (HTMLEditorKit) d;
663         throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
664       }
665       
666       /**
667        * Returns an array of Elements that contain the offset.
668        * The first elements corresponds to the roots of the doc.
669        * 
670        * @param doc - the document to get the Elements from.
671        * @param offset - the offset the Elements must contain
672        * @return an array of all the elements containing the offset.
673        */
674       protected Element[] getElementsAt(HTMLDocument doc,
675                                         int offset)
676       {
677         return getElementsAt(doc.getDefaultRootElement(), offset, 0);
678       }
679       
680       /**
681        * Helper function to get all elements using recursion.
682        */
683       private Element[] getElementsAt(Element root, int offset, int depth)
684       {
685         Element[] elements = null;
686         if (root != null)
687           {
688             if (root.isLeaf())
689               {
690                 elements = new Element[depth + 1];
691                 elements[depth] = root;
692                 return elements;
693               }
694             elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
695                                      offset, depth + 1);
696             elements[depth] = root;
697           }
698         return elements;
699       }
700       
701       /**
702        * Returns the number of elements, starting at the deepest point, needed
703        * to get an element representing tag. -1 if no elements are found, 0 if
704        * the parent of the leaf at offset represents the tag.
705        * 
706        * @param doc -
707        *          the document to search
708        * @param offset -
709        *          the offset to check
710        * @param tag -
711        *          the tag to look for
712        * @return - the number of elements needed to get an element representing
713        *         tag.
714        */
715       protected int elementCountToTag(HTMLDocument doc,
716                                       int offset, HTML.Tag tag)
717       {
718         Element root = doc.getDefaultRootElement();
719         int num = -1;
720         Element next = root.getElement(root.getElementIndex(offset));
721         
722         while (!next.isLeaf())
723           {
724             num++;
725             if (next.getAttributes().
726                 getAttribute(StyleConstants.NameAttribute).equals(tag))
727               return num;
728             next = next.getElement(next.getElementIndex(offset));
729           }
730         return num;
731       }
732       
733       /**
734        * Gets the deepest element at offset with the
735        * matching tag.
736        * 
737        * @param doc - the document to search
738        * @param offset - the offset to check for
739        * @param tag - the tag to match
740        * @return - the element that is found, null if not found.
741        */
742       protected Element findElementMatchingTag(HTMLDocument doc,
743                                                int offset, HTML.Tag tag)
744       {
745         Element element = doc.getDefaultRootElement();
746         Element tagElement = null;
747         
748         while (element != null)
749           {
750             Object otag = element.getAttributes().getAttribute(
751                                      StyleConstants.NameAttribute);
752             if (otag instanceof HTML.Tag && otag.equals(tag))
753               tagElement = element;
754             element = element.getElement(element.getElementIndex(offset));
755           }
756         
757         return tagElement;
758       }
759     }
760   
761   /**
762    * A {@link ViewFactory} that is able to create {@link View}s for
763    * the <code>Element</code>s that are supported.
764    */
765   public static class HTMLFactory
766     implements ViewFactory
767   {
768     
769     /**
770      * Constructor
771      */
772     public HTMLFactory()
773     {
774       // Do Nothing here.
775     }
776     
777     /**
778      * Creates a {@link View} for the specified <code>Element</code>.
779      *
780      * @param element the <code>Element</code> to create a <code>View</code>
781      *        for
782      * @return the <code>View</code> for the specified <code>Element</code>
783      *         or <code>null</code> if the type of <code>element</code> is
784      *         not supported
785      */
786     public View create(Element element)
787     {
788       View view = null;
789       Object attr =
790         element.getAttributes().getAttribute(StyleConstants.NameAttribute);
791       if (attr instanceof HTML.Tag)
792         {
793           HTML.Tag tag = (HTML.Tag) attr;
794
795           if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
796               || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
797               || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
798               || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
799               || tag == HTML.Tag.DT)
800             view = new ParagraphView(element);
801           else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
802                    || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
803                    || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
804                    || tag == HTML.Tag.DIV
805                    || tag == HTML.Tag.BLOCKQUOTE
806                    || tag == HTML.Tag.PRE
807                    || tag == HTML.Tag.FORM
808                    // Misplaced TD and TH tags get mapped as vertical block.
809                    // Note that correctly placed tags get mapped in TableView.
810                    || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
811             view = new BlockView(element, View.Y_AXIS);
812           else if (tag == HTML.Tag.TR)
813             // Misplaced TR tags get mapped as horizontal blocks.
814             // Note that correctly placed tags get mapped in TableView.
815             view = new BlockView(element, View.X_AXIS);
816           else if (tag == HTML.Tag.IMG)
817             view = new ImageView(element);
818           
819           else if (tag == HTML.Tag.CONTENT)
820             view = new InlineView(element);
821           else if (tag == HTML.Tag.HEAD)
822             view = new NullView(element);
823           else if (tag == HTML.Tag.TABLE)
824             view = new javax.swing.text.html.TableView(element);
825           else if (tag == HTML.Tag.HR)
826             view = new HRuleView(element);
827           else if (tag == HTML.Tag.BR)
828             view = new BRView(element);
829           else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
830                    || tag == HTML.Tag.TEXTAREA)
831             view = new FormView(element);
832
833           else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
834                    || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
835             view = new ListView(element);
836           else if (tag == HTML.Tag.FRAMESET)
837             view = new FrameSetView(element);
838           else if (tag == HTML.Tag.FRAME)
839             view = new FrameView(element);
840           else if (tag == HTML.Tag.OBJECT)
841             view = new ObjectView(element);
842         }
843       if (view == null)
844         {
845           view = new NullView(element);
846         }
847       return view;
848     }
849   }
850   
851   /**
852    * The abstract HTML parser declaration.
853    */
854   public abstract static class Parser
855   {
856     /**
857      * Parse the HTML text, calling various methods of the provided callback
858      * in response to the occurence of the corresponding HTML constructions.
859      * @param reader The reader to read the source HTML from.
860      * @param callback The callback to receive information about the parsed
861      * HTML structures
862      * @param ignoreCharSet If true, the parser ignores all charset information
863      * that may be present in HTML documents.
864      * @throws IOException, normally if the reader throws one.
865      */
866     public abstract void parse(Reader reader, ParserCallback callback,
867                                boolean ignoreCharSet) throws IOException;
868   }
869
870   /**
871    * The "hook" that receives all information about the HTML document
872    * structure while parsing it. The methods are invoked by parser
873    * and should be normally overridden.
874    */
875   public static class ParserCallback
876   {
877     /**
878      * If the tag does not occurs in the html stream directly, but
879      * is supposed by parser, the tag attribute set contains this additional
880      * attribute, having value Boolean.True.
881      */
882     public static final Object IMPLIED = "_implied_";
883
884     /**
885      * Constructor
886      */
887     public ParserCallback()
888     {
889       // Nothing to do here.
890     }
891     
892     /**
893      * The parser calls this method after it finishes parsing the document.
894      */
895     public void flush() throws BadLocationException
896     {
897       // Nothing to do here.
898     }
899
900     /**
901      * Handle HTML comment, present in the given position.
902      * @param comment the comment
903      * @position the position of the comment in the text being parsed.
904      */
905     public void handleComment(char[] comment, int position)
906     {
907       // Nothing to do here.
908     }
909
910     /**
911      * Notifies about the character sequences, used to separate lines in
912      * this document. The parser calls this method after it finishes
913      * parsing the document, but before flush().
914      * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
915      */
916     public void handleEndOfLineString(String end_of_line)
917     {
918       // Nothing to do here.
919     }
920
921     /**
922      * The method is called when the HTML closing tag ((like &lt;/table&gt;)
923      * is found or if the parser concludes that the one should be present
924      * in the current position.
925      * @param tag The tag being handled
926      * @param position the tag position in the text being parsed.
927      */
928     public void handleEndTag(HTML.Tag tag, int position)
929     {
930       // Nothing to do here.
931     }
932
933     /**
934      * Handle the error.
935      * @param message The message, explaining the error.
936      * @param position The starting position of the fragment that has caused
937      * the error in the html document being parsed.
938      */
939     public void handleError(String message, int position)
940     {
941       // Nothing to do here.
942     }
943
944     /**
945      * Handle the tag with no content, like &lt;br&gt;. The method is
946      * called for the elements that, in accordance with the current DTD,
947      * has an empty content.
948      * @param tag The tag being handled.
949      * @param position The tag position in the text being parsed.
950      */
951     public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
952                                 int position)
953     {
954       // Nothing to do here.
955     }
956
957     /**
958      * The method is called when the HTML opening tag ((like &lt;table&gt;)
959      * is found or if the parser concludes that the one should be present
960      * in the current position.
961      * @param tag The tag being handled
962      * @param position The tag position in the text being parsed
963      */
964     public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
965                                int position)
966     {
967       // Nothing to do here.
968     }
969
970     /**
971      * Handle the text section.
972      * @param text A section text.
973      * @param position The text position in the HTML document text being parsed.
974      */
975     public void handleText(char[] text, int position)
976     {
977       // Nothing to do here.
978     }
979   }
980
981   /**
982    * Use serialVersionUID (v1.4) for interoperability.
983    */
984   private static final long serialVersionUID = 8751997116710384592L;
985
986   /**
987    * Default cascading stylesheed file ("default.css").
988    */
989   public static final String DEFAULT_CSS = "default.css";
990
991   /**
992    * The <b>bold</b> action identifier.
993    */
994   public static final String BOLD_ACTION = "html-bold-action";
995
996   /**
997    * The <i>italic</i> action identifier.
998    */
999   public static final String ITALIC_ACTION = "html-italic-action";
1000
1001   /**
1002    * The <font color="#FF0000">color</font> action indentifier
1003    * (passing the color as an argument).
1004    */
1005   public static final String COLOR_ACTION = "html-color-action";
1006
1007   /**
1008    * The <font size="+1">increase</font> font action identifier.
1009    */
1010   public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
1011
1012   /**
1013    * The <font size="-1">decrease</font> font action identifier.
1014    */
1015   public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
1016
1017   /**
1018    * Align images at the bottom.
1019    */
1020   public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
1021
1022   /**
1023    * Align images at the middle.
1024    */
1025   public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
1026
1027   /**
1028    * Align images at the top.
1029    */
1030   public static final String IMG_ALIGN_TOP = "html-image-align-top";
1031
1032   /**
1033    * Align images at the border.
1034    */
1035   public static final String IMG_BORDER = "html-image-border";
1036
1037   /**
1038    * The "logical style" action identifier, passing that style as parameter.
1039    */
1040   public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
1041
1042   /**
1043    * The "ident paragraph left" action.
1044    */
1045   public static final String PARA_INDENT_LEFT = "html-para-indent-left";
1046
1047   /**
1048    * The "ident paragraph right" action.
1049    */
1050   public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
1051   
1052   /**
1053    * Actions for HTML 
1054    */
1055   private static final Action[] defaultActions =
1056   {
1057     new InsertHTMLTextAction("InsertTable",
1058                              "<table border=1><tr><td></td></tr></table>",
1059                              HTML.Tag.BODY, HTML.Tag.TABLE),
1060     new InsertHTMLTextAction("InsertTableRow",
1061                              "<table border=1><tr><td></td></tr></table>",
1062                              HTML.Tag.TABLE, HTML.Tag.TR,
1063                              HTML.Tag.BODY, HTML.Tag.TABLE),
1064     new InsertHTMLTextAction("InsertTableCell",
1065                              "<table border=1><tr><td></td></tr></table>",
1066                              HTML.Tag.TR, HTML.Tag.TD,
1067                              HTML.Tag.BODY, HTML.Tag.TABLE),
1068     new InsertHTMLTextAction("InsertUnorderedList",
1069                              "<ul><li></li></ul>",
1070                              HTML.Tag.BODY, HTML.Tag.UL),
1071     new InsertHTMLTextAction("InsertUnorderedListItem",
1072                              "<ul><li></li></ul>",
1073                              HTML.Tag.UL, HTML.Tag.LI,
1074                              HTML.Tag.BODY, HTML.Tag.UL),
1075     new InsertHTMLTextAction("InsertOrderedList",
1076                              "<ol><li></li></ol>",
1077                              HTML.Tag.BODY, HTML.Tag.OL),
1078     new InsertHTMLTextAction("InsertOrderedListItem",
1079                              "<ol><li></li></ol>",
1080                              HTML.Tag.OL, HTML.Tag.LI,
1081                              HTML.Tag.BODY, HTML.Tag.OL),
1082     new InsertHTMLTextAction("InsertPre",
1083                              "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
1084     // TODO: The reference impl has an InsertHRAction too.
1085   };
1086   
1087   /**
1088    * The current style sheet.
1089    */
1090   private StyleSheet styleSheet;
1091   
1092   /**
1093    * The ViewFactory for HTMLFactory.
1094    */
1095   HTMLFactory viewFactory;
1096   
1097   /**
1098    * The Cursor for links.
1099    */
1100   Cursor linkCursor;
1101   
1102   /**
1103    * The default cursor.
1104    */
1105   Cursor defaultCursor;
1106   
1107   /**
1108    * The parser.
1109    */
1110   Parser parser;
1111   
1112   /**
1113    * The mouse listener used for links.
1114    */
1115   private LinkController linkController;
1116   
1117   /** The content type */
1118   String contentType = "text/html";
1119   
1120   /** The input attributes defined by default.css */
1121   MutableAttributeSet inputAttributes;
1122   
1123   /** The editor pane used. */
1124   JEditorPane editorPane;
1125
1126   /**
1127    * Whether or not the editor kit handles form submissions.
1128    *
1129    * @see #isAutoFormSubmission()
1130    * @see #setAutoFormSubmission(boolean)
1131    */
1132   private boolean autoFormSubmission;
1133
1134   /**
1135    * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
1136    */
1137   public HTMLEditorKit()
1138   {
1139     linkController = new LinkController();
1140     autoFormSubmission = true;
1141   }
1142   
1143   /**
1144    * Gets a factory suitable for producing views of any 
1145    * models that are produced by this kit.
1146    * 
1147    * @return the view factory suitable for producing views.
1148    */
1149   public ViewFactory getViewFactory()
1150   {
1151     if (viewFactory == null)
1152       viewFactory = new HTMLFactory();
1153     return viewFactory;
1154   }
1155   
1156   /**
1157    * Create a text storage model for this type of editor.
1158    *
1159    * @return the model
1160    */
1161   public Document createDefaultDocument()
1162   {
1163     // Protect the shared stylesheet.
1164     StyleSheet styleSheet = getStyleSheet();
1165     StyleSheet ss = new StyleSheet();
1166     ss.addStyleSheet(styleSheet);
1167
1168     HTMLDocument document = new HTMLDocument(ss);
1169     document.setParser(getParser());
1170     document.setAsynchronousLoadPriority(4);
1171     document.setTokenThreshold(100);
1172     return document;
1173   }
1174
1175   /**
1176    * Get the parser that this editor kit uses for reading HTML streams. This
1177    * method can be overridden to use the alternative parser.
1178    * 
1179    * @return the HTML parser (by default, {@link ParserDelegator}).
1180    */
1181   protected Parser getParser()
1182   {
1183     if (parser == null)
1184       {
1185         parser = new GnuParserDelegator(HTML_401F.getInstance());
1186       }
1187     return parser;
1188   }
1189   
1190   /**
1191    * Inserts HTML into an existing document.
1192    * 
1193    * @param doc - the Document to insert the HTML into.
1194    * @param offset - where to begin inserting the HTML.
1195    * @param html - the String to insert
1196    * @param popDepth - the number of ElementSpec.EndTagTypes 
1197    * to generate before inserting
1198    * @param pushDepth - the number of ElementSpec.StartTagTypes 
1199    * with a direction of ElementSpec.JoinNextDirection that 
1200    * should be generated before
1201    * @param insertTag - the first tag to start inserting into document
1202    * @throws IOException - on any I/O error
1203    * @throws BadLocationException - if pos represents an invalid location
1204    * within the document
1205    */
1206   public void insertHTML(HTMLDocument doc, int offset, String html,
1207                          int popDepth, int pushDepth, HTML.Tag insertTag)
1208       throws BadLocationException, IOException
1209   {
1210     Parser parser = getParser();
1211     if (offset < 0 || offset > doc.getLength())
1212       throw new BadLocationException("Bad location", offset);
1213     if (parser == null)
1214       throw new IOException("Parser is null.");
1215
1216     ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
1217
1218     // FIXME: What should ignoreCharSet be set to?
1219     
1220     // parser.parse inserts html into the buffer
1221     parser.parse(new StringReader(html), pc, false);
1222     pc.flush();
1223   }
1224   
1225   /**
1226    * Inserts content from the given stream. Inserting HTML into a non-empty 
1227    * document must be inside the body Element, if you do not insert into 
1228    * the body an exception will be thrown. When inserting into a non-empty 
1229    * document all tags outside of the body (head, title) will be dropped.
1230    * 
1231    * @param in - the stream to read from
1232    * @param doc - the destination for the insertion
1233    * @param pos - the location in the document to place the content
1234    * @throws IOException - on any I/O error
1235    * @throws BadLocationException - if pos represents an invalid location
1236    * within the document
1237    */
1238   public void read(Reader in, Document doc, int pos) throws IOException,
1239       BadLocationException
1240   {
1241     if (doc instanceof HTMLDocument)
1242       {
1243         Parser parser = getParser();
1244         if (pos < 0 || pos > doc.getLength())
1245           throw new BadLocationException("Bad location", pos);
1246         if (parser == null)
1247           throw new IOException("Parser is null.");
1248         
1249         HTMLDocument hd = ((HTMLDocument) doc);
1250         if (editorPane != null)
1251           hd.setBase(editorPane.getPage());
1252         ParserCallback pc = hd.getReader(pos);
1253         
1254         // FIXME: What should ignoreCharSet be set to?
1255         
1256         // parser.parse inserts html into the buffer
1257         parser.parse(in, pc, false);
1258         pc.flush();
1259       }
1260     else
1261       // read in DefaultEditorKit is called.
1262       // the string is inserted in the document as usual.
1263       super.read(in, doc, pos);
1264   }
1265   
1266   /**
1267    * Writes content from a document to the given stream in 
1268    * an appropriate format.
1269    * 
1270    * @param out - the stream to write to
1271    * @param doc - the source for the write
1272    * @param pos - the location in the document to get the content.
1273    * @param len - the amount to write out
1274    * @throws IOException - on any I/O error
1275    * @throws BadLocationException - if pos represents an invalid location
1276    * within the document
1277    */
1278   public void write(Writer out, Document doc, int pos, int len)
1279       throws IOException, BadLocationException
1280   {
1281     if (doc instanceof HTMLDocument)
1282       {
1283         HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
1284         writer.write();
1285       }
1286     else if (doc instanceof StyledDocument)
1287       {
1288         MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
1289                                                          (StyledDocument) doc,
1290                                                          pos, len);
1291         writer.write();
1292       }
1293     else
1294       super.write(out, doc, pos, len);
1295   }
1296   
1297   /**
1298    * Gets the content type that the kit supports.
1299    * This kit supports the type text/html.
1300    * 
1301    * @returns the content type supported.
1302    */
1303   public String getContentType()
1304   {
1305     return contentType;
1306   } 
1307   
1308   /**
1309    * Creates a copy of the editor kit.
1310    * 
1311    * @return a copy of this.
1312    */
1313   public Object clone()
1314   {
1315     // FIXME: Need to clone all fields
1316     HTMLEditorKit copy = (HTMLEditorKit) super.clone();
1317     copy.linkController = new LinkController();
1318     return copy;
1319   }
1320   
1321   /**
1322    * Copies the key/values in elements AttributeSet into set. 
1323    * This does not copy component, icon, or element names attributes.
1324    * This is called anytime the caret moves over a different location. 
1325    * 
1326    * @param element - the element to create the input attributes for.
1327    * @param set - the set to copy the values into.
1328    */
1329   protected void createInputAttributes(Element element,
1330                                        MutableAttributeSet set)
1331   {
1332     set.removeAttributes(set);
1333     set.addAttributes(element.getAttributes());
1334     // FIXME: Not fully implemented.
1335   }
1336   
1337   /**
1338    * Called when this is installed into the JEditorPane.
1339    * 
1340    * @param c - the JEditorPane installed into.
1341    */
1342   public void install(JEditorPane c)
1343   {
1344     super.install(c);
1345     c.addMouseListener(linkController);
1346     c.addMouseMotionListener(linkController);
1347     editorPane = c;
1348   }
1349   
1350   /**
1351    * Called when the this is removed from the JEditorPane.
1352    * It unregisters any listeners.
1353    * 
1354    * @param c - the JEditorPane being removed from.
1355    */
1356   public void deinstall(JEditorPane c)
1357   {
1358     super.deinstall(c);
1359     c.removeMouseListener(linkController);
1360     c.removeMouseMotionListener(linkController);
1361     editorPane = null;
1362   }
1363   
1364   /**
1365    * Gets the AccessibleContext associated with this.
1366    * 
1367    * @return the AccessibleContext for this.
1368    */
1369   public AccessibleContext getAccessibleContext()
1370   {
1371     // FIXME: Should return an instance of 
1372     // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
1373     // Not implemented yet.
1374     return null;
1375   }
1376   
1377   /**
1378    * Gets the action list. This list is supported by the superclass
1379    * augmented by the collection of actions defined locally for style
1380    * operations.
1381    * 
1382    * @return an array of all the actions
1383    */
1384   public Action[] getActions()
1385   {
1386     return TextAction.augmentList(super.getActions(), defaultActions);
1387   }
1388   
1389   /**
1390    * Returns the default cursor.
1391    * 
1392    * @return the default cursor
1393    */
1394   public Cursor getDefaultCursor()
1395   {
1396     if (defaultCursor == null)
1397       defaultCursor = Cursor.getDefaultCursor();
1398     return defaultCursor;
1399   }
1400   
1401   /**
1402    * Returns the cursor for links.
1403    * 
1404    * @return the cursor for links.
1405    */
1406   public Cursor getLinkCursor()
1407   {
1408     if (linkCursor == null)
1409       linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
1410     return linkCursor;
1411   }
1412   
1413   /**
1414    * Sets the Cursor for links.
1415    * 
1416    * @param cursor - the new cursor for links.
1417    */
1418   public void setLinkCursor(Cursor cursor)
1419   {
1420     linkCursor = cursor;
1421   }
1422   
1423   /**
1424    * Sets the default cursor.
1425    * 
1426    * @param cursor - the new default cursor.
1427    */
1428   public void setDefaultCursor(Cursor cursor)
1429   {
1430     defaultCursor = cursor;
1431   }
1432   
1433   /**
1434    * Gets the input attributes used for the styled editing actions.
1435    * 
1436    * @return the attribute set
1437    */
1438   public MutableAttributeSet getInputAttributes()
1439   {
1440     return inputAttributes;
1441   }
1442   
1443   /**
1444    * Get the set of styles currently being used to render the HTML elements. 
1445    * By default the resource specified by DEFAULT_CSS gets loaded, and is 
1446    * shared by all HTMLEditorKit instances.
1447    * 
1448    * @return the style sheet.
1449    */
1450   public StyleSheet getStyleSheet()
1451   {
1452     if (styleSheet == null)
1453       {
1454         try
1455           {
1456             styleSheet = new StyleSheet();
1457             Class c = HTMLEditorKit.class;
1458             InputStream in = c.getResourceAsStream(DEFAULT_CSS);
1459             InputStreamReader r = new InputStreamReader(in);
1460             styleSheet.loadRules(r,  null);
1461             r.close();
1462           }
1463         catch (IOException ex)
1464           {
1465             // No style available.
1466           }
1467       }
1468     return styleSheet;
1469   }
1470   
1471   /**
1472    * Set the set of styles to be used to render the various HTML elements. 
1473    * These styles are specified in terms of CSS specifications. Each document 
1474    * produced by the kit will have a copy of the sheet which it can add the 
1475    * document specific styles to. By default, the StyleSheet specified is shared 
1476    * by all HTMLEditorKit instances. 
1477    * 
1478    * @param s - the new style sheet
1479    */
1480   public void setStyleSheet(StyleSheet s)
1481   {
1482     styleSheet = s;
1483   }
1484
1485   /**
1486    * Returns <code>true</code> when forms should be automatically submitted
1487    * by the editor kit. Set this to <code>false</code> when you want to
1488    * intercept form submission. In this case you'd want to listen for
1489    * hyperlink events on the document and handle FormSubmitEvents specially.
1490    *
1491    * The default is <code>true</code>.
1492    *
1493    * @return <code>true</code> when forms should be automatically submitted
1494    *         by the editor kit, <code>false</code> otherwise
1495    *
1496    * @since 1.5
1497    *
1498    * @see #setAutoFormSubmission(boolean)
1499    * @see FormSubmitEvent
1500    */
1501   public boolean isAutoFormSubmission()
1502   {
1503     return autoFormSubmission;
1504   }
1505
1506   /**
1507    * Sets whether or not the editor kit should automatically submit forms.
1508    *  
1509    * @param auto <code>true</code> when the editor kit should handle form
1510    *        submission, <code>false</code> otherwise
1511    *
1512    * @since 1.5
1513    *
1514    * @see #isAutoFormSubmission()
1515    */
1516   public void setAutoFormSubmission(boolean auto)
1517   {
1518     autoFormSubmission = auto;
1519   }
1520 }