From ab299a044b188842ba2109dabb5eb61fa625b48d Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 23 Oct 2026 15:48:56 +0000 Subject: [PATCH 1/2] Fix xdoc generator to HTML-escape content of {@code} and {@literal} tags The chhorz javadoc-parser wraps {@code X} content verbatim in X without HTML-escaping X. When X contains angle brackets (e.g. {@code }), Jsoup subsequently parses as an unknown HTML tag and silently drops it from the XML output, causing the tag name to disappear from the generated site. This is visible in Maven's settings reference where descriptions such as {@code } and {@code } rendered with the tag name missing on the published site. Fix: pre-process {@code} and {@literal} inline tags in XdocGenerator.rewrite() by HTML-escaping their content before the javadoc-parser and Jsoup pipeline see it. The escapeHtml() helper normalises pre-existing </> entities first (which appear in CDATA-wrapped .mdo descriptions) so both forms are handled correctly and produce identical output. Add a test case to html4.mdo / html4.expected.xml that exercises the fix with {@code <name>value</name>} (decoded to {@code value} by MXParser) and verifies <name>value</name> in output. --- .../modello/plugin/xdoc/XdocGenerator.java | 45 ++++++++++++++++++- .../src/test/resources/html4.expected.xml | 13 +++++- .../src/test/resources/html4.mdo | 8 ++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java index 9fceef8d6..289b6c9a6 100644 --- a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java +++ b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java @@ -29,6 +29,8 @@ import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import com.github.chhorz.javadoc.JavaDoc; @@ -685,6 +687,43 @@ private static void writeMarkupElement(XMLWriter w, String name, String markup) w.endElement(); } + /** + * Pattern matching {@code {@code ...}} and {@code {@literal ...}} inline tags, + * including nested braces. + */ + private static final Pattern INLINE_CODE_TAG_PATTERN = + Pattern.compile("\\{@(?:code|literal)\\s+((?:[^{}]|\\{[^{}]*\\})*)\\}"); + + /** + * HTML-escapes {@code <}, {@code >} and {@code &} in {@code s}. + * Handles both raw characters and pre-existing HTML entities (e.g. {@code <} from + * CDATA-wrapped .mdo descriptions) so that the result is always a clean, once-escaped + * HTML string. + */ + private static String escapeHtml(String s) { + // Normalise any pre-escaped entities first so we never double-escape + String unescaped = s.replace("<", "<").replace(">", ">").replace("&", "&"); + return unescaped.replace("&", "&").replace("<", "<").replace(">", ">"); + } + + /** + * Pre-processes Javadoc {@code {@code}} and {@code {@literal}} inline tags so that + * their content is HTML-escaped before being fed into the HTML/Jsoup pipeline. + * + *

Without this step, content such as {@code {@code }} would be passed + * verbatim to the Javadoc parser which wraps it in a {@code } HTML element + * without escaping; Jsoup would then silently drop the unknown {@code } tag.

+ */ + private static String preprocessInlineCodeTags(String text) { + StringBuffer sb = new StringBuffer(); + Matcher m = INLINE_CODE_TAG_PATTERN.matcher(text); + while (m.find()) { + m.appendReplacement(sb, Matcher.quoteReplacement("" + escapeHtml(m.group(1)) + "")); + } + m.appendTail(sb); + return sb.toString(); + } + /** * Ensures that text will have balanced tags * @@ -692,10 +731,14 @@ private static void writeMarkupElement(XMLWriter w, String name, String markup) * @return valid XML string */ private static String rewrite(String text) { + // Pre-process {@code} / {@literal} tags to HTML-escape their content before the + // Javadoc parser and Jsoup pipeline see it; otherwise angle brackets inside those + // tags are treated as HTML elements and silently dropped by Jsoup. + String preprocessed = preprocessInlineCodeTags(text); JavaDoc javaDoc = JavaDocParserBuilder.withStandardJavadocTags() .withOutputType(OutputType.HTML) .build() - .parse(text); + .parse(preprocessed); String html = javaDoc.getDescription() + javaDoc.getTags().stream() .map(XdocGenerator::renderJavaDocTag) diff --git a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml index a629a5bf3..15410f53c 100644 --- a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml +++ b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml @@ -13,6 +13,7 @@ <model> <extend/> + <properties/> </model> @@ -41,8 +42,18 @@ This is a description in HTML4 1.0.0 No description. + + + properties + + + String + + 1.0.0 + The format is <name>value</name>. + - + \ No newline at end of file diff --git a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.mdo b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.mdo index cb85dc092..b9f1d8d57 100644 --- a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.mdo +++ b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.mdo @@ -31,6 +31,14 @@ 1.0.0+ String + + properties + 1.0.0+ + String + + The format is {@code <name>value</name>}. + + From 07f03ca749edd0a65e29ec3b0c45bb4ffcdf2041 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 5 Nov 2026 16:15:54 +0000 Subject: [PATCH 2/2] Address review: fix literal tag rendering, qualify brace nesting comment, add trailing newline - Use ... for {@literal} and ... for {@code}, matching chhorz parser semantics (was always for both) - Change non-capturing group to capturing group in INLINE_CODE_TAG_PATTERN so the tag name is available as group(1) and content shifts to group(2) - Qualify Javadoc comment: 'including one level of nested braces' (the regex only handles one level, not arbitrary nesting) - Add missing trailing newline to html4.expected.xml --- .../org/codehaus/modello/plugin/xdoc/XdocGenerator.java | 9 ++++++--- .../src/test/resources/html4.expected.xml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java index 289b6c9a6..f9e1f1c10 100644 --- a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java +++ b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java @@ -689,10 +689,10 @@ private static void writeMarkupElement(XMLWriter w, String name, String markup) /** * Pattern matching {@code {@code ...}} and {@code {@literal ...}} inline tags, - * including nested braces. + * including one level of nested braces. */ private static final Pattern INLINE_CODE_TAG_PATTERN = - Pattern.compile("\\{@(?:code|literal)\\s+((?:[^{}]|\\{[^{}]*\\})*)\\}"); + Pattern.compile("\\{@(code|literal)\\s+((?:[^{}]|\\{[^{}]*\\})*)\\}"); /** * HTML-escapes {@code <}, {@code >} and {@code &} in {@code s}. @@ -718,7 +718,10 @@ private static String preprocessInlineCodeTags(String text) { StringBuffer sb = new StringBuffer(); Matcher m = INLINE_CODE_TAG_PATTERN.matcher(text); while (m.find()) { - m.appendReplacement(sb, Matcher.quoteReplacement("" + escapeHtml(m.group(1)) + "")); + String tag = m.group(1); + String open = "literal".equals(tag) ? "" : ""; + String close = "literal".equals(tag) ? "" : ""; + m.appendReplacement(sb, Matcher.quoteReplacement(open + escapeHtml(m.group(2)) + close)); } m.appendTail(sb); return sb.toString(); diff --git a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml index 15410f53c..0b304798b 100644 --- a/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml +++ b/modello-plugins/modello-plugin-xdoc/src/test/resources/html4.expected.xml @@ -56,4 +56,4 @@ This is a description in HTML4 - \ No newline at end of file +