Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Microoptimization for converting JSoup DOM to W3C DOM

AuthorDaveJarvis <email>
Date2020-11-11 22:16:42 GMT-0800
Commit92db824950bfb820154c593e2386c3a91fc8561d
Parentd8ac1c2
Delta99 lines added, 45 lines removed, 54-line increase
src/main/java/com/keenwrite/preview/DomConverter.java
+package com.keenwrite.preview;
+
+import org.jsoup.helper.W3CDom;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Responsible for converting JSoup document object model (DOM) to a W3C DOM.
+ * Provides a lighter implementation than the superclass by overriding the
+ * {@link #fromJsoup(org.jsoup.nodes.Document)} method to reuse factories,
+ * builders, and implementations.
+ */
+class DomConverter extends W3CDom {
+ private final static DocumentBuilderFactory DOCUMENT_FACTORY;
+ private static DocumentBuilder DOCUMENT_BUILDER;
+ private static DOMImplementation DOM_IMPL;
+
+ static {
+ DOCUMENT_FACTORY = DocumentBuilderFactory.newInstance();
+ DOCUMENT_FACTORY.setNamespaceAware( true );
+
+ try {
+ DOCUMENT_BUILDER = DOCUMENT_FACTORY.newDocumentBuilder();
+ DOM_IMPL = DOCUMENT_BUILDER.getDOMImplementation();
+ } catch( final Exception ignored ) {
+ }
+ }
+
+ @Override
+ public Document fromJsoup( final org.jsoup.nodes.Document in ) {
+ assert in != null;
+ assert DOCUMENT_BUILDER != null;
+ assert DOM_IMPL != null;
+
+ final var out = DOCUMENT_BUILDER.newDocument();
+ final org.jsoup.nodes.DocumentType doctype = in.documentType();
+
+ if( doctype != null ) {
+ out.appendChild(
+ DOM_IMPL.createDocumentType(
+ doctype.name(),
+ doctype.publicId(),
+ doctype.systemId()
+ )
+ );
+ }
+
+ out.setXmlStandalone( true );
+ convert( in, out );
+ return out;
+ }
+}
src/main/java/com/keenwrite/preview/HtmlPanel.java
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
-import org.jsoup.helper.W3CDom;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.render.Box;
* editor.
*/
- private static class HyperlinkListener extends LinkListener {
+ private static final class HyperlinkListener extends LinkListener {
@Override
public void linkClicked( final BasicPanel panel, final String link ) {
try {
switch( getProtocol( link ) ) {
- case HTTP:
+ case HTTP -> {
final var desktop = getDesktop();
if( desktop.isSupported( BROWSE ) ) {
desktop.browse( new URI( link ) );
}
- break;
- case FILE:
+ }
+ case FILE -> {
// TODO: #88 -- publish a message to the event bus.
- break;
+ }
}
} catch( final Exception ex ) {
clue( ex );
}
}
}
- private static final W3CDom W3C_DOM = new W3CDom();
+ private static final DomConverter CONVERTER = new DomConverter();
private static final XhtmlNamespaceHandler XNH = new XhtmlNamespaceHandler();
+ /**
+ *
+ */
public HtmlPanel() {
addDocumentListener( new DocumentEventHandler() );
removeMouseTrackingListeners();
addMouseTrackingListener( new HyperlinkListener() );
}
+ /**
+ * Updates the document model displayed by the renderer. Effectively, this
+ * updates the HTML document to provide new content.
+ *
+ * @param html A complete HTML5 document, including doctype.
+ * @param baseUri URI to use for finding relative files, such as images.
+ */
public void render( final String html, final String baseUri ) {
- setDocument( W3C_DOM.fromJsoup( parse( html ) ), baseUri, XNH );
+ setDocument( CONVERTER.fromJsoup( parse( html ) ), baseUri, XNH );
}
src/main/java/com/keenwrite/preview/HtmlPreview.java
import javafx.embed.swing.SwingNode;
-import javafx.scene.Node;
import org.xhtmlrenderer.render.Box;
import org.xhtmlrenderer.swing.SwingReplacedElementFactory;
public void clear() {
process( "" );
+ }
+
+ /**
+ * Sets the base URI to the containing directory the file being edited.
+ *
+ * @param path The path to the file being edited.
+ */
+ public void setBaseUri( final Path path ) {
+ final var parent = path.getParent();
+ mBaseUriPath = parent == null ? "" : parent.toUri().toString();
+ mBaseUriHtml = format( HTML_BASE, mBaseUriPath );
+ }
+
+ public void repaintScrollPane() {
+ getScrollPane().repaint();
}
private void scrollTo( final Point point ) {
mView.scrollTo( point );
- }
-
- private String decorate( final String html ) {
- // Trim the HTML back to only the prefix.
- mHtmlDocument.setLength( HTML_PREFIX_LENGTH );
-
- // Write the HTML body element followed by closing tags.
- return mHtmlDocument.append( mBaseUriHtml )
- .append( HTML_HEAD_CLOSE )
- .append( html )
- .append( HTML_TAIL )
- .toString();
- }
-
- /**
- * Sets the base URI to the containing directory the file being edited.
- *
- * @param path The path to the file being edited.
- */
- public void setBaseUri( final Path path ) {
- final var parent = path.getParent();
- mBaseUriPath = parent == null ? "" : parent.toUri().toString();
- mBaseUriHtml = format( HTML_BASE, mBaseUriPath );
- }
-
- /**
- * Content to embed in a panel.
- *
- * @return The content to display to the user.
- */
- public Node getNode() {
- return this;
- }
-
- public void repaintScrollPane() {
- getScrollPane().repaint();
}
return new Point( x, y );
+ }
+
+ private String decorate( final String html ) {
+ // Trim the HTML back to only the prefix.
+ mHtmlDocument.setLength( HTML_PREFIX_LENGTH );
+
+ // Write the HTML body element followed by closing tags.
+ return mHtmlDocument.append( mBaseUriHtml )
+ .append( HTML_HEAD_CLOSE )
+ .append( html )
+ .append( HTML_TAIL )
+ .toString();
}