|
Java example source code file (HTMLEditorKit.java)
The HTMLEditorKit.java Java example source code/* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text.html; import sun.awt.AppContext; import java.lang.reflect.Method; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import javax.swing.text.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.plaf.TextUI; import java.util.*; import javax.accessibility.*; import java.lang.ref.*; /** * The Swing JEditorPane text component supports different kinds * of content via a plug-in mechanism called an EditorKit. Because * HTML is a very popular format of content, some support is provided * by default. The default support is provided by this class, which * supports HTML version 3.2 (with some extensions), and is migrating * toward version 4.0. * The <applet> tag is not supported, but some support is provided * for the <object> tag. * <p> * There are several goals of the HTML EditorKit provided, that have * an effect upon the way that HTML is modeled. These * have influenced its design in a substantial way. * <dl> * <dt> * Support editing * <dd> * It might seem fairly obvious that a plug-in for JEditorPane * should provide editing support, but that fact has several * design considerations. There are a substantial number of HTML * documents that don't properly conform to an HTML specification. * These must be normalized somewhat into a correct form if one * is to edit them. Additionally, users don't like to be presented * with an excessive amount of structure editing, so using traditional * text editing gestures is preferred over using the HTML structure * exactly as defined in the HTML document. * <p> * The modeling of HTML is provided by the class <code>HTMLDocument. * Its documentation describes the details of how the HTML is modeled. * The editing support leverages heavily off of the text package. * * <dt> * Extendable/Scalable * <dd> * To maximize the usefulness of this kit, a great deal of effort * has gone into making it extendable. These are some of the * features. * <ol> * <li> * The parser is replaceable. The default parser is the Hot Java * parser which is DTD based. A different DTD can be used, or an * entirely different parser can be used. To change the parser, * reimplement the getParser method. The default parser is * dynamically loaded when first asked for, so the class files * will never be loaded if an alternative parser is used. The * default parser is in a separate package called parser below * this package. * <li> * The parser drives the ParserCallback, which is provided by * HTMLDocument. To change the callback, subclass HTMLDocument * and reimplement the createDefaultDocument method to return * document that produces a different reader. The reader controls * how the document is structured. Although the Document provides * HTML support by default, there is nothing preventing support of * non-HTML tags that result in alternative element structures. * <li> * The default view of the models are provided as a hierarchy of * View implementations, so one can easily customize how a particular * element is displayed or add capabilities for new kinds of elements * by providing new View implementations. The default set of views * are provided by the <code>HTMLFactory class. This can * be easily changed by subclassing or replacing the HTMLFactory * and reimplementing the getViewFactory method to return the alternative * factory. * <li> * The View implementations work primarily off of CSS attributes, * which are kept in the views. This makes it possible to have * multiple views mapped over the same model that appear substantially * different. This can be especially useful for printing. For * most HTML attributes, the HTML attributes are converted to CSS * attributes for display. This helps make the View implementations * more general purpose * </ol> * * <dt> * Asynchronous Loading * <dd> * Larger documents involve a lot of parsing and take some time * to load. By default, this kit produces documents that will be * loaded asynchronously if loaded using <code>JEditorPane.setPage. * This is controlled by a property on the document. The method * {@link #createDefaultDocument createDefaultDocument} can * be overriden to change this. The batching of work is done * by the <code>HTMLDocument.HTMLReader class. The actual * work is done by the <code>DefaultStyledDocument and * <code>AbstractDocument classes in the text package. * * <dt> * Customization from current LAF * <dd> * HTML provides a well known set of features without exactly * specifying the display characteristics. Swing has a theme * mechanism for its look-and-feel implementations. It is desirable * for the look-and-feel to feed display characteristics into the * HTML views. An user with poor vision for example would want * high contrast and larger than typical fonts. * <p> * The support for this is provided by the <code>StyleSheet * class. The presentation of the HTML can be heavily influenced * by the setting of the StyleSheet property on the EditorKit. * * <dt> * Not lossy * <dd> * An EditorKit has the ability to be read and save documents. * It is generally the most pleasing to users if there is no loss * of data between the two operation. The policy of the HTMLEditorKit * will be to store things not recognized or not necessarily visible * so they can be subsequently written out. The model of the HTML document * should therefore contain all information discovered while reading the * document. This is constrained in some ways by the need to support * editing (i.e. incorrect documents sometimes must be normalized). * The guiding principle is that information shouldn't be lost, but * some might be synthesized to produce a more correct model or it might * be rearranged. * </dl> * * @author Timothy Prinzing */ public class HTMLEditorKit extends StyledEditorKit implements Accessible { private JEditorPane theEditor; /** * Constructs an HTMLEditorKit, creates a StyleContext, * and loads the style sheet. */ public HTMLEditorKit() { } /** * Get the MIME type of the data that this * kit represents support for. This kit supports * the type <code>text/html. * * @return the type */ public String getContentType() { return "text/html"; } /** * Fetch a factory that is suitable for producing * views of any models that are produced by this * kit. * * @return the factory */ public ViewFactory getViewFactory() { return defaultFactory; } /** * Create an uninitialized text storage model * that is appropriate for this type of editor. * * @return the model */ public Document createDefaultDocument() { StyleSheet styles = getStyleSheet(); StyleSheet ss = new StyleSheet(); ss.addStyleSheet(styles); HTMLDocument doc = new HTMLDocument(ss); doc.setParser(getParser()); doc.setAsynchronousLoadPriority(4); doc.setTokenThreshold(100); return doc; } /** * Try to get an HTML parser from the document. If no parser is set for * the document, return the editor kit's default parser. It is an error * if no parser could be obtained from the editor kit. */ private Parser ensureParser(HTMLDocument doc) throws IOException { Parser p = doc.getParser(); if (p == null) { p = getParser(); } if (p == null) { throw new IOException("Can't load parser"); } return p; } /** * Inserts content from the given stream. If <code>doc is * an instance of HTMLDocument, this will read * HTML 3.2 text. 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 * @exception IOException on any I/O error * @exception BadLocationException if pos represents an invalid * location within the document * @exception RuntimeException (will eventually be a BadLocationException) * if pos is invalid */ public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { if (doc instanceof HTMLDocument) { HTMLDocument hdoc = (HTMLDocument) doc; if (pos > doc.getLength()) { throw new BadLocationException("Invalid location", pos); } Parser p = ensureParser(hdoc); ParserCallback receiver = hdoc.getReader(pos); Boolean ignoreCharset = (Boolean)doc.getProperty("IgnoreCharsetDirective"); p.parse(in, receiver, (ignoreCharset == null) ? false : ignoreCharset.booleanValue()); receiver.flush(); } else { super.read(in, doc, pos); } } /** * Inserts HTML into an existing document. * * @param doc the document to insert into * @param offset the offset to insert HTML at * @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 inserting, but after the end tags have been generated * @param insertTag the first tag to start inserting into document * @exception RuntimeException (will eventually be a BadLocationException) * if pos is invalid */ public void insertHTML(HTMLDocument doc, int offset, String html, int popDepth, int pushDepth, HTML.Tag insertTag) throws BadLocationException, IOException { if (offset > doc.getLength()) { throw new BadLocationException("Invalid location", offset); } Parser p = ensureParser(doc); ParserCallback receiver = doc.getReader(offset, popDepth, pushDepth, insertTag); Boolean ignoreCharset = (Boolean)doc.getProperty ("IgnoreCharsetDirective"); p.parse(new StringReader(html), receiver, (ignoreCharset == null) ? false : ignoreCharset.booleanValue()); receiver.flush(); } /** * Write content from a document to the given stream * in a format appropriate for this kind of content handler. * * @param out the stream to write to * @param doc the source for the write * @param pos the location in the document to fetch the * content * @param len the amount to write out * @exception IOException on any I/O error * @exception 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 w = new HTMLWriter(out, (HTMLDocument)doc, pos, len); w.write(); } else if (doc instanceof StyledDocument) { MinimalHTMLWriter w = new MinimalHTMLWriter(out, (StyledDocument)doc, pos, len); w.write(); } else { super.write(out, doc, pos, len); } } /** * Called when the kit is being installed into the * a JEditorPane. * * @param c the JEditorPane */ public void install(JEditorPane c) { c.addMouseListener(linkHandler); c.addMouseMotionListener(linkHandler); c.addCaretListener(nextLinkAction); super.install(c); theEditor = c; } /** * Called when the kit is being removed from the * JEditorPane. This is used to unregister any * listeners that were attached. * * @param c the JEditorPane */ public void deinstall(JEditorPane c) { c.removeMouseListener(linkHandler); c.removeMouseMotionListener(linkHandler); c.removeCaretListener(nextLinkAction); super.deinstall(c); theEditor = null; } /** * Default Cascading Style Sheet file that sets * up the tag views. */ public static final String DEFAULT_CSS = "default.css"; /** * 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. * This should be reimplemented to provide a finer granularity * if desired. */ public void setStyleSheet(StyleSheet s) { if (s == null) { AppContext.getAppContext().remove(DEFAULT_STYLES_KEY); } else { AppContext.getAppContext().put(DEFAULT_STYLES_KEY, s); } } /** * 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. */ public StyleSheet getStyleSheet() { AppContext appContext = AppContext.getAppContext(); StyleSheet defaultStyles = (StyleSheet) appContext.get(DEFAULT_STYLES_KEY); if (defaultStyles == null) { defaultStyles = new StyleSheet(); appContext.put(DEFAULT_STYLES_KEY, defaultStyles); try { InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS); Reader r = new BufferedReader( new InputStreamReader(is, "ISO-8859-1")); defaultStyles.loadRules(r, null); r.close(); } catch (Throwable e) { // on error we simply have no styles... the html // will look mighty wrong but still function. } } return defaultStyles; } /** * Fetch a resource relative to the HTMLEditorKit classfile. * If this is called on 1.2 the loading will occur under the * protection of a doPrivileged call to allow the HTMLEditorKit * to function when used in an applet. * * @param name the name of the resource, relative to the * HTMLEditorKit class * @return a stream representing the resource */ static InputStream getResourceAsStream(String name) { try { return ResourceLoader.getResourceAsStream(name); } catch (Throwable e) { // If the class doesn't exist or we have some other // problem we just try to call getResourceAsStream directly. return HTMLEditorKit.class.getResourceAsStream(name); } } /** * Fetches the command list for the editor. This is * the list of commands supported by the superclass * augmented by the collection of commands defined * locally for style operations. * * @return the command list */ public Action[] getActions() { return TextAction.augmentList(super.getActions(), this.defaultActions); } /** * Copies the key/values in <code>elements AttributeSet into * <code>set. This does not copy component, icon, or element * names attributes. Subclasses may wish to refine what is and what * isn't copied here. But be sure to first remove all the attributes that * are in <code>set. | View created * </tr> | |
---|---|---|
InlineView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
javax.swing.text.html.ParagraphView * </tr> | ||
ListView * </tr> | ||
ListView * </tr> | ||
ListView * </tr> | ||
ListView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
BlockView * </tr> | ||
ImageView * </tr> | ||
HRuleView * </tr> | ||
BRView * </tr> | ||
javax.swing.text.html.TableView * </tr> | ||
FormView * </tr> | ||
FormView * </tr> | ||
FormView * </tr> | ||
ObjectView * </tr> | ||
FrameSetView * </tr> | ||
FrameView * </tr> * </table> */ public static class HTMLFactory implements ViewFactory { /** * Creates a view from an element. * * @param elem the element * @return the view */ public View create(Element elem) { AttributeSet attrs = elem.getAttributes(); Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute); Object o = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute); if (o instanceof HTML.Tag) { HTML.Tag kind = (HTML.Tag) o; if (kind == HTML.Tag.CONTENT) { return new InlineView(elem); } else if (kind == HTML.Tag.IMPLIED) { String ws = (String) elem.getAttributes().getAttribute( CSS.Attribute.WHITE_SPACE); if ((ws != null) && ws.equals("pre")) { return new LineView(elem); } return new javax.swing.text.html.ParagraphView(elem); } else if ((kind == HTML.Tag.P) || (kind == HTML.Tag.H1) || (kind == HTML.Tag.H2) || (kind == HTML.Tag.H3) || (kind == HTML.Tag.H4) || (kind == HTML.Tag.H5) || (kind == HTML.Tag.H6) || (kind == HTML.Tag.DT)) { // paragraph return new javax.swing.text.html.ParagraphView(elem); } else if ((kind == HTML.Tag.MENU) || (kind == HTML.Tag.DIR) || (kind == HTML.Tag.UL) || (kind == HTML.Tag.OL)) { return new ListView(elem); } else if (kind == HTML.Tag.BODY) { return new BodyBlockView(elem); } else if (kind == HTML.Tag.HTML) { return new BlockView(elem, View.Y_AXIS); } else if ((kind == HTML.Tag.LI) || (kind == HTML.Tag.CENTER) || (kind == HTML.Tag.DL) || (kind == HTML.Tag.DD) || (kind == HTML.Tag.DIV) || (kind == HTML.Tag.BLOCKQUOTE) || (kind == HTML.Tag.PRE) || (kind == HTML.Tag.FORM)) { // vertical box return new BlockView(elem, View.Y_AXIS); } else if (kind == HTML.Tag.NOFRAMES) { return new NoFramesView(elem, View.Y_AXIS); } else if (kind==HTML.Tag.IMG) { return new ImageView(elem); } else if (kind == HTML.Tag.ISINDEX) { return new IsindexView(elem); } else if (kind == HTML.Tag.HR) { return new HRuleView(elem); } else if (kind == HTML.Tag.BR) { return new BRView(elem); } else if (kind == HTML.Tag.TABLE) { return new javax.swing.text.html.TableView(elem); } else if ((kind == HTML.Tag.INPUT) || (kind == HTML.Tag.SELECT) || (kind == HTML.Tag.TEXTAREA)) { return new FormView(elem); } else if (kind == HTML.Tag.OBJECT) { return new ObjectView(elem); } else if (kind == HTML.Tag.FRAMESET) { if (elem.getAttributes().isDefined(HTML.Attribute.ROWS)) { return new FrameSetView(elem, View.Y_AXIS); } else if (elem.getAttributes().isDefined(HTML.Attribute.COLS)) { return new FrameSetView(elem, View.X_AXIS); } throw new RuntimeException("Can't build a" + kind + ", " + elem + ":" + "no ROWS or COLS defined."); } else if (kind == HTML.Tag.FRAME) { return new FrameView(elem); } else if (kind instanceof HTML.UnknownTag) { return new HiddenTagView(elem); } else if (kind == HTML.Tag.COMMENT) { return new CommentView(elem); } else if (kind == HTML.Tag.HEAD) { // Make the head never visible, and never load its // children. For Cursor positioning, // getNextVisualPositionFrom is overriden to always return // the end offset of the element. return new BlockView(elem, View.X_AXIS) { public float getPreferredSpan(int axis) { return 0; } public float getMinimumSpan(int axis) { return 0; } public float getMaximumSpan(int axis) { return 0; } protected void loadChildren(ViewFactory f) { } public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { return a; } public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) { return getElement().getEndOffset(); } }; } else if ((kind == HTML.Tag.TITLE) || (kind == HTML.Tag.META) || (kind == HTML.Tag.LINK) || (kind == HTML.Tag.STYLE) || (kind == HTML.Tag.SCRIPT) || (kind == HTML.Tag.AREA) || (kind == HTML.Tag.MAP) || (kind == HTML.Tag.PARAM) || (kind == HTML.Tag.APPLET)) { return new HiddenTagView(elem); } } // If we get here, it's either an element we don't know about // or something from StyledDocument that doesn't have a mapping to HTML. String nm = (elementName != null) ? (String)elementName : elem.getName(); if (nm != null) { if (nm.equals(AbstractDocument.ContentElementName)) { return new LabelView(elem); } else if (nm.equals(AbstractDocument.ParagraphElementName)) { return new ParagraphView(elem); } else if (nm.equals(AbstractDocument.SectionElementName)) { return new BoxView(elem, View.Y_AXIS); } else if (nm.equals(StyleConstants.ComponentElementName)) { return new ComponentView(elem); } else if (nm.equals(StyleConstants.IconElementName)) { return new IconView(elem); } } // default to text display return new LabelView(elem); } static class BodyBlockView extends BlockView implements ComponentListener { public BodyBlockView(Element elem) { super(elem,View.Y_AXIS); } // reimplement major axis requirements to indicate that the // block is flexible for the body element... so that it can // be stretched to fill the background properly. protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) { r = super.calculateMajorAxisRequirements(axis, r); r.maximum = Integer.MAX_VALUE; return r; } protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { Container container = getContainer(); Container parentContainer; if (container != null && (container instanceof javax.swing.JEditorPane) && (parentContainer = container.getParent()) != null && (parentContainer instanceof javax.swing.JViewport)) { JViewport viewPort = (JViewport)parentContainer; if (cachedViewPort != null) { JViewport cachedObject = cachedViewPort.get(); if (cachedObject != null) { if (cachedObject != viewPort) { cachedObject.removeComponentListener(this); } } else { cachedViewPort = null; } } if (cachedViewPort == null) { viewPort.addComponentListener(this); cachedViewPort = new WeakReference<JViewport>(viewPort); } componentVisibleWidth = viewPort.getExtentSize().width; if (componentVisibleWidth > 0) { Insets insets = container.getInsets(); viewVisibleWidth = componentVisibleWidth - insets.left - getLeftInset(); //try to use viewVisibleWidth if it is smaller than targetSpan targetSpan = Math.min(targetSpan, viewVisibleWidth); } } else { if (cachedViewPort != null) { JViewport cachedObject = cachedViewPort.get(); if (cachedObject != null) { cachedObject.removeComponentListener(this); } cachedViewPort = null; } } super.layoutMinorAxis(targetSpan, axis, offsets, spans); } public void setParent(View parent) { //if parent == null unregister component listener if (parent == null) { if (cachedViewPort != null) { Object cachedObject; if ((cachedObject = cachedViewPort.get()) != null) { ((JComponent)cachedObject).removeComponentListener(this); } cachedViewPort = null; } } super.setParent(parent); } public void componentResized(ComponentEvent e) { if ( !(e.getSource() instanceof JViewport) ) { return; } JViewport viewPort = (JViewport)e.getSource(); if (componentVisibleWidth != viewPort.getExtentSize().width) { Document doc = getDocument(); if (doc instanceof AbstractDocument) { AbstractDocument document = (AbstractDocument)getDocument(); document.readLock(); try { layoutChanged(X_AXIS); preferenceChanged(null, true, true); } finally { document.readUnlock(); } } } } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } /* * we keep weak reference to viewPort if and only if BodyBoxView is listening for ComponentEvents * only in that case cachedViewPort is not equal to null. * we need to keep this reference in order to remove BodyBoxView from viewPort listeners. * */ private Reference<JViewport> cachedViewPort = null; private boolean isListening = false; private int viewVisibleWidth = Integer.MAX_VALUE; private int componentVisibleWidth = Integer.MAX_VALUE; } } // --- Action implementations ------------------------------ /** The bold action identifier */ public static final String BOLD_ACTION = "html-bold-action"; /** The italic action identifier */ public static final String ITALIC_ACTION = "html-italic-action"; /** The paragraph left indent action identifier */ public static final String PARA_INDENT_LEFT = "html-para-indent-left"; /** The paragraph right indent action identifier */ public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; /** The font size increase to next value action identifier */ public static final String FONT_CHANGE_BIGGER = "html-font-bigger"; /** The font size decrease to next value action identifier */ public static final String FONT_CHANGE_SMALLER = "html-font-smaller"; /** The Color choice action identifier The color is passed as an argument */ public static final String COLOR_ACTION = "html-color-action"; /** The logical style choice action identifier The logical style is passed in as an argument */ public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action"; /** * Align images at the top. */ public static final String IMG_ALIGN_TOP = "html-image-align-top"; /** * Align images in the middle. */ public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle"; /** * Align images at the bottom. */ public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom"; /** * Align images at the border. */ public static final String IMG_BORDER = "html-image-border"; /** HTML used when inserting tables. */ private static final String INSERT_TABLE_HTML = "<table border=1> | ||
Here is a short list of links related to this Java HTMLEditorKit.java source code file:
... this post is sponsored by my books ... | |
![]() #1 New Release! |
![]() FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.