src/share/classes/javax/swing/text/html/HTMLEditorKit.java

Print this page




 143  * by the setting of the StyleSheet property on the EditorKit.
 144  *
 145  * <dt>
 146  * Not lossy
 147  * <dd>
 148  * An EditorKit has the ability to be read and save documents.
 149  * It is generally the most pleasing to users if there is no loss
 150  * of data between the two operation.  The policy of the HTMLEditorKit
 151  * will be to store things not recognized or not necessarily visible
 152  * so they can be subsequently written out.  The model of the HTML document
 153  * should therefore contain all information discovered while reading the
 154  * document.  This is constrained in some ways by the need to support
 155  * editing (i.e. incorrect documents sometimes must be normalized).
 156  * The guiding principle is that information shouldn't be lost, but
 157  * some might be synthesized to produce a more correct model or it might
 158  * be rearranged.
 159  * </dl>
 160  *
 161  * @author  Timothy Prinzing
 162  */

 163 public class HTMLEditorKit extends StyledEditorKit implements Accessible {
 164 
 165     private JEditorPane theEditor;
 166 
 167     /**
 168      * Constructs an HTMLEditorKit, creates a StyleContext,
 169      * and loads the style sheet.
 170      */
 171     public HTMLEditorKit() {
 172 
 173     }
 174 
 175     /**
 176      * Get the MIME type of the data that this
 177      * kit represents support for.  This kit supports
 178      * the type <code>text/html</code>.
 179      *
 180      * @return the type
 181      */
 182     public String getContentType() {


 623     private static final Cursor MoveCursor = Cursor.getPredefinedCursor
 624                                     (Cursor.HAND_CURSOR);
 625     private static final Cursor DefaultCursor = Cursor.getPredefinedCursor
 626                                     (Cursor.DEFAULT_CURSOR);
 627 
 628     /** Shared factory for creating HTML Views. */
 629     private static final ViewFactory defaultFactory = new HTMLFactory();
 630 
 631     MutableAttributeSet input;
 632     private static final Object DEFAULT_STYLES_KEY = new Object();
 633     private LinkController linkHandler = new LinkController();
 634     private static Parser defaultParser = null;
 635     private Cursor defaultCursor = DefaultCursor;
 636     private Cursor linkCursor = MoveCursor;
 637     private boolean isAutoFormSubmission = true;
 638 
 639     /**
 640      * Class to watch the associated component and fire
 641      * hyperlink events on it when appropriate.
 642      */

 643     public static class LinkController extends MouseAdapter implements MouseMotionListener, Serializable {
 644         private Element curElem = null;
 645         /**
 646          * If true, the current element (curElem) represents an image.
 647          */
 648         private boolean curElemImage = false;
 649         private String href = null;
 650         /** This is used by viewToModel to avoid allocing a new array each
 651          * time. */
 652         private transient Position.Bias[] bias = new Position.Bias[1];
 653         /**
 654          * Current offset.
 655          */
 656         private int curOffset;
 657 
 658         /**
 659          * Called for a mouse click event.
 660          * If the component is read-only (ie a browser) then
 661          * the clicked event is used to drive an attempt to
 662          * follow the reference specified by a link.


1465                                  HTML.Tag.BODY, HTML.Tag.PRE),
1466         nextLinkAction, previousLinkAction, activateLinkAction,
1467 
1468         new BeginAction(beginAction, false),
1469         new BeginAction(selectionBeginAction, true)
1470     };
1471 
1472     // link navigation support
1473     private boolean foundLink = false;
1474     private int prevHypertextOffset = -1;
1475     private Object linkNavigationTag;
1476 
1477 
1478     /**
1479      * An abstract Action providing some convenience methods that may
1480      * be useful in inserting HTML into an existing document.
1481      * <p>NOTE: None of the convenience methods obtain a lock on the
1482      * document. If you have another thread modifying the text these
1483      * methods may have inconsistent behavior, or return the wrong thing.
1484      */

1485     public static abstract class HTMLTextAction extends StyledTextAction {
1486         public HTMLTextAction(String name) {
1487             super(name);
1488         }
1489 
1490         /**
1491          * @return HTMLDocument of <code>e</code>.
1492          */
1493         protected HTMLDocument getHTMLDocument(JEditorPane e) {
1494             Document d = e.getDocument();
1495             if (d instanceof HTMLDocument) {
1496                 return (HTMLDocument) d;
1497             }
1498             throw new IllegalArgumentException("document must be HTMLDocument");
1499         }
1500 
1501         /**
1502          * @return HTMLEditorKit for <code>e</code>.
1503          */
1504         protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) {


1575     }
1576 
1577 
1578     /**
1579      * InsertHTMLTextAction can be used to insert an arbitrary string of HTML
1580      * into an existing HTML document. At least two HTML.Tags need to be
1581      * supplied. The first Tag, parentTag, identifies the parent in
1582      * the document to add the elements to. The second tag, addTag,
1583      * identifies the first tag that should be added to the document as
1584      * seen in the HTML string. One important thing to remember, is that
1585      * the parser is going to generate all the appropriate tags, even if
1586      * they aren't in the HTML string passed in.<p>
1587      * For example, lets say you wanted to create an action to insert
1588      * a table into the body. The parentTag would be HTML.Tag.BODY,
1589      * addTag would be HTML.Tag.TABLE, and the string could be something
1590      * like &lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;.
1591      * <p>There is also an option to supply an alternate parentTag and
1592      * addTag. These will be checked for if there is no parentTag at
1593      * offset.
1594      */

1595     public static class InsertHTMLTextAction extends HTMLTextAction {
1596         public InsertHTMLTextAction(String name, String html,
1597                                     HTML.Tag parentTag, HTML.Tag addTag) {
1598             this(name, html, parentTag, addTag, null, null);
1599         }
1600 
1601         public InsertHTMLTextAction(String name, String html,
1602                                     HTML.Tag parentTag,
1603                                     HTML.Tag addTag,
1604                                     HTML.Tag alternateParentTag,
1605                                     HTML.Tag alternateAddTag) {
1606             this(name, html, parentTag, addTag, alternateParentTag,
1607                  alternateAddTag, true);
1608         }
1609 
1610         /* public */
1611         InsertHTMLTextAction(String name, String html,
1612                                     HTML.Tag parentTag,
1613                                     HTML.Tag addTag,
1614                                     HTML.Tag alternateParentTag,


1806         /** Tag to check for in the document. */
1807         protected HTML.Tag parentTag;
1808         /** Tag in HTML to start adding tags from. */
1809         protected HTML.Tag addTag;
1810         /** Alternate Tag to check for in the document if parentTag is
1811          * not found. */
1812         protected HTML.Tag alternateParentTag;
1813         /** Alternate tag in HTML to start adding tags from if parentTag
1814          * is not found and alternateParentTag is found. */
1815         protected HTML.Tag alternateAddTag;
1816         /** True indicates the selection should be adjusted after an insert. */
1817         boolean adjustSelection;
1818     }
1819 
1820 
1821     /**
1822      * InsertHRAction is special, at actionPerformed time it will determine
1823      * the parent HTML.Tag based on the paragraph element at the selection
1824      * start.
1825      */

1826     static class InsertHRAction extends InsertHTMLTextAction {
1827         InsertHRAction() {
1828             super("InsertHR", "<hr>", null, HTML.Tag.IMPLIED, null, null,
1829                   false);
1830         }
1831 
1832         /**
1833          * Inserts the HTML into the document.
1834          *
1835          * @param ae the event
1836          */
1837         public void actionPerformed(ActionEvent ae) {
1838             JEditorPane editor = getEditor(ae);
1839             if (editor != null) {
1840                 HTMLDocument doc = getHTMLDocument(editor);
1841                 int offset = editor.getSelectionStart();
1842                 Element paragraph = doc.getParagraphElement(offset);
1843                 if (paragraph.getParentElement() != null) {
1844                     parentTag = (HTML.Tag)paragraph.getParentElement().
1845                                   getAttributes().getAttribute


1860             Object nextKey = names.nextElement();
1861             Object nextVal = attr.getAttribute(nextKey);
1862             if (nextVal instanceof AttributeSet) {
1863                 Object value = getAttrValue((AttributeSet)nextVal, key);
1864                 if (value != null) {
1865                     return value;
1866                 }
1867             } else if (nextKey == key) {
1868                 return nextVal;
1869             }
1870         }
1871         return null;
1872     }
1873 
1874     /*
1875      * Action to move the focus on the next or previous hypertext link
1876      * or object. TODO: This method relies on support from the
1877      * javax.accessibility package.  The text package should support
1878      * keyboard navigation of text elements directly.
1879      */

1880     static class NavigateLinkAction extends TextAction implements CaretListener {
1881 
1882         private static final FocusHighlightPainter focusPainter =
1883             new FocusHighlightPainter(null);
1884         private final boolean focusBack;
1885 
1886         /*
1887          * Create this action with the appropriate identifier.
1888          */
1889         public NavigateLinkAction(String actionName) {
1890             super(actionName);
1891             focusBack = "previous-link-action".equals(actionName);
1892         }
1893 
1894         /**
1895          * Called when the caret position is updated.
1896          *
1897          * @param e the caret event
1898          */
1899         public void caretUpdate(CaretEvent e) {


2069                         Rectangle r = (shape instanceof Rectangle) ?
2070                             (Rectangle)shape : shape.getBounds();
2071                         g.drawRect(r.x, r.y, r.width - 1, r.height);
2072                         return r;
2073                     } catch (BadLocationException e) {
2074                         // can't render
2075                     }
2076                 }
2077                 // Only if exception
2078                 return null;
2079             }
2080         }
2081     }
2082 
2083     /*
2084      * Action to activate the hypertext link that has focus.
2085      * TODO: This method relies on support from the
2086      * javax.accessibility package.  The text package should support
2087      * keyboard navigation of text elements directly.
2088      */

2089     static class ActivateLinkAction extends TextAction {
2090 
2091         /**
2092          * Create this action with the appropriate identifier.
2093          */
2094         public ActivateLinkAction(String actionName) {
2095             super(actionName);
2096         }
2097 
2098         /*
2099          * activates the hyperlink at offset
2100          */
2101         private void activateLink(String href, HTMLDocument doc,
2102                                   JEditorPane editor, int offset) {
2103             try {
2104                 URL page =
2105                     (URL)doc.getProperty(Document.StreamDescriptionProperty);
2106                 URL url = new URL(page, href);
2107                 HyperlinkEvent linkEvent = new HyperlinkEvent
2108                     (editor, HyperlinkEvent.EventType.


2242             }
2243         }
2244     }
2245 
2246     private static int getBodyElementStart(JTextComponent comp) {
2247         Element rootElement = comp.getDocument().getRootElements()[0];
2248         for (int i = 0; i < rootElement.getElementCount(); i++) {
2249             Element currElement = rootElement.getElement(i);
2250             if("body".equals(currElement.getName())) {
2251                 return currElement.getStartOffset();
2252             }
2253         }
2254         return 0;
2255     }
2256 
2257     /*
2258      * Move the caret to the beginning of the document.
2259      * @see DefaultEditorKit#beginAction
2260      * @see HTMLEditorKit#getActions
2261      */
2262 
2263     static class BeginAction extends TextAction {
2264 
2265         /* Create this object with the appropriate identifier. */
2266         BeginAction(String nm, boolean select) {
2267             super(nm);
2268             this.select = select;
2269         }
2270 
2271         /** The operation to perform when this action is triggered. */
2272         public void actionPerformed(ActionEvent e) {
2273             JTextComponent target = getTextComponent(e);
2274             int bodyStart = getBodyElementStart(target);
2275 
2276             if (target != null) {
2277                 if (select) {
2278                     target.moveCaretPosition(bodyStart);
2279                 } else {
2280                     target.setCaretPosition(bodyStart);
2281                 }
2282             }


 143  * by the setting of the StyleSheet property on the EditorKit.
 144  *
 145  * <dt>
 146  * Not lossy
 147  * <dd>
 148  * An EditorKit has the ability to be read and save documents.
 149  * It is generally the most pleasing to users if there is no loss
 150  * of data between the two operation.  The policy of the HTMLEditorKit
 151  * will be to store things not recognized or not necessarily visible
 152  * so they can be subsequently written out.  The model of the HTML document
 153  * should therefore contain all information discovered while reading the
 154  * document.  This is constrained in some ways by the need to support
 155  * editing (i.e. incorrect documents sometimes must be normalized).
 156  * The guiding principle is that information shouldn't be lost, but
 157  * some might be synthesized to produce a more correct model or it might
 158  * be rearranged.
 159  * </dl>
 160  *
 161  * @author  Timothy Prinzing
 162  */
 163 @SuppressWarnings("serial") // Same-version serialization only
 164 public class HTMLEditorKit extends StyledEditorKit implements Accessible {
 165 
 166     private JEditorPane theEditor;
 167 
 168     /**
 169      * Constructs an HTMLEditorKit, creates a StyleContext,
 170      * and loads the style sheet.
 171      */
 172     public HTMLEditorKit() {
 173 
 174     }
 175 
 176     /**
 177      * Get the MIME type of the data that this
 178      * kit represents support for.  This kit supports
 179      * the type <code>text/html</code>.
 180      *
 181      * @return the type
 182      */
 183     public String getContentType() {


 624     private static final Cursor MoveCursor = Cursor.getPredefinedCursor
 625                                     (Cursor.HAND_CURSOR);
 626     private static final Cursor DefaultCursor = Cursor.getPredefinedCursor
 627                                     (Cursor.DEFAULT_CURSOR);
 628 
 629     /** Shared factory for creating HTML Views. */
 630     private static final ViewFactory defaultFactory = new HTMLFactory();
 631 
 632     MutableAttributeSet input;
 633     private static final Object DEFAULT_STYLES_KEY = new Object();
 634     private LinkController linkHandler = new LinkController();
 635     private static Parser defaultParser = null;
 636     private Cursor defaultCursor = DefaultCursor;
 637     private Cursor linkCursor = MoveCursor;
 638     private boolean isAutoFormSubmission = true;
 639 
 640     /**
 641      * Class to watch the associated component and fire
 642      * hyperlink events on it when appropriate.
 643      */
 644     @SuppressWarnings("serial") // Same-version serialization only
 645     public static class LinkController extends MouseAdapter implements MouseMotionListener, Serializable {
 646         private Element curElem = null;
 647         /**
 648          * If true, the current element (curElem) represents an image.
 649          */
 650         private boolean curElemImage = false;
 651         private String href = null;
 652         /** This is used by viewToModel to avoid allocing a new array each
 653          * time. */
 654         private transient Position.Bias[] bias = new Position.Bias[1];
 655         /**
 656          * Current offset.
 657          */
 658         private int curOffset;
 659 
 660         /**
 661          * Called for a mouse click event.
 662          * If the component is read-only (ie a browser) then
 663          * the clicked event is used to drive an attempt to
 664          * follow the reference specified by a link.


1467                                  HTML.Tag.BODY, HTML.Tag.PRE),
1468         nextLinkAction, previousLinkAction, activateLinkAction,
1469 
1470         new BeginAction(beginAction, false),
1471         new BeginAction(selectionBeginAction, true)
1472     };
1473 
1474     // link navigation support
1475     private boolean foundLink = false;
1476     private int prevHypertextOffset = -1;
1477     private Object linkNavigationTag;
1478 
1479 
1480     /**
1481      * An abstract Action providing some convenience methods that may
1482      * be useful in inserting HTML into an existing document.
1483      * <p>NOTE: None of the convenience methods obtain a lock on the
1484      * document. If you have another thread modifying the text these
1485      * methods may have inconsistent behavior, or return the wrong thing.
1486      */
1487     @SuppressWarnings("serial") // Superclass is not serializable across versions
1488     public static abstract class HTMLTextAction extends StyledTextAction {
1489         public HTMLTextAction(String name) {
1490             super(name);
1491         }
1492 
1493         /**
1494          * @return HTMLDocument of <code>e</code>.
1495          */
1496         protected HTMLDocument getHTMLDocument(JEditorPane e) {
1497             Document d = e.getDocument();
1498             if (d instanceof HTMLDocument) {
1499                 return (HTMLDocument) d;
1500             }
1501             throw new IllegalArgumentException("document must be HTMLDocument");
1502         }
1503 
1504         /**
1505          * @return HTMLEditorKit for <code>e</code>.
1506          */
1507         protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) {


1578     }
1579 
1580 
1581     /**
1582      * InsertHTMLTextAction can be used to insert an arbitrary string of HTML
1583      * into an existing HTML document. At least two HTML.Tags need to be
1584      * supplied. The first Tag, parentTag, identifies the parent in
1585      * the document to add the elements to. The second tag, addTag,
1586      * identifies the first tag that should be added to the document as
1587      * seen in the HTML string. One important thing to remember, is that
1588      * the parser is going to generate all the appropriate tags, even if
1589      * they aren't in the HTML string passed in.<p>
1590      * For example, lets say you wanted to create an action to insert
1591      * a table into the body. The parentTag would be HTML.Tag.BODY,
1592      * addTag would be HTML.Tag.TABLE, and the string could be something
1593      * like &lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;.
1594      * <p>There is also an option to supply an alternate parentTag and
1595      * addTag. These will be checked for if there is no parentTag at
1596      * offset.
1597      */
1598     @SuppressWarnings("serial") // Superclass is not serializable across versions
1599     public static class InsertHTMLTextAction extends HTMLTextAction {
1600         public InsertHTMLTextAction(String name, String html,
1601                                     HTML.Tag parentTag, HTML.Tag addTag) {
1602             this(name, html, parentTag, addTag, null, null);
1603         }
1604 
1605         public InsertHTMLTextAction(String name, String html,
1606                                     HTML.Tag parentTag,
1607                                     HTML.Tag addTag,
1608                                     HTML.Tag alternateParentTag,
1609                                     HTML.Tag alternateAddTag) {
1610             this(name, html, parentTag, addTag, alternateParentTag,
1611                  alternateAddTag, true);
1612         }
1613 
1614         /* public */
1615         InsertHTMLTextAction(String name, String html,
1616                                     HTML.Tag parentTag,
1617                                     HTML.Tag addTag,
1618                                     HTML.Tag alternateParentTag,


1810         /** Tag to check for in the document. */
1811         protected HTML.Tag parentTag;
1812         /** Tag in HTML to start adding tags from. */
1813         protected HTML.Tag addTag;
1814         /** Alternate Tag to check for in the document if parentTag is
1815          * not found. */
1816         protected HTML.Tag alternateParentTag;
1817         /** Alternate tag in HTML to start adding tags from if parentTag
1818          * is not found and alternateParentTag is found. */
1819         protected HTML.Tag alternateAddTag;
1820         /** True indicates the selection should be adjusted after an insert. */
1821         boolean adjustSelection;
1822     }
1823 
1824 
1825     /**
1826      * InsertHRAction is special, at actionPerformed time it will determine
1827      * the parent HTML.Tag based on the paragraph element at the selection
1828      * start.
1829      */
1830     @SuppressWarnings("serial") // Superclass is not serializable across versions
1831     static class InsertHRAction extends InsertHTMLTextAction {
1832         InsertHRAction() {
1833             super("InsertHR", "<hr>", null, HTML.Tag.IMPLIED, null, null,
1834                   false);
1835         }
1836 
1837         /**
1838          * Inserts the HTML into the document.
1839          *
1840          * @param ae the event
1841          */
1842         public void actionPerformed(ActionEvent ae) {
1843             JEditorPane editor = getEditor(ae);
1844             if (editor != null) {
1845                 HTMLDocument doc = getHTMLDocument(editor);
1846                 int offset = editor.getSelectionStart();
1847                 Element paragraph = doc.getParagraphElement(offset);
1848                 if (paragraph.getParentElement() != null) {
1849                     parentTag = (HTML.Tag)paragraph.getParentElement().
1850                                   getAttributes().getAttribute


1865             Object nextKey = names.nextElement();
1866             Object nextVal = attr.getAttribute(nextKey);
1867             if (nextVal instanceof AttributeSet) {
1868                 Object value = getAttrValue((AttributeSet)nextVal, key);
1869                 if (value != null) {
1870                     return value;
1871                 }
1872             } else if (nextKey == key) {
1873                 return nextVal;
1874             }
1875         }
1876         return null;
1877     }
1878 
1879     /*
1880      * Action to move the focus on the next or previous hypertext link
1881      * or object. TODO: This method relies on support from the
1882      * javax.accessibility package.  The text package should support
1883      * keyboard navigation of text elements directly.
1884      */
1885     @SuppressWarnings("serial") // Superclass is not serializable across versions
1886     static class NavigateLinkAction extends TextAction implements CaretListener {
1887 
1888         private static final FocusHighlightPainter focusPainter =
1889             new FocusHighlightPainter(null);
1890         private final boolean focusBack;
1891 
1892         /*
1893          * Create this action with the appropriate identifier.
1894          */
1895         public NavigateLinkAction(String actionName) {
1896             super(actionName);
1897             focusBack = "previous-link-action".equals(actionName);
1898         }
1899 
1900         /**
1901          * Called when the caret position is updated.
1902          *
1903          * @param e the caret event
1904          */
1905         public void caretUpdate(CaretEvent e) {


2075                         Rectangle r = (shape instanceof Rectangle) ?
2076                             (Rectangle)shape : shape.getBounds();
2077                         g.drawRect(r.x, r.y, r.width - 1, r.height);
2078                         return r;
2079                     } catch (BadLocationException e) {
2080                         // can't render
2081                     }
2082                 }
2083                 // Only if exception
2084                 return null;
2085             }
2086         }
2087     }
2088 
2089     /*
2090      * Action to activate the hypertext link that has focus.
2091      * TODO: This method relies on support from the
2092      * javax.accessibility package.  The text package should support
2093      * keyboard navigation of text elements directly.
2094      */
2095     @SuppressWarnings("serial") // Superclass is not serializable across versions
2096     static class ActivateLinkAction extends TextAction {
2097 
2098         /**
2099          * Create this action with the appropriate identifier.
2100          */
2101         public ActivateLinkAction(String actionName) {
2102             super(actionName);
2103         }
2104 
2105         /*
2106          * activates the hyperlink at offset
2107          */
2108         private void activateLink(String href, HTMLDocument doc,
2109                                   JEditorPane editor, int offset) {
2110             try {
2111                 URL page =
2112                     (URL)doc.getProperty(Document.StreamDescriptionProperty);
2113                 URL url = new URL(page, href);
2114                 HyperlinkEvent linkEvent = new HyperlinkEvent
2115                     (editor, HyperlinkEvent.EventType.


2249             }
2250         }
2251     }
2252 
2253     private static int getBodyElementStart(JTextComponent comp) {
2254         Element rootElement = comp.getDocument().getRootElements()[0];
2255         for (int i = 0; i < rootElement.getElementCount(); i++) {
2256             Element currElement = rootElement.getElement(i);
2257             if("body".equals(currElement.getName())) {
2258                 return currElement.getStartOffset();
2259             }
2260         }
2261         return 0;
2262     }
2263 
2264     /*
2265      * Move the caret to the beginning of the document.
2266      * @see DefaultEditorKit#beginAction
2267      * @see HTMLEditorKit#getActions
2268      */
2269     @SuppressWarnings("serial") // Superclass is not serializable across versions
2270     static class BeginAction extends TextAction {
2271 
2272         /* Create this object with the appropriate identifier. */
2273         BeginAction(String nm, boolean select) {
2274             super(nm);
2275             this.select = select;
2276         }
2277 
2278         /** The operation to perform when this action is triggered. */
2279         public void actionPerformed(ActionEvent e) {
2280             JTextComponent target = getTextComponent(e);
2281             int bodyStart = getBodyElementStart(target);
2282 
2283             if (target != null) {
2284                 if (select) {
2285                     target.moveCaretPosition(bodyStart);
2286                 } else {
2287                     target.setCaretPosition(bodyStart);
2288                 }
2289             }