Class JEditorPane

All Implemented Interfaces:
ImageObserver, MenuContainer, Serializable, Accessible, Scrollable
Direct Known Subclasses:
JTextPane

public class JEditorPane extends JTextComponent
Emulator for JEditorPane, rendered by a SJEditorPane (RTE) peer, editable by default like the JDK class. Decided per D_jeditorpane / SD_sjeditorpane_rte (Shape S: always-RTE, single peer); the read-only Div peer is gone.

R_leaf_peer_lockdown status: non-leaf, keeps the protected peer ctor

JDK JTextPane extends JEditorPane, so JEditorPane is non-leaf in the public Swing hierarchy. Per R_leaf_peer_lockdown we keep the protected (Component peer) ctor open as the D_peer_ctor_injection seam for a future JTextPane (or other subclass) to pass its own peer through. The class stays non-final.

R_swing_is_truth two-way sync (editable)

The base JTextComponent keeps a Document as the emulator-side source of truth and only auto-syncs it to TextFieldBase peers — RTE is not one, so JEditorPane wires both directions itself:
  • Swing → peer. setText chains super.setText(t) (mutating the Document, firing user DocumentListeners) and pushes the markup into RTE's htmlValue via peer.setText. Direct Document mutation (e.g. getDocument().insertString(...)) does NOT reach the peer — that path needs an HTMLEditorKit pipeline that's permanently OOS per R_match_swing_errors sub-bucket (b).
  • peer → Swing. Browser edits fire RTE's value-change (EAGER, so the Document tracks typing per R_swing_is_truth's "updates while typing" contract); the listener mirrors the HTML into the Document under preventPeerEvents (R_swing_is_truth feedback guard), wrapped in EHelper.callSwing per R_callswing_envelope. So getText() reflects user edits and DocumentListeners fire.

Lossy HTML subset (SD_sjeditorpane_rte)

RTE renders/accepts only its Quill subset — no tables, no arbitrary CSS, base64-only images. This is now the accepted regression in display too (the Div peer that rendered any HTML is deleted). The emulator's Document still holds the faithful markup, so getText() is exact; only the rendered peer is lossy. Details in SJEditorPane.

Content type, page and document: the JDK's

The content type is the installed kit's, the page is the document's StreamDescriptionProperty, and a kit change brings a new, empty document — so the content type goes first. setPage(java.net.URL) is the JDK's body over the protected getStream(URL) hook, fetching on the server and synchronously, and skipping a URL that is the same file as the loaded one. None of it reads the peer (D_emulator_owned_state).

HTML element model

A text/html pane's getDocument() is a HTMLDocument — a read-only projection of the markup, reparsed on every change (D_htmldocument). So the JDK-shaped introspection code works:
pane.setContentType("text/html");
pane.setText("<h2>Report</h2><p id=\"total\">42</p>");
Element total = ((HTMLDocument) pane.getDocument()).getElement("total");
getText() keeps returning markup — the document's own text is the rendered text, as in Swing — and the document's HTML mutators WARN, so writes still go through setText. A text/plain pane keeps a PlainDocument. addHyperlinkListener(HyperlinkListener) delivers ACTIVATED when the user clicks a link in a non-editable pane, with source rebound to this emulator. The peer-side plumbing lives on SJEditorPane; the non-editable restriction is Swing's own (HTMLEditorKit.LinkController).

EditorKit: the stylesheet, not the rendering pipeline

setEditorKit(EditorKit) accepts a HTMLEditorKit so that kit.getStyleSheet().addRule(…) styles the pane's rendered content (D_htmleditorkit). The kit's rendering half stays out of scope — Vaadin renders the HTML, and the JDK path is Graphics-based (R_match_swing_errors sub-bucket (b)) — so a custom ViewFactory is inert, and installing a kit does not swap the document (the content type does, see above).

What drops-and-WARNs

setEditorKit with any other kind of kit, getEditorKit before one is installed, and createEditorKitForContentType / getEditorKitForContentType — content-type-driven kit lookup has nothing to return.
See Also:
  • Field Details

  • Constructor Details

    • JEditorPane

      public JEditorPane()
    • JEditorPane

      public JEditorPane(String type, String text)
      (String type, String text) JDK ctor — sets the content type first so setText writes against the right mime default, then writes the initial markup.
    • JEditorPane

      public JEditorPane(URL initialPage) throws IOException
      URL-loading ctor — fetches the URL synchronously via setPage(java.net.URL). IOException propagates per Swing's signature.
      Throws:
      IOException
    • JEditorPane

      public JEditorPane(String url) throws IOException
      URL-string-loading ctor — parses the string and dispatches to setPage(java.lang.String).
      Throws:
      IOException
    • JEditorPane

      protected JEditorPane(com.vaadin.flow.component.Component peer)
      R_leaf_peer_lockdown D_peer_ctor_injection seam — kept protected because JTextPane is a JDK subclass. Subclasses passing a non-SJEditorPane peer lose the setText-forwarding and edit-sync the public ctors wire; that's their problem to handle (matches the JTextField precedent).
    • JEditorPane

      protected <P extends com.vaadin.flow.component.Component> JEditorPane(Class<P> peerType, Supplier<? extends P> peerFactory)
  • Method Details

    • wirePeerToDocument

      protected void wirePeerToDocument(com.vaadin.swingbridge.surrogates.SJEditorPane editor)
      Subscribes to browser edits and mirrors them into the Document (EAGER per R_swing_is_truth's "tracks typing", callSwing per R_callswing_envelope). Overridable seam: JTextPane drives its two-way sync through RTE's Delta value instead of HTML, so it replaces this wiring rather than adding a second, conflicting listener.
    • setText

      public void setText(String t)
      Description copied from class: JTextComponent
      Replaces the Document's text in one replace, so a DocumentFilter sees a replace; a Document that is not an AbstractDocument gets a remove and an insert.
      Overrides:
      setText in class JTextComponent
    • getText

      public String getText()
      The pane's markup — HTML for an HTML pane, exactly as setText / setPage / the last browser edit left it.

      Overridden because the base reads the document, and an HTMLDocument's text is the rendered text ("Report one two"), not the markup. Swing's own JEditorPane serializes via kit.write() instead, which we deliberately don't copy: its HTML-3.2 dialect (<font>, <b>, align=) would rewrite the caller's markup on every read.

      Overrides:
      getText in class JTextComponent
    • pushTextToPeer

      protected void pushTextToPeer(String t)
      Pushes setText's markup into RTE's htmlValue under preventPeerEvents (so the peer's value-change listener doesn't re-mirror our own write back into the Document). Overridable seam: JTextPane pushes through its Delta serializer instead, driven by the Document mutation super.setText already made, so it overrides this to a no-op.
    • setEditable

      public void setEditable(boolean b)
      Overrides:
      setEditable in class JTextComponent
    • setContentType

      public final void setContentType(String type)
      Sets the type of content, installing the kit the JDK would: the type up to any ";" parameter list, if one of text/plain, text/html, text/rtf, application/rtf, else text/plain. A charset parameter on a text/ type becomes the "charset" client property.

      A different kit brings its own empty document, as in the JDK, so a setText before setContentType is lost:

      pane.setContentType("text/html");   // first: installs the HTMLDocument
      pane.setText("<h2>Report</h2>");
      
      Setting the installed registered type again changes nothing; an unregistered type installs a fresh document every time.
      Throws:
      NullPointerException - if type is null
    • getContentType

      public final String getContentType()
      The installed kit's content type. It is not the argument of setContentType(String): "ADMIN" reads back as "text/plain", and a parameter list never survives.
    • addHyperlinkListener

      public void addHyperlinkListener(HyperlinkListener listener)
      Register a listener for link activation on a read-only pane. The event's source is this JEditorPane — not the peer — so JDK-shaped user code casting e.getSource() works. getURL() / getDescription() carry what Swing's LinkController would supply; the source Element is always null (no HTMLDocument element model). Delivery is ACTIVATED-only, and only while !isEditable() — both matching Swing.
    • removeHyperlinkListener

      public void removeHyperlinkListener(HyperlinkListener listener)
    • getHyperlinkListeners

      public HyperlinkListener[] getHyperlinkListeners()
    • fireHyperlinkUpdate

      public void fireHyperlinkUpdate(HyperlinkEvent e)
      Fan out to this emulator's registered listeners, in registration order. Public to match the JDK, where it is both the internal dispatch point and a documented way for user code to synthesize an event.
    • setPage

      public void setPage(URL page) throws IOException
      Loads page, the JDK's way: the stream comes from getStream(URL), which also sets the content type from the connection, and the page is read into a new document of that type's kit. A page that is the same file as the loaded one is not fetched again — only its reference, if any, is scrolled to — so a reload clears the page first:
      pane.getDocument().putProperty(Document.StreamDescriptionProperty, null);
      pane.setPage(url);
      

      The fetch runs on the server, and synchronously: the JDK loads an HTML page on a background thread, so its "page" event comes after this returns, where here it comes before.

      Throws:
      IOException - if page is null ("invalid url") or the fetch fails — after the content type has switched, as in the JDK
    • getStream

      protected InputStream getStream(URL page) throws IOException
      Opens the connection, sets the content type from it, and returns its stream. A migrator's override (authentication, a custom resolver) is called from setPage(URL) as on the desktop. Redirects are not followed, as in the JDK.
      Throws:
      IOException
    • scrollToReference

      public void scrollToReference(String reference)
      Scrolls to the named anchor — which needs view geometry only the browser has, so it WARNs. setPage(URL) calls it for a URL with a reference, as the JDK's does.
    • setPage

      public void setPage(String url) throws IOException
      Parses url and loads it.
      Throws:
      IOException - if url is null ("invalid url") or not a URL (MalformedURLException, as the JDK's new URL(String) throws)
    • getPage

      public URL getPage()
      The page loaded into the current document — its StreamDescriptionProperty.
    • setEditorKit

      public void setEditorKit(EditorKit kit)
      Install an editor kit. Every kit is stored and read back by getEditorKit(); only a HTMLEditorKit additionally takes effect, its stylesheet binding to the peer so rules added to it (before or after this call) style the pane's content. Any other kind WARNs — without a route to the Vaadin peer, installing it could only pretend.

      Two JDK behaviours are deliberately not reproduced. The document is left alone — the JDK replaces it with kit.createDefaultDocument(), whereas here the element model follows the content type instead (setContentType(String)), so installing a kit never silently changes what getDocument() returns. And the content type is left alone; set it explicitly if it matters.

    • getEditorKit

      public EditorKit getEditorKit()
      Returns:
      the installed kit, or null when none was set — where the JDK would manufacture one from the content type, which here would drive nothing (R_match_swing_errors sub-bucket (b))
    • getEditorKitForContentType

      public EditorKit getEditorKitForContentType(String type)
    • setEditorKitForContentType

      public void setEditorKitForContentType(String type, EditorKit k)
    • createEditorKitForContentType

      public static EditorKit createEditorKitForContentType(String type)
    • getUIClassID

      public String getUIClassID()
      Overrides:
      getUIClassID in class JComponent
    • createDefaultDocument

      protected Document createDefaultDocument()
      Description copied from class: JTextComponent
      Factory for the default Document. Real Swing subclasses override to pick richer Document models (the EditorKit-driven path in JEditorPane, etc.). Our base picks PlainDocument — the simplest line- oriented model, and what JTextField would pick anyway.
      Overrides:
      createDefaultDocument in class JTextComponent