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>}. + +