Skip to content

Commit

Permalink
(main) Port fixes for maven-site breadcrumbs from v2.2.x #763 (#774)
Browse files Browse the repository at this point in the history
* Add new HeaderParser and HeaderMetadata components to obtain Asciidoctor header
details and inject them into the Sink header
* Renames package `org.asciidoctor.maven.site.ast` to `org.asciidoctor.maven.site.parser`
to match module name
* Updated Integration Tests
* Quick docs update (may require review after v3.0.0 release)
  • Loading branch information
abelsromero committed Feb 11, 2024
1 parent 5432f0c commit 1fc2014
Show file tree
Hide file tree
Showing 56 changed files with 853 additions and 166 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Bug Fixes::
* Fix default value for eruby which caused a fail when using erb templates (#610)
* Fix maven properties not being injected as attributes during site conversion (#656)
* Remove Java 'requires open access' module warning in modern Java versions with JRuby v9.4.5.0 (#553)
* Fix breadcrumbs not showing the document title in maven-site pages (#763)

Improvements::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.2</version>
<version>3.4.5</version>
</plugin>
<!-- tag::plugin-decl[] -->
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
= File with TOC
The Author
:docdatetime: 2024-02-07 23:36:29
:toc:

[.lead]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<project name="Maven Site Plugin IT">
<body>
<breadcrumbs>
<item name="Doxia" href="https://maven.apache.org/doxia/index.html"/>
<item name="Trunk" href="https://maven.apache.org/doxia/doxia/index.html"/>
</breadcrumbs>
<menu ref="reports" inherit="top"/>
<menu ref="modules"/>
<menu name="AsciiDoc Pages">
Expand All @@ -10,6 +14,6 @@
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-fluido-skin</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</skin>
</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import java.nio.file.Files
import java.util.regex.Matcher
import java.util.regex.Pattern

File outputDir = new File(basedir, "target/site")
Expand All @@ -12,44 +11,42 @@ final Pattern elementIdPattern = Pattern.compile(".* id=\"(.*?)\".*")

def asserter = new FileAsserter()

def tocEntryCapturer = new PatternCapturer("<li><a href=\"#(.*?)\">.*")
def elementIdCapturer = new PatternCapturer(".* id=\"(.*?)\".*")

for (String expectedFile : expectedFiles) {
final File file = new File(outputDir, expectedFile)
println "Checking for presence of $file"

println "Checking for presence of $file"
asserter.isNotEmpty(file)

if (expectedFile == "file-with-toc.html") {
List lines = Files.readAllLines(file.toPath())
println "Ensuring IDs match TOC links"

List tocEntries = new ArrayList()
// == Assert TOC ==
for (String line : lines) {
Matcher matcher = tocEntry.matcher(line)
if (matcher.matches()) {
tocEntries.add(matcher.group(1))
}
tocEntryCapturer.tryMatches(line)

matcher = elementIdPattern.matcher(line)
if (matcher.matches()) {
String elementId = matcher.group(1)
if (tocEntries.contains(elementId)) {
tocEntries.remove(tocEntries.indexOf(elementId))
}
String match = elementIdCapturer.tryMatches(line)
if (match != null) {
tocEntryCapturer.remove(match)
}
}

if (tocEntries.size() != 0) {
throw new Exception("Couldn't find matching IDs for the following TOC entries: $tocEntries")
if (tocEntryCapturer.size() != 0) {
throw new Exception("Couldn't find matching IDs for the following TOC entries: ${tocEntryCapturer.getHits()}")
}

boolean includeResolved = false
boolean sourceHighlighted = false

// == Assert includes ==
for (String line : lines) {
if (!includeResolved && line.contains("Content included from the file ")) {
includeResolved = true
} else if (!sourceHighlighted && line.contains("<span style=\"color:#070;font-weight:bold\">&lt;plugin&gt;</span>")) {
sourceHighlighted = true
sourceHighlighted = true;
}
}

Expand All @@ -72,6 +69,46 @@ for (String expectedFile : expectedFiles) {
if (!foundReplacement) {
throw new Exception("Maven properties not replaced.")
}

// == Assert header metadata ==
def metaPattern = Pattern.compile("<meta name=\"(author|date)\" content=\"(.*)\" />")
boolean headFound = false
Map<String, String> metaTags = new HashMap<>()

for (String line : lines) {
if (!headFound) {
headFound = line.endsWith("<head>")
continue
}
if (line.endsWith("</head>")) break
def matcher = metaPattern.matcher(line.trim())
if (matcher.matches()) {
metaTags.put(matcher.group(1), matcher.group(2))
}
}

if (metaTags['author'] != 'The Author')
throw new RuntimeException("Author not found in $metaTags")
if (metaTags['date'] != '2024-02-07 23:36:29')
throw new RuntimeException("docdatetime not found in: $metaTags")

// assert breadcrumbs
boolean breadcrumbTagFound = false
boolean breadcrumbFound = false
final String docTitle = "File with TOC"

for (String line : lines) {
if (!breadcrumbTagFound) {
breadcrumbTagFound = line.endsWith("<div id=\"breadcrumbs\">")
continue
}
println "Testing: ${line.trim()}"
breadcrumbFound = line.trim().endsWith("${docTitle}</li>")
if (breadcrumbFound) break
}

if (!breadcrumbFound)
throw new RuntimeException("No breadcrumb found: expected title: $docTitle")
}
}

Expand All @@ -95,4 +132,39 @@ class FileAsserter {
}
}

class PatternCapturer {

private final Pattern pattern
private final List<String> hits

PatternCapturer(String pattern) {
this.pattern = Pattern.compile(pattern)
this.hits = new ArrayList()
}


String tryMatches(String text) {
def matcher = pattern.matcher(text)
if (matcher.matches()) {
def group = matcher.group(1)
hits.add(group)
return group
}
return null
}

void remove(String text) {
hits.remove(text)
}

int size() {
return hits.size()
}

List<String> getHits() {
return hits
}
}


return true
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package org.asciidoctor.maven.site;

import javax.inject.Inject;
import javax.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.logging.Logger;

import org.apache.maven.doxia.parser.AbstractTextParser;
import org.apache.maven.doxia.parser.ParseException;
import org.apache.maven.doxia.parser.Parser;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.project.MavenProject;
import org.asciidoctor.*;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.AttributesBuilder;
import org.asciidoctor.OptionsBuilder;
import org.asciidoctor.SafeMode;
import org.asciidoctor.maven.log.LogHandler;
import org.asciidoctor.maven.log.LogRecordFormatter;
import org.asciidoctor.maven.log.LogRecordsProcessors;
import org.asciidoctor.maven.log.MemoryLogHandler;
import org.asciidoctor.maven.site.SiteConverterDecorator.Result;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import javax.inject.Inject;
import javax.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.logging.Logger;

/**
* This class is used by <a href="https://maven.apache.org/doxia/overview.html">the Doxia framework</a>
* to handle the actual parsing of the AsciiDoc input files into HTML to be consumed/wrapped
Expand Down Expand Up @@ -67,31 +71,36 @@ 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);
}

final LogHandler logHandler = getLogHandlerConfig(siteConfig);
final MemoryLogHandler memoryLogHandler = asciidoctorLoggingSetup(asciidoctor, logHandler, siteDirectory);

// QUESTION should we keep OptionsBuilder & AttributesBuilder separate for call to convertAsciiDoc?
String asciidocHtml = convertAsciiDoc(asciidoctor, source, conversionConfig.getOptions());
final SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
final Result headerMetadata = siteConverter.process(source, conversionConfig.getOptions());

try {
// process log messages according to mojo configuration
new LogRecordsProcessors(logHandler, siteDirectory, errorMessage -> getLog().error(errorMessage))
.processLogRecords(memoryLogHandler);
.processLogRecords(memoryLogHandler);
} catch (Exception exception) {
throw new ParseException(exception.getMessage(), exception);
}

sink.rawText(asciidocHtml);
new HeadParser(sink)
.parse(headerMetadata.getHeaderMetadata());

sink.rawText(headerMetadata.getHtml());
}


private MemoryLogHandler asciidoctorLoggingSetup(Asciidoctor asciidoctor, LogHandler logHandler, File siteDirectory) {

final MemoryLogHandler memoryLogHandler = new MemoryLogHandler(logHandler.getOutputToConsole(),
logRecord -> getLog().info(LogRecordFormatter.format(logRecord, siteDirectory)));
logRecord -> getLog().info(LogRecordFormatter.format(logRecord, siteDirectory)));
asciidoctor.registerLogHandler(memoryLogHandler);
// disable default console output of AsciidoctorJ
Logger.getLogger("asciidoctor").setUseParentHandlers(false);
Expand Down Expand Up @@ -119,16 +128,16 @@ 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));
return OptionsBuilder.options()
.backend("xhtml")
.safe(SafeMode.UNSAFE)
.baseDir(new File(siteDirectory, ROLE_HINT));
}

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

private void requireLibrary(Asciidoctor asciidoctor, String require) {
Expand All @@ -140,9 +149,4 @@ private void requireLibrary(Asciidoctor asciidoctor, String require) {
}
}
}

protected String convertAsciiDoc(Asciidoctor asciidoctor, String source, Options options) {
return asciidoctor.convert(source, options);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.asciidoctor.maven.site;

import java.util.Map;

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Options;
import org.asciidoctor.OptionsBuilder;
import org.asciidoctor.ast.Document;

/**
* Asciidoctor conversion wrapper for maven-site integration.
* In addition to conversion, handles header metadata extraction.
*/
class SiteConverterDecorator {

private final Asciidoctor asciidoctor;

SiteConverterDecorator(Asciidoctor asciidoctor) {
this.asciidoctor = asciidoctor;
}

Result process(String content, Options options) {
final Document document = asciidoctor.load(content, headerProcessingMetadata(options));
final HeaderMetadata headerMetadata = HeaderMetadata.from(document);

final String html = asciidoctor.convert(content, options);

return new Result(headerMetadata, html);
}

private static Options headerProcessingMetadata(Options options) {
Map<String, Object> optionsMap = options.map();
OptionsBuilder builder = Options.builder();
for (Map.Entry<String, Object> entry : optionsMap.entrySet()) {
builder.option(entry.getKey(), entry.getValue());
}

builder.parseHeaderOnly(Boolean.TRUE);
return builder.build();
}

/**
* Simple tuple to return Asciidoctor extracted metadata and conversion result.
*/
final class Result {

private final HeaderMetadata headerMetadata;
private final String html;

Result(HeaderMetadata headerMetadata, String html) {
this.headerMetadata = headerMetadata;
this.html = html;
}

HeaderMetadata getHeaderMetadata() {
return headerMetadata;
}

String getHtml() {
return html;
}
}


}

0 comments on commit 1fc2014

Please sign in to comment.