Skip to content

Commit

Permalink
Refactor of AST traversal method (#944)
Browse files Browse the repository at this point in the history
* Solve limitations in the current method used to traverse the AST.
The new method does not look externally (outside of NodeProcessors) but instead injects NodeSinker into the NodeProcessors so each one decides if further traversal needs to happen. This has simplified the code notably, and removes the need for specific dependency injection.

* Changes in how HTML content is generated:
  * Empty document no longer generated empty <h1></h1>
  * Empty literal no longer generated empty <pre></pre>
  * Sections are now wrapped around <div> elements for better organization.
  • Loading branch information
abelsromero authored Oct 19, 2024
1 parent 4bbd255 commit 912de34
Show file tree
Hide file tree
Showing 34 changed files with 485 additions and 430 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Improvements::
* Added support for AsciidoctorJ v3.0.0 (#651)
* Add compatibility with maven-site-plugin v3.20.0 and Doxia v2.0.0 (#933)
* Add support for code blocks titles in asciidoctor-parser-doxia-module (#935)
* Refactor AST traversal method in asciidoctor-parser-doxia-module (#944)
* Empty titles in document or empty literals no longer generate <h1> or <pre> in asciidoctor-parser-doxia-module (#944)
* Sections are now wrapped in <div> in asciidoctor-parser-doxia-module (#944)

Build / Infrastructure::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* This allows Doxia to build:
* - breadcrumbs
* - HTML head's meta elements
*
* @since 3.0.0
*/
public class HeadParser {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.logging.Logger;

import org.apache.maven.doxia.parser.AbstractTextParser;
Expand All @@ -28,6 +29,7 @@
import org.asciidoctor.maven.site.SiteConversionConfiguration;
import org.asciidoctor.maven.site.SiteConversionConfigurationParser;
import org.asciidoctor.maven.site.SiteLogHandlerDeserializer;
import org.asciidoctor.maven.site.parser.processors.DescriptionListNodeProcessor;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.xml.Xpp3Dom;
Expand Down Expand Up @@ -82,7 +84,7 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
final Asciidoctor asciidoctor = Asciidoctor.Factory.create();

SiteConversionConfiguration conversionConfig = new SiteConversionConfigurationParser(project)
.processAsciiDocConfig(siteConfig, defaultOptions(siteDirectory), defaultAttributes());
.processAsciiDocConfig(siteConfig, defaultOptions(siteDirectory), defaultAttributes());
for (String require : conversionConfig.getRequires()) {
requireLibrary(asciidoctor, require);
}
Expand All @@ -98,7 +100,7 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
try {
// process log messages according to mojo configuration
new LogRecordsProcessors(logHandler, siteDirectory, errorMessage -> logger.error(errorMessage))
.processLogRecords(memoryLogHandler);
.processLogRecords(memoryLogHandler);

} catch (Exception exception) {
throw new ParseException(exception.getMessage(), exception);
Expand All @@ -110,10 +112,8 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
new HeadParser(sink)
.parse(headerMetadata);

sink.body();
final NodesSinker nodesSinker = new NodesSinker(sink);
nodesSinker.processNode(document);
sink.body_();
new NodeSinker(sink)
.sink(document);
}

private MemoryLogHandler asciidoctorLoggingSetup(Asciidoctor asciidoctor, LogHandler logHandler, File siteDirectory) {
Expand Down Expand Up @@ -148,15 +148,15 @@ protected File resolveSiteDirectory(MavenProject project, Xpp3Dom siteConfig) {

protected OptionsBuilder defaultOptions(File siteDirectory) {
return Options.builder()
.backend("xhtml")
.safe(SafeMode.UNSAFE)
.baseDir(new File(siteDirectory, ROLE_HINT));
.backend("xhtml")
.safe(SafeMode.UNSAFE)
.baseDir(new File(siteDirectory, ROLE_HINT));
}

protected AttributesBuilder defaultAttributes() {
return Attributes.builder()
.attribute("idprefix", "@")
.attribute("showtitle", "@");
.attribute("idprefix", "@")
.attribute("showtitle", "@");
}

private void requireLibrary(Asciidoctor asciidoctor, String require) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.asciidoctor.maven.site.parser;

import java.util.Arrays;
import java.util.List;

import org.apache.maven.doxia.sink.Sink;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.maven.site.parser.processors.DescriptionListNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.DocumentNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.ImageNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.ListItemNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.ListingNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.LiteralNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.NoOpNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.OrderedListNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.ParagraphNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.PreambleNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.SectionNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.TableNodeProcessor;
import org.asciidoctor.maven.site.parser.processors.UnorderedListNodeProcessor;

/**
* Factory and repository for NodeProcessors.
*
* @author abelsromero
* @since 3.1.0
*/
public class NodeSinker {

private final List<NodeProcessor> nodeProcessors;

private final NodeProcessor noOpProcessor;

public NodeSinker(Sink sink) {
nodeProcessors = Arrays.asList(
new DescriptionListNodeProcessor(sink, this),
new DocumentNodeProcessor(sink, this),
new ImageNodeProcessor(sink, this),
new ListItemNodeProcessor(sink, this),
new ListingNodeProcessor(sink, this),
new ListingNodeProcessor(sink, this),
new LiteralNodeProcessor(sink, this),
new OrderedListNodeProcessor(sink, this),
new ParagraphNodeProcessor(sink, this),
new PreambleNodeProcessor(sink, this),
new SectionNodeProcessor(sink, this),
new TableNodeProcessor(sink, this),
new UnorderedListNodeProcessor(sink, this)
);
noOpProcessor = new NoOpNodeProcessor(sink, this);
}

/**
* Returns first NodeProcessor that can treat the node.
**/
private NodeProcessor get(StructuralNode node) {
return nodeProcessors.stream()
.filter(nodeProcessor -> nodeProcessor.applies(node))
.findFirst()
.orElse(noOpProcessor);
}

public void sink(StructuralNode node) {
get(node).process(node);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.apache.maven.doxia.sink.Sink;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.maven.site.parser.NodeSinker;

/**
* Recommended base case to build a {@link org.asciidoctor.maven.site.parser.NodeProcessor}.
Expand All @@ -12,14 +14,17 @@
public class AbstractSinkNodeProcessor {

private final Sink sink;
private final NodeSinker nodeSinker;

/**
* Constructor.
*
* @param sink Doxia {@link Sink}
* @param sink Doxia {@link Sink}
* @param nodeSinker
*/
public AbstractSinkNodeProcessor(Sink sink) {
public AbstractSinkNodeProcessor(Sink sink, NodeSinker nodeSinker) {
this.sink = sink;
this.nodeSinker = nodeSinker;
}

/**
Expand All @@ -31,6 +36,16 @@ protected Sink getSink() {
return sink;
}

/**
* Delegates the processing of the new node to the appropriate processor.
* Similar to {@link org.asciidoctor.maven.site.parser.NodeProcessor#process(StructuralNode)}
* but this selects the processor from the ones available.
*
* @param node Node to process
*/
protected void sink(StructuralNode node) {
nodeSinker.sink(node);
}

/**
* Tests for the presence of an attribute in current and parent nodes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.asciidoctor.ast.ListItem;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.maven.site.parser.NodeProcessor;
import org.asciidoctor.maven.site.parser.NodeSinker;

/**
* Description list processor.
Expand All @@ -17,24 +18,14 @@
*/
public class DescriptionListNodeProcessor extends AbstractSinkNodeProcessor implements NodeProcessor {

private ListItemNodeProcessor itemNodeProcessor;

/**
* Constructor.
*
* @param sink Doxia {@link Sink}
*/
public DescriptionListNodeProcessor(Sink sink) {
super(sink);
}

/**
* Inject a {@link ListItemNodeProcessor}.
*
* @param nodeProcessor {@link ListItemNodeProcessor}
* @param sink Doxia {@link Sink}
* @param nodeSinker
*/
public void setItemNodeProcessor(ListItemNodeProcessor nodeProcessor) {
this.itemNodeProcessor = nodeProcessor;
public DescriptionListNodeProcessor(Sink sink, NodeSinker nodeSinker) {
super(sink, nodeSinker);
}

@Override
Expand Down Expand Up @@ -67,7 +58,7 @@ public void process(StructuralNode node) {
if (description.getBlocks().isEmpty()) {
sink.rawText(description.getText());
} else {
itemNodeProcessor.process(description);
super.sink(description);
}
sink.definition_();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import org.apache.maven.doxia.sink.Sink;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.maven.site.parser.NodeProcessor;
import org.asciidoctor.maven.site.parser.NodeSinker;

import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;

/**
* Root document processor.
Expand All @@ -17,8 +20,8 @@ public class DocumentNodeProcessor extends AbstractSinkNodeProcessor implements
*
* @param sink Doxia {@link Sink}
*/
public DocumentNodeProcessor(Sink sink) {
super(sink);
public DocumentNodeProcessor(Sink sink, NodeSinker nodeSinker) {
super(sink, nodeSinker);
}

@Override
Expand All @@ -28,7 +31,18 @@ public boolean applies(StructuralNode node) {

@Override
public void process(StructuralNode node) {
// NOTE: H1 generation fixed in doxia 2.0.0-MX
getSink().rawText(String.format("<h1>%s</h1>", node.getTitle()));
final Sink sink = getSink();

sink.body();
String title = node.getTitle();
if (isNotBlank(title)) {
sink.sectionTitle1();
sink.rawText(title);
sink.sectionTitle1_();
}
node.getBlocks()
.forEach(this::sink);

sink.body_();
}
}
Loading

0 comments on commit 912de34

Please sign in to comment.