diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/text/html/HTMLEditorKit.java | |
download | cbb-gcc-4.6.4-upstream.tar.bz2 cbb-gcc-4.6.4-upstream.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/text/html/HTMLEditorKit.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/html/HTMLEditorKit.java | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java new file mode 100644 index 000000000..e3505d3c2 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -0,0 +1,1520 @@ +/* HTMLEditorKit.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + + +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.awt.Cursor; +import java.awt.Point; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; + +import javax.swing.Action; +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; +import javax.swing.event.HyperlinkEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Element; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; +import javax.swing.text.StyledEditorKit; +import javax.swing.text.TextAction; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.parser.ParserDelegator; + +/* Move these imports here after javax.swing.text.html to make it compile + with jikes. */ +import gnu.javax.swing.text.html.parser.GnuParserDelegator; +import gnu.javax.swing.text.html.parser.HTML_401F; + +/** + * @author Lillian Angel (langel at redhat dot com) + */ +public class HTMLEditorKit + extends StyledEditorKit + implements Serializable, Cloneable, Accessible +{ + + /** + * Fires the hyperlink events on the associated component + * when needed. + */ + public static class LinkController + extends MouseAdapter + implements MouseMotionListener, Serializable + { + + /** + * The element of the last anchor tag. + */ + private Element lastAnchorElement; + + /** + * Constructor + */ + public LinkController() + { + super(); + } + + /** + * Dispatched when the mouse is clicked. If the component + * is read-only, then the clicked event is used to drive an + * attempt to follow the reference specified by a link + * + * @param e - the mouse event + */ + public void mouseClicked(MouseEvent e) + { + JEditorPane editor = (JEditorPane) e.getSource(); + if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e)) + { + Point loc = e.getPoint(); + int pos = editor.viewToModel(loc); + if (pos >= 0) + activateLink(pos, editor, e.getX(), e.getY()); + } + } + + /** + * Dispatched when the mouse is dragged on a component. + * + * @param e - the mouse event. + */ + public void mouseDragged(MouseEvent e) + { + // Nothing to do here. + } + + /** + * Dispatched when the mouse cursor has moved into the component. + * + * @param e - the mouse event. + */ + public void mouseMoved(MouseEvent e) + { + JEditorPane editor = (JEditorPane) e.getSource(); + HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit(); + if (! editor.isEditable()) + { + Document doc = editor.getDocument(); + if (doc instanceof HTMLDocument) + { + Cursor newCursor = kit.getDefaultCursor(); + HTMLDocument htmlDoc = (HTMLDocument) doc; + Point loc = e.getPoint(); + int pos = editor.viewToModel(loc); + Element el = htmlDoc.getCharacterElement(pos); + if (pos < el.getStartOffset() || pos >= el.getEndOffset()) + el = null; + if (el != null) + { + AttributeSet aAtts = (AttributeSet) + el.getAttributes().getAttribute(HTML.Tag.A); + if (aAtts != null) + { + if (el != lastAnchorElement) + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = el; + htmlDoc.updateSpecialClass(el, + HTML.Attribute.DYNAMIC_CLASS, + "hover"); + } + newCursor = kit.getLinkCursor(); + } + else + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = null; + } + } + else + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = null; + } + if (editor.getCursor() != newCursor) + { + editor.setCursor(newCursor); + } + } + } + } + + /** + * If the given position represents a link, then linkActivated is called + * on the JEditorPane. + * + * @param pos the position + * @param editor the editor pane + */ + protected void activateLink(int pos, JEditorPane editor) + { + activateLink(pos, editor); + } + + private void activateLink(int pos, JEditorPane editor, int x, int y) + { + // TODO: This is here for future extension for mapped links support. + // For the time beeing we implement simple hyperlinks. + Document doc = editor.getDocument(); + if (doc instanceof HTMLDocument) + { + HTMLDocument htmlDoc = (HTMLDocument) doc; + Element el = htmlDoc.getCharacterElement(pos); + AttributeSet atts = el.getAttributes(); + AttributeSet anchorAtts = + (AttributeSet) atts.getAttribute(HTML.Tag.A); + String href = null; + if (anchorAtts != null) + { + href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF); + htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS, + "visited"); + } + else + { + // TODO: Implement link maps here. + } + HyperlinkEvent event = null; + if (href != null) + event = createHyperlinkEvent(editor, htmlDoc, href, + anchorAtts, el); + if (event != null) + editor.fireHyperlinkUpdate(event); + } + + } + + /** + * Creates a HyperlinkEvent for the specified href and anchor if + * possible. If for some reason this won't work, return null. + * + * @param editor the editor + * @param doc the document + * @param href the href link + * @param anchor the anchor + * @param el the element + * + * @return the hyperlink event, or <code>null</code> if we couldn't + * create one + */ + private HyperlinkEvent createHyperlinkEvent(JEditorPane editor, + HTMLDocument doc, + String href, + AttributeSet anchor, + Element el) + { + URL url; + try + { + URL base = doc.getBase(); + url = new URL(base, href); + + } + catch (MalformedURLException ex) + { + url = null; + } + HyperlinkEvent ev; + if (doc.isFrameDocument()) + { + String target = null; + if (anchor != null) + target = (String) anchor.getAttribute(HTML.Attribute.TARGET); + if (target == null || target.equals("")) + target = doc.getBaseTarget(); + if (target == null || target.equals("")) + target = "_self"; + ev = new HTMLFrameHyperlinkEvent(editor, + HyperlinkEvent.EventType.ACTIVATED, + url, href, el, target); + } + else + { + ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, + url, href, el); + } + return ev; + } + } + + /** + * This class is used to insert a string of HTML into an existing + * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag) + * identifies the parent in the document to add the elements to. The second, (addTag), + * identifies that the first tag should be added to the document as seen in the string. + * The parser will generate all appropriate (opening/closing tags_ even if they are not + * in the HTML string passed in. + */ + public static class InsertHTMLTextAction + extends HTMLTextAction + { + + /** + * Tag in HTML to start adding tags from. + */ + protected HTML.Tag addTag; + + /** + * Alternate tag in HTML to start adding tags from if parentTag is + * not found and alternateParentTag is not found. + */ + protected HTML.Tag alternateAddTag; + + /** + * Alternate tag to check if parentTag is not found. + */ + protected HTML.Tag alternateParentTag; + + /** + * HTML to insert. + */ + protected String html; + + /** + * Tag to check for in the document. + */ + protected HTML.Tag parentTag; + + /** + * Initializes all fields. + * + * @param name - the name of the document. + * @param html - the html to insert + * @param parentTag - the parent tag to check for + * @param addTag - the tag to start adding from + */ + public InsertHTMLTextAction(String name, String html, + HTML.Tag parentTag, HTML.Tag addTag) + { + this(name, html, parentTag, addTag, null, null); + } + + /** + * Initializes all fields and calls super + * + * @param name - the name of the document. + * @param html - the html to insert + * @param parentTag - the parent tag to check for + * @param addTag - the tag to start adding from + * @param alternateParentTag - the alternate parent tag + * @param alternateAddTag - the alternate add tag + */ + public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, + HTML.Tag addTag, HTML.Tag alternateParentTag, + HTML.Tag alternateAddTag) + { + super(name); + // Fields are for easy access when the action is applied to an actual + // document. + this.html = html; + this.parentTag = parentTag; + this.addTag = addTag; + this.alternateParentTag = alternateParentTag; + this.alternateAddTag = alternateAddTag; + } + + /** + * HTMLEditorKit.insertHTML is called. If an exception is + * thrown, it is wrapped in a RuntimeException and thrown. + * + * @param editor - the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param html - + * the String to insert + * @param popDepth - + * the number of ElementSpec.EndTagTypes to generate before + * inserting + * @param pushDepth - + * the number of ElementSpec.StartTagTypes with a direction of + * ElementSpec.JoinNextDirection that should be generated before + * @param addTag - + * the first tag to start inserting into document + */ + protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset, + String html, int popDepth, int pushDepth, + HTML.Tag addTag) + { + try + { + super.getHTMLEditorKit(editor).insertHTML(doc, offset, html, + popDepth, pushDepth, addTag); + } + catch (IOException e) + { + throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e); + } + catch (BadLocationException ex) + { + throw (RuntimeException) new RuntimeException("BadLocationException: " + + offset).initCause(ex); + } + } + + /** + * Invoked when inserting at a boundary. Determines the number of pops, + * and then the number of pushes that need to be performed. The it calls + * insertHTML. + * + * @param editor - + * the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param insertElement - + * the element to insert + * @param html - + * the html to insert + * @param parentTag - + * the parent tag + * @param addTag - + * the first tag + */ + protected void insertAtBoundary(JEditorPane editor, + HTMLDocument doc, int offset, + Element insertElement, + String html, HTML.Tag parentTag, + HTML.Tag addTag) + { + insertAtBoundry(editor, doc, offset, insertElement, + html, parentTag, addTag); + } + + /** + * Invoked when inserting at a boundary. Determines the number of pops, + * and then the number of pushes that need to be performed. The it calls + * insertHTML. + * + * @param editor - the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param insertElement - the element to insert + * @param html - the html to insert + * @param parentTag - the parent tag + * @param addTag - the first tag + * + * @deprecated as of v1.3, use insertAtBoundary + */ + protected void insertAtBoundry(JEditorPane editor, + HTMLDocument doc, + int offset, Element insertElement, + String html, HTML.Tag parentTag, + HTML.Tag addTag) + { + Element parent = insertElement; + Element el; + // Find common parent element. + if (offset > 0 || insertElement == null) + { + el = doc.getDefaultRootElement(); + while (el != null && el.getStartOffset() != offset + && ! el.isLeaf()) + el = el.getElement(el.getElementIndex(offset)); + parent = el != null ? el.getParentElement() : null; + } + if (parent != null) + { + int pops = 0; + int pushes = 0; + if (offset == 0 && insertElement != null) + { + el = parent; + while (el != null && ! el.isLeaf()) + { + el = el.getElement(el.getElementIndex(offset)); + pops++; + } + } + else + { + el = parent; + offset--; + while (el != null && ! el.isLeaf()) + { + el = el.getElement(el.getElementIndex(offset)); + pops++; + } + el = parent; + offset++; + while (el != null && el != insertElement) + { + el = el.getElement(el.getElementIndex(offset)); + pushes++; + } + } + pops = Math.max(0, pops - 1); + insertHTML(editor, doc, offset, html, pops, pushes, addTag); + } + } + + /** + * Inserts the HTML. + * + * @param ae - the action performed + */ + public void actionPerformed(ActionEvent ae) + { + JEditorPane source = getEditor(ae); + if (source != null) + { + HTMLDocument d = getHTMLDocument(source); + int offset = source.getSelectionStart(); + int length = d.getLength(); + boolean inserted = true; + if (! tryInsert(source, d, offset, parentTag, addTag)) + { + inserted = tryInsert(source, d, offset, alternateParentTag, + alternateAddTag); + } + if (inserted) + adjustSelection(source, d, offset, length); + } + } + + /** + * Tries to insert the html chunk to the specified <code>addTag</code>. + * + * @param pane the editor + * @param doc the document + * @param offset the offset at which to insert + * @param tag the tag at which to insert + * @param addTag the add tag + * + * @return <code>true</code> when the html has been inserted successfully, + * <code>false</code> otherwise + */ + private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset, + HTML.Tag tag, HTML.Tag addTag) + { + boolean inserted = false; + Element el = findElementMatchingTag(doc, offset, tag); + if (el != null && el.getStartOffset() == offset) + { + insertAtBoundary(pane, doc, offset, el, html, tag, addTag); + inserted = true; + } + else if (offset > 0) + { + int depth = elementCountToTag(doc, offset - 1, tag); + if (depth != -1) + { + insertHTML(pane, doc, offset, html, depth, 0, addTag); + inserted = true; + } + } + return inserted; + } + + /** + * Adjusts the selection after an insertion has been performed. + * + * @param pane the editor pane + * @param doc the document + * @param offset the insert offset + * @param oldLen the old document length + */ + private void adjustSelection(JEditorPane pane, HTMLDocument doc, + int offset, int oldLen) + { + int newLen = doc.getLength(); + if (newLen != oldLen && offset < newLen) + { + if (offset > 0) + { + String text; + try + { + text = doc.getText(offset - 1, 1); + } + catch (BadLocationException ex) + { + text = null; + } + if (text != null && text.length() > 0 + && text.charAt(0) == '\n') + { + pane.select(offset, offset); + } + else + { + pane.select(offset + 1, offset + 1); + } + } + else + { + pane.select(1, 1); + } + } + } + } + + /** + * Abstract Action class that helps inserting HTML into an existing document. + */ + public abstract static class HTMLTextAction + extends StyledEditorKit.StyledTextAction + { + + /** + * Constructor + */ + public HTMLTextAction(String name) + { + super(name); + } + + /** + * Gets the HTMLDocument from the JEditorPane. + * + * @param e - the editor pane + * @return the html document. + */ + protected HTMLDocument getHTMLDocument(JEditorPane e) + { + Document d = e.getDocument(); + if (d instanceof HTMLDocument) + return (HTMLDocument) d; + throw new IllegalArgumentException("Document is not a HTMLDocument."); + } + + /** + * Gets the HTMLEditorKit + * + * @param e - the JEditorPane to get the HTMLEditorKit from. + * @return the HTMLEditorKit + */ + protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) + { + EditorKit d = e.getEditorKit(); + if (d instanceof HTMLEditorKit) + return (HTMLEditorKit) d; + throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit."); + } + + /** + * Returns an array of Elements that contain the offset. + * The first elements corresponds to the roots of the doc. + * + * @param doc - the document to get the Elements from. + * @param offset - the offset the Elements must contain + * @return an array of all the elements containing the offset. + */ + protected Element[] getElementsAt(HTMLDocument doc, + int offset) + { + return getElementsAt(doc.getDefaultRootElement(), offset, 0); + } + + /** + * Helper function to get all elements using recursion. + */ + private Element[] getElementsAt(Element root, int offset, int depth) + { + Element[] elements = null; + if (root != null) + { + if (root.isLeaf()) + { + elements = new Element[depth + 1]; + elements[depth] = root; + return elements; + } + elements = getElementsAt(root.getElement(root.getElementIndex(offset)), + offset, depth + 1); + elements[depth] = root; + } + return elements; + } + + /** + * Returns the number of elements, starting at the deepest point, needed + * to get an element representing tag. -1 if no elements are found, 0 if + * the parent of the leaf at offset represents the tag. + * + * @param doc - + * the document to search + * @param offset - + * the offset to check + * @param tag - + * the tag to look for + * @return - the number of elements needed to get an element representing + * tag. + */ + protected int elementCountToTag(HTMLDocument doc, + int offset, HTML.Tag tag) + { + Element root = doc.getDefaultRootElement(); + int num = -1; + Element next = root.getElement(root.getElementIndex(offset)); + + while (!next.isLeaf()) + { + num++; + if (next.getAttributes(). + getAttribute(StyleConstants.NameAttribute).equals(tag)) + return num; + next = next.getElement(next.getElementIndex(offset)); + } + return num; + } + + /** + * Gets the deepest element at offset with the + * matching tag. + * + * @param doc - the document to search + * @param offset - the offset to check for + * @param tag - the tag to match + * @return - the element that is found, null if not found. + */ + protected Element findElementMatchingTag(HTMLDocument doc, + int offset, HTML.Tag tag) + { + Element element = doc.getDefaultRootElement(); + Element tagElement = null; + + while (element != null) + { + Object otag = element.getAttributes().getAttribute( + StyleConstants.NameAttribute); + if (otag instanceof HTML.Tag && otag.equals(tag)) + tagElement = element; + element = element.getElement(element.getElementIndex(offset)); + } + + return tagElement; + } + } + + /** + * A {@link ViewFactory} that is able to create {@link View}s for + * the <code>Element</code>s that are supported. + */ + public static class HTMLFactory + implements ViewFactory + { + + /** + * Constructor + */ + public HTMLFactory() + { + // Do Nothing here. + } + + /** + * Creates a {@link View} for the specified <code>Element</code>. + * + * @param element the <code>Element</code> to create a <code>View</code> + * for + * @return the <code>View</code> for the specified <code>Element</code> + * or <code>null</code> if the type of <code>element</code> is + * not supported + */ + public View create(Element element) + { + View view = null; + Object attr = + element.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (attr instanceof HTML.Tag) + { + HTML.Tag tag = (HTML.Tag) attr; + + if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P + || tag == HTML.Tag.H1 || tag == HTML.Tag.H2 + || tag == HTML.Tag.H3 || tag == HTML.Tag.H4 + || tag == HTML.Tag.H5 || tag == HTML.Tag.H6 + || tag == HTML.Tag.DT) + view = new ParagraphView(element); + else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL + || tag == HTML.Tag.DD || tag == HTML.Tag.BODY + || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER + || tag == HTML.Tag.DIV + || tag == HTML.Tag.BLOCKQUOTE + || tag == HTML.Tag.PRE + || tag == HTML.Tag.FORM + // Misplaced TD and TH tags get mapped as vertical block. + // Note that correctly placed tags get mapped in TableView. + || tag == HTML.Tag.TD || tag == HTML.Tag.TH) + view = new BlockView(element, View.Y_AXIS); + else if (tag == HTML.Tag.TR) + // Misplaced TR tags get mapped as horizontal blocks. + // Note that correctly placed tags get mapped in TableView. + view = new BlockView(element, View.X_AXIS); + else if (tag == HTML.Tag.IMG) + view = new ImageView(element); + + else if (tag == HTML.Tag.CONTENT) + view = new InlineView(element); + else if (tag == HTML.Tag.HEAD) + view = new NullView(element); + else if (tag == HTML.Tag.TABLE) + view = new javax.swing.text.html.TableView(element); + else if (tag == HTML.Tag.HR) + view = new HRuleView(element); + else if (tag == HTML.Tag.BR) + view = new BRView(element); + else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT + || tag == HTML.Tag.TEXTAREA) + view = new FormView(element); + + else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR + || tag == HTML.Tag.UL || tag == HTML.Tag.OL) + view = new ListView(element); + else if (tag == HTML.Tag.FRAMESET) + view = new FrameSetView(element); + else if (tag == HTML.Tag.FRAME) + view = new FrameView(element); + else if (tag == HTML.Tag.OBJECT) + view = new ObjectView(element); + } + if (view == null) + { + view = new NullView(element); + } + return view; + } + } + + /** + * The abstract HTML parser declaration. + */ + public abstract static class Parser + { + /** + * Parse the HTML text, calling various methods of the provided callback + * in response to the occurence of the corresponding HTML constructions. + * @param reader The reader to read the source HTML from. + * @param callback The callback to receive information about the parsed + * HTML structures + * @param ignoreCharSet If true, the parser ignores all charset information + * that may be present in HTML documents. + * @throws IOException, normally if the reader throws one. + */ + public abstract void parse(Reader reader, ParserCallback callback, + boolean ignoreCharSet) throws IOException; + } + + /** + * The "hook" that receives all information about the HTML document + * structure while parsing it. The methods are invoked by parser + * and should be normally overridden. + */ + public static class ParserCallback + { + /** + * If the tag does not occurs in the html stream directly, but + * is supposed by parser, the tag attribute set contains this additional + * attribute, having value Boolean.True. + */ + public static final Object IMPLIED = "_implied_"; + + /** + * Constructor + */ + public ParserCallback() + { + // Nothing to do here. + } + + /** + * The parser calls this method after it finishes parsing the document. + */ + public void flush() throws BadLocationException + { + // Nothing to do here. + } + + /** + * Handle HTML comment, present in the given position. + * @param comment the comment + * @position the position of the comment in the text being parsed. + */ + public void handleComment(char[] comment, int position) + { + // Nothing to do here. + } + + /** + * Notifies about the character sequences, used to separate lines in + * this document. The parser calls this method after it finishes + * parsing the document, but before flush(). + * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n. + */ + public void handleEndOfLineString(String end_of_line) + { + // Nothing to do here. + } + + /** + * The method is called when the HTML closing tag ((like </table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + * @param position the tag position in the text being parsed. + */ + public void handleEndTag(HTML.Tag tag, int position) + { + // Nothing to do here. + } + + /** + * Handle the error. + * @param message The message, explaining the error. + * @param position The starting position of the fragment that has caused + * the error in the html document being parsed. + */ + public void handleError(String message, int position) + { + // Nothing to do here. + } + + /** + * Handle the tag with no content, like <br>. The method is + * called for the elements that, in accordance with the current DTD, + * has an empty content. + * @param tag The tag being handled. + * @param position The tag position in the text being parsed. + */ + public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, + int position) + { + // Nothing to do here. + } + + /** + * The method is called when the HTML opening tag ((like <table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + * @param position The tag position in the text being parsed + */ + public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, + int position) + { + // Nothing to do here. + } + + /** + * Handle the text section. + * @param text A section text. + * @param position The text position in the HTML document text being parsed. + */ + public void handleText(char[] text, int position) + { + // Nothing to do here. + } + } + + /** + * Use serialVersionUID (v1.4) for interoperability. + */ + private static final long serialVersionUID = 8751997116710384592L; + + /** + * Default cascading stylesheed file ("default.css"). + */ + public static final String DEFAULT_CSS = "default.css"; + + /** + * The <b>bold</b> action identifier. + */ + public static final String BOLD_ACTION = "html-bold-action"; + + /** + * The <i>italic</i> action identifier. + */ + public static final String ITALIC_ACTION = "html-italic-action"; + + /** + * The <font color="#FF0000">color</font> action indentifier + * (passing the color as an argument). + */ + public static final String COLOR_ACTION = "html-color-action"; + + /** + * The <font size="+1">increase</font> font action identifier. + */ + public static final String FONT_CHANGE_BIGGER = "html-font-bigger"; + + /** + * The <font size="-1">decrease</font> font action identifier. + */ + public static final String FONT_CHANGE_SMALLER = "html-font-smaller"; + + /** + * Align images at the bottom. + */ + public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom"; + + /** + * Align images at the middle. + */ + public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle"; + + /** + * Align images at the top. + */ + public static final String IMG_ALIGN_TOP = "html-image-align-top"; + + /** + * Align images at the border. + */ + public static final String IMG_BORDER = "html-image-border"; + + /** + * The "logical style" action identifier, passing that style as parameter. + */ + public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action"; + + /** + * The "ident paragraph left" action. + */ + public static final String PARA_INDENT_LEFT = "html-para-indent-left"; + + /** + * The "ident paragraph right" action. + */ + public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; + + /** + * Actions for HTML + */ + private static final Action[] defaultActions = + { + new InsertHTMLTextAction("InsertTable", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableRow", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.TABLE, HTML.Tag.TR, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableCell", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.TR, HTML.Tag.TD, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertUnorderedList", + "<ul><li></li></ul>", + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertUnorderedListItem", + "<ul><li></li></ul>", + HTML.Tag.UL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertOrderedList", + "<ol><li></li></ol>", + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertOrderedListItem", + "<ol><li></li></ol>", + HTML.Tag.OL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertPre", + "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE) + // TODO: The reference impl has an InsertHRAction too. + }; + + /** + * The current style sheet. + */ + private StyleSheet styleSheet; + + /** + * The ViewFactory for HTMLFactory. + */ + HTMLFactory viewFactory; + + /** + * The Cursor for links. + */ + Cursor linkCursor; + + /** + * The default cursor. + */ + Cursor defaultCursor; + + /** + * The parser. + */ + Parser parser; + + /** + * The mouse listener used for links. + */ + private LinkController linkController; + + /** The content type */ + String contentType = "text/html"; + + /** The input attributes defined by default.css */ + MutableAttributeSet inputAttributes; + + /** The editor pane used. */ + JEditorPane editorPane; + + /** + * Whether or not the editor kit handles form submissions. + * + * @see #isAutoFormSubmission() + * @see #setAutoFormSubmission(boolean) + */ + private boolean autoFormSubmission; + + /** + * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet. + */ + public HTMLEditorKit() + { + linkController = new LinkController(); + autoFormSubmission = true; + } + + /** + * Gets a factory suitable for producing views of any + * models that are produced by this kit. + * + * @return the view factory suitable for producing views. + */ + public ViewFactory getViewFactory() + { + if (viewFactory == null) + viewFactory = new HTMLFactory(); + return viewFactory; + } + + /** + * Create a text storage model for this type of editor. + * + * @return the model + */ + public Document createDefaultDocument() + { + // Protect the shared stylesheet. + StyleSheet styleSheet = getStyleSheet(); + StyleSheet ss = new StyleSheet(); + ss.addStyleSheet(styleSheet); + + HTMLDocument document = new HTMLDocument(ss); + document.setParser(getParser()); + document.setAsynchronousLoadPriority(4); + document.setTokenThreshold(100); + return document; + } + + /** + * Get the parser that this editor kit uses for reading HTML streams. This + * method can be overridden to use the alternative parser. + * + * @return the HTML parser (by default, {@link ParserDelegator}). + */ + protected Parser getParser() + { + if (parser == null) + { + parser = new GnuParserDelegator(HTML_401F.getInstance()); + } + return parser; + } + + /** + * Inserts HTML into an existing document. + * + * @param doc - the Document to insert the HTML into. + * @param offset - where to begin inserting the HTML. + * @param html - the String to insert + * @param popDepth - the number of ElementSpec.EndTagTypes + * to generate before inserting + * @param pushDepth - the number of ElementSpec.StartTagTypes + * with a direction of ElementSpec.JoinNextDirection that + * should be generated before + * @param insertTag - the first tag to start inserting into document + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void insertHTML(HTMLDocument doc, int offset, String html, + int popDepth, int pushDepth, HTML.Tag insertTag) + throws BadLocationException, IOException + { + Parser parser = getParser(); + if (offset < 0 || offset > doc.getLength()) + throw new BadLocationException("Bad location", offset); + if (parser == null) + throw new IOException("Parser is null."); + + ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag); + + // FIXME: What should ignoreCharSet be set to? + + // parser.parse inserts html into the buffer + parser.parse(new StringReader(html), pc, false); + pc.flush(); + } + + /** + * Inserts content from the given stream. Inserting HTML into a non-empty + * document must be inside the body Element, if you do not insert into + * the body an exception will be thrown. When inserting into a non-empty + * document all tags outside of the body (head, title) will be dropped. + * + * @param in - the stream to read from + * @param doc - the destination for the insertion + * @param pos - the location in the document to place the content + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void read(Reader in, Document doc, int pos) throws IOException, + BadLocationException + { + if (doc instanceof HTMLDocument) + { + Parser parser = getParser(); + if (pos < 0 || pos > doc.getLength()) + throw new BadLocationException("Bad location", pos); + if (parser == null) + throw new IOException("Parser is null."); + + HTMLDocument hd = ((HTMLDocument) doc); + if (editorPane != null) + hd.setBase(editorPane.getPage()); + ParserCallback pc = hd.getReader(pos); + + // FIXME: What should ignoreCharSet be set to? + + // parser.parse inserts html into the buffer + parser.parse(in, pc, false); + pc.flush(); + } + else + // read in DefaultEditorKit is called. + // the string is inserted in the document as usual. + super.read(in, doc, pos); + } + + /** + * Writes content from a document to the given stream in + * an appropriate format. + * + * @param out - the stream to write to + * @param doc - the source for the write + * @param pos - the location in the document to get the content. + * @param len - the amount to write out + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void write(Writer out, Document doc, int pos, int len) + throws IOException, BadLocationException + { + if (doc instanceof HTMLDocument) + { + HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len); + writer.write(); + } + else if (doc instanceof StyledDocument) + { + MinimalHTMLWriter writer = new MinimalHTMLWriter(out, + (StyledDocument) doc, + pos, len); + writer.write(); + } + else + super.write(out, doc, pos, len); + } + + /** + * Gets the content type that the kit supports. + * This kit supports the type text/html. + * + * @returns the content type supported. + */ + public String getContentType() + { + return contentType; + } + + /** + * Creates a copy of the editor kit. + * + * @return a copy of this. + */ + public Object clone() + { + // FIXME: Need to clone all fields + HTMLEditorKit copy = (HTMLEditorKit) super.clone(); + copy.linkController = new LinkController(); + return copy; + } + + /** + * Copies the key/values in elements AttributeSet into set. + * This does not copy component, icon, or element names attributes. + * This is called anytime the caret moves over a different location. + * + * @param element - the element to create the input attributes for. + * @param set - the set to copy the values into. + */ + protected void createInputAttributes(Element element, + MutableAttributeSet set) + { + set.removeAttributes(set); + set.addAttributes(element.getAttributes()); + // FIXME: Not fully implemented. + } + + /** + * Called when this is installed into the JEditorPane. + * + * @param c - the JEditorPane installed into. + */ + public void install(JEditorPane c) + { + super.install(c); + c.addMouseListener(linkController); + c.addMouseMotionListener(linkController); + editorPane = c; + } + + /** + * Called when the this is removed from the JEditorPane. + * It unregisters any listeners. + * + * @param c - the JEditorPane being removed from. + */ + public void deinstall(JEditorPane c) + { + super.deinstall(c); + c.removeMouseListener(linkController); + c.removeMouseMotionListener(linkController); + editorPane = null; + } + + /** + * Gets the AccessibleContext associated with this. + * + * @return the AccessibleContext for this. + */ + public AccessibleContext getAccessibleContext() + { + // FIXME: Should return an instance of + // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext + // Not implemented yet. + return null; + } + + /** + * Gets the action list. This list is supported by the superclass + * augmented by the collection of actions defined locally for style + * operations. + * + * @return an array of all the actions + */ + public Action[] getActions() + { + return TextAction.augmentList(super.getActions(), defaultActions); + } + + /** + * Returns the default cursor. + * + * @return the default cursor + */ + public Cursor getDefaultCursor() + { + if (defaultCursor == null) + defaultCursor = Cursor.getDefaultCursor(); + return defaultCursor; + } + + /** + * Returns the cursor for links. + * + * @return the cursor for links. + */ + public Cursor getLinkCursor() + { + if (linkCursor == null) + linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + return linkCursor; + } + + /** + * Sets the Cursor for links. + * + * @param cursor - the new cursor for links. + */ + public void setLinkCursor(Cursor cursor) + { + linkCursor = cursor; + } + + /** + * Sets the default cursor. + * + * @param cursor - the new default cursor. + */ + public void setDefaultCursor(Cursor cursor) + { + defaultCursor = cursor; + } + + /** + * Gets the input attributes used for the styled editing actions. + * + * @return the attribute set + */ + public MutableAttributeSet getInputAttributes() + { + return inputAttributes; + } + + /** + * Get the set of styles currently being used to render the HTML elements. + * By default the resource specified by DEFAULT_CSS gets loaded, and is + * shared by all HTMLEditorKit instances. + * + * @return the style sheet. + */ + public StyleSheet getStyleSheet() + { + if (styleSheet == null) + { + try + { + styleSheet = new StyleSheet(); + Class<?> c = HTMLEditorKit.class; + InputStream in = c.getResourceAsStream(DEFAULT_CSS); + InputStreamReader r = new InputStreamReader(in); + styleSheet.loadRules(r, null); + r.close(); + } + catch (IOException ex) + { + throw new RuntimeException("No style available.", ex); + } + } + return styleSheet; + } + + /** + * Set the set of styles to be used to render the various HTML elements. + * These styles are specified in terms of CSS specifications. Each document + * produced by the kit will have a copy of the sheet which it can add the + * document specific styles to. By default, the StyleSheet specified is shared + * by all HTMLEditorKit instances. + * + * @param s - the new style sheet + */ + public void setStyleSheet(StyleSheet s) + { + styleSheet = s; + } + + /** + * Returns <code>true</code> when forms should be automatically submitted + * by the editor kit. Set this to <code>false</code> when you want to + * intercept form submission. In this case you'd want to listen for + * hyperlink events on the document and handle FormSubmitEvents specially. + * + * The default is <code>true</code>. + * + * @return <code>true</code> when forms should be automatically submitted + * by the editor kit, <code>false</code> otherwise + * + * @since 1.5 + * + * @see #setAutoFormSubmission(boolean) + * @see FormSubmitEvent + */ + public boolean isAutoFormSubmission() + { + return autoFormSubmission; + } + + /** + * Sets whether or not the editor kit should automatically submit forms. + * + * @param auto <code>true</code> when the editor kit should handle form + * submission, <code>false</code> otherwise + * + * @since 1.5 + * + * @see #isAutoFormSubmission() + */ + public void setAutoFormSubmission(boolean auto) + { + autoFormSubmission = auto; + } +} |