diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java index 6f5d8bf6c23a8..f8f6ee9283aba 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java @@ -52,6 +52,8 @@ final class JavaDocParser { private static final String ORDERED_LIST_NODE = "ol"; private static final String SUPER_SCRIPT_NODE = "sup"; private static final String UN_ORDERED_LIST_NODE = "ul"; + private static final String PREFORMATED_NODE = "pre"; + private static final String BLOCKQUOTE_NODE = "blockquote"; private static final String BIG_ASCIDOC_STYLE = "[.big]"; private static final String LINK_ATTRIBUTE_FORMAT = "[%s]"; @@ -62,6 +64,10 @@ final class JavaDocParser { private static final String UNORDERED_LIST_ITEM_ASCIDOC_STYLE = " - "; private static final String UNDERLINE_ASCIDOC_STYLE = "[.underline]"; private static final String LINE_THROUGH_ASCIDOC_STYLE = "[.line-through]"; + private static final String HARD_LINE_BREAK_ASCIDOC_STYLE = " +\n"; + private static final String CODE_BLOCK_ASCIDOC_STYLE = "```"; + private static final String BLOCKQUOTE_BLOCK_ASCIDOC_STYLE = "[quote]\n____"; + private static final String BLOCKQUOTE_BLOCK_ASCIDOC_STYLE_END = "____"; private final boolean inlineMacroMode; @@ -185,25 +191,51 @@ private String htmlJavadocToAsciidoc(JavadocDescription javadocDescription) { } } - return sb.toString().trim(); + return trim(sb); } private void appendHtml(StringBuilder sb, Node node) { for (Node childNode : node.childNodes()) { switch (childNode.nodeName()) { case PARAGRAPH_NODE: - sb.append(NEW_LINE); + newLine(sb); + newLine(sb); appendHtml(sb, childNode); break; + case PREFORMATED_NODE: + newLine(sb); + newLine(sb); + sb.append(CODE_BLOCK_ASCIDOC_STYLE); + newLine(sb); + for (Node grandChildNode : childNode.childNodes()) { + unescapeHtmlEntities(sb, grandChildNode.toString()); + } + newLineIfNeeded(sb); + sb.append(CODE_BLOCK_ASCIDOC_STYLE); + newLine(sb); + newLine(sb); + break; + case BLOCKQUOTE_NODE: + newLine(sb); + newLine(sb); + sb.append(BLOCKQUOTE_BLOCK_ASCIDOC_STYLE); + newLine(sb); + appendHtml(sb, childNode); + newLineIfNeeded(sb); + sb.append(BLOCKQUOTE_BLOCK_ASCIDOC_STYLE_END); + newLine(sb); + newLine(sb); + break; case ORDERED_LIST_NODE: case UN_ORDERED_LIST_NODE: + newLine(sb); appendHtml(sb, childNode); break; case LIST_ITEM_NODE: final String marker = childNode.parent().nodeName().equals(ORDERED_LIST_NODE) ? ORDERED_LIST_ITEM_ASCIDOC_STYLE : UNORDERED_LIST_ITEM_ASCIDOC_STYLE; - sb.append(NEW_LINE); + newLine(sb); sb.append(marker); appendHtml(sb, childNode); break; @@ -213,7 +245,7 @@ private void appendHtml(StringBuilder sb, Node node) { sb.append(link); final StringBuilder caption = new StringBuilder(); appendHtml(caption, childNode); - sb.append(String.format(LINK_ATTRIBUTE_FORMAT, caption.toString().trim())); + sb.append(String.format(LINK_ATTRIBUTE_FORMAT, trim(caption))); break; case CODE_NODE: sb.append(BACKTICK); @@ -269,7 +301,7 @@ private void appendHtml(StringBuilder sb, Node node) { sb.append(HASH); break; case NEW_LINE_NODE: - sb.append(NEW_LINE); + sb.append(HARD_LINE_BREAK_ASCIDOC_STYLE); break; case TEXT_NODE: String text = ((TextNode) childNode).text(); @@ -295,6 +327,142 @@ private void appendHtml(StringBuilder sb, Node node) { } } + /** + * Trim the content of the given {@link StringBuilder} holding also AsciiDoc had line break {@code " +\n"} + * for whitespace in addition to characters <= {@code ' '}. + * + * @param sb the {@link StringBuilder} to trim + * @return the trimmed content of the given {@link StringBuilder} + */ + static String trim(StringBuilder sb) { + int length = sb.length(); + int offset = 0; + while (offset < length) { + final char ch = sb.charAt(offset); + if (ch == ' ' + && offset + 2 < length + && sb.charAt(offset + 1) == '+' + && sb.charAt(offset + 2) == '\n') { + /* Space followed by + and newline is AsciiDoc hard break that we consider whitespace */ + offset += 3; + continue; + } else if (ch > ' ') { + /* Non-whitespace as defined by String.trim() */ + break; + } + offset++; + } + if (offset > 0) { + sb.delete(0, offset); + } + if (sb.length() > 0) { + offset = sb.length() - 1; + while (offset >= 0) { + final char ch = sb.charAt(offset); + if (ch == '\n' + && offset - 2 >= 0 + && sb.charAt(offset - 1) == '+' + && sb.charAt(offset - 2) == ' ') { + /* Space followed by + is AsciiDoc hard break that we consider whitespace */ + offset -= 3; + continue; + } else if (ch > ' ') { + /* Non-whitespace as defined by String.trim() */ + break; + } + offset--; + } + if (offset < sb.length() - 1) { + sb.setLength(offset + 1); + } + } + return sb.toString(); + } + + private static StringBuilder newLineIfNeeded(StringBuilder sb) { + trimText(sb, " \t\r\n"); + return sb.append(NEW_LINE); + } + + private static StringBuilder newLine(StringBuilder sb) { + /* Trim trailing spaces and tabs at the end of line */ + trimText(sb, " \t"); + return sb.append(NEW_LINE); + } + + private static StringBuilder trimText(StringBuilder sb, String charsToTrim) { + while (sb.length() > 0 && charsToTrim.indexOf(sb.charAt(sb.length() - 1)) >= 0) { + sb.setLength(sb.length() - 1); + } + return sb; + } + + private StringBuilder unescapeHtmlEntities(StringBuilder sb, String text) { + int i = 0; + /* trim leading whitespace */ + LOOP: while (i < text.length()) { + switch (text.charAt(i++)) { + case ' ': + case '\t': + case '\r': + case '\n': + break; + default: + i--; + break LOOP; + } + } + for (; i < text.length(); i++) { + final char ch = text.charAt(i); + switch (ch) { + case '&': + int start = ++i; + while (i < text.length() && text.charAt(i) != ';') { + i++; + } + if (i > start) { + final String abbrev = text.substring(start, i); + switch (abbrev) { + case "lt": + sb.append('<'); + break; + case "gt": + sb.append('>'); + break; + case "nbsp": + sb.append("{nbsp}"); + break; + case "amp": + sb.append('&'); + break; + default: + try { + int code = Integer.parseInt(abbrev); + sb.append((char) code); + } catch (NumberFormatException e) { + throw new RuntimeException( + "Could not parse HTML entity &" + abbrev + "; in\n\n" + text + "\n\n"); + } + break; + } + } + break; + case '\r': + if (i + 1 < text.length() && text.charAt(i + 1) == '\n') { + /* Ignore \r followed by \n */ + } else { + /* A Mac single \r: replace by \n */ + sb.append('\n'); + } + break; + default: + sb.append(ch); + + } + } + return sb; + } + private StringBuilder appendEscapedAsciiDoc(StringBuilder sb, String text) { boolean escaping = false; for (int i = 0; i < text.length(); i++) { diff --git a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java index 5410b7f7bdc91..f23705f230e63 100644 --- a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java +++ b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java @@ -28,7 +28,7 @@ public void parseNullJavaDoc() { @Test public void removeParagraphIndentation() { String parsed = parser.parseConfigDescription("First paragraph

Second Paragraph"); - assertEquals("First paragraph\n\nSecond Paragraph", parsed); + assertEquals("First paragraph +\n +\nSecond Paragraph", parsed); } @Test @@ -50,13 +50,13 @@ public void parseSimpleJavaDoc() { @Test public void parseJavaDocWithParagraph() { String javaDoc = "hello

world

"; - String expectedOutput = "hello\nworld"; + String expectedOutput = "hello\n\nworld"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); javaDoc = "hello world

bonjour

le monde

"; - expectedOutput = "hello world\nbonjour \nle monde"; + expectedOutput = "hello world\n\nbonjour\n\nle monde"; parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -118,21 +118,6 @@ public void parseJavaDocWithStyles() { assertEquals(expectedOutput, parsed); } - @Test - public void parseJavaDocWithUlTags() { - String javaDoc = "hello "; - String expectedOutput = "hello world"; - String parsed = parser.parseConfigDescription(javaDoc); - - assertEquals(expectedOutput, parsed); - - javaDoc = "hello world"; - expectedOutput = "hello world bonjour le monde"; - parsed = parser.parseConfigDescription(javaDoc); - - assertEquals(expectedOutput, parsed); - } - @Test public void parseJavaDocWithLiTagsInsideUlTag() { String javaDoc = "List:" + @@ -141,7 +126,7 @@ public void parseJavaDocWithLiTagsInsideUlTag() { "
  • 2
  • \n" + "" + ""; - String expectedOutput = "List: \n - 1 \n - 2"; + String expectedOutput = "List:\n\n - 1\n - 2"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -155,7 +140,7 @@ public void parseJavaDocWithLiTagsInsideOlTag() { "
  • 2
  • \n" + "" + ""; - String expectedOutput = "List: \n . 1 \n . 2"; + String expectedOutput = "List:\n\n . 1\n . 2"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -224,6 +209,49 @@ public void parseJavaDocWithUnknownNode() { assertEquals(expectedOutput, parsed); } + @Test + public void parseJavaDocWithBlockquoteBlock() { + assertEquals("See Section 4.5.5 of the JSR 380 specification, specifically\n" + + "\n" + + "[quote]\n" + + "____\n" + + "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation. This would pose a strengthening of preconditions to be fulfilled by the caller.\n" + + "____\n" + + "\n" + + "That was interesting, wasn't it?", + parser.parseConfigDescription("See Section 4.5.5 of the JSR 380 specification, specifically\n" + + "\n" + + "
    \n" + + "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may\n" + + "be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.\n" + + "This would pose a strengthening of preconditions to be fulfilled by the caller.\n" + + "
    \nThat was interesting, wasn't it?")); + + assertEquals( + "Some HTML entities & special characters:\n\n```\n|[/variant]|/[/variant]\n```\n\nbaz", + parser.parseConfigDescription( + "Some HTML entities & special characters:\n\n
    <os>|<arch>[/variant]|<os>/<arch>[/variant]\n
    \n\nbaz")); + + // TODO + // assertEquals("Example:\n\n```\nfoo\nbar\n```", + // parser.parseConfigDescription("Example:\n\n
    {@code\nfoo\nbar\n}
    ")); + } + + @Test + public void parseJavaDocWithCodeBlock() { + assertEquals("Example:\n\n```\nfoo\nbar\n```\n\nbaz", + parser.parseConfigDescription("Example:\n\n
    \nfoo\nbar\n
    \n\nbaz")); + + assertEquals( + "Some HTML entities & special characters:\n\n```\n|[/variant]|/[/variant]\n```\n\nbaz", + parser.parseConfigDescription( + "Some HTML entities & special characters:\n\n
    <os>|<arch>[/variant]|<os>/<arch>[/variant]\n
    \n\nbaz")); + + // TODO + // assertEquals("Example:\n\n```\nfoo\nbar\n```", + // parser.parseConfigDescription("Example:\n\n
    {@code\nfoo\nbar\n}
    ")); + } + @Test public void asciidoc() { String asciidoc = "== My Asciidoc\n" + @@ -308,4 +336,25 @@ public void escapeBrackets(String ch) { assertEquals(expected, actual); } + @Test + void trim() { + assertEquals("+ \nfoo", JavaDocParser.trim(new StringBuilder("+ \nfoo"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" +\nfoo"))); + assertEquals("foo +", JavaDocParser.trim(new StringBuilder("foo +"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder("foo"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder("+ \n"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +\n+ \n"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" \n\tfoo"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder("foo \n\t"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" \n\tfoo \n\t"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(""))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" \n\t"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n +\n"))); + assertEquals("foo +\nbar", JavaDocParser.trim(new StringBuilder(" foo +\nbar +\n"))); + } + } diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java index 18f48f9aa1968..106207774b677 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java @@ -147,13 +147,13 @@ public class JibConfig { * List of target platforms. Each platform is defined using the pattern: * *
    -     *  {@literal |[/variant]|/[/variant]}
    +     * <os>|<arch>[/variant]|<os>/<arch>[/variant]
          * 
    * * for example: * *
    -     * {@literal linux/amd64,linux/arm64/v8}
    +     * linux/amd64,linux/arm64/v8
          * 
    * * If not specified, OS default is linux and architecture default is {@code amd64}. diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java index 950c2fda895a9..002b38e409414 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java @@ -44,11 +44,11 @@ public interface HibernateValidatorMethodBuildTimeConfig { *

    * See Section 4.5.5 of the JSR 380 specification, specifically * - *

    -         * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
    +         * 
    + * In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation. - * This would pose a strengthening of preconditions to be fulfilled by the caller." - *
    + * This would pose a strengthening of preconditions to be fulfilled by the caller. + * */ @WithDefault("false") boolean allowOverridingParameterConstraints(); @@ -59,12 +59,12 @@ public interface HibernateValidatorMethodBuildTimeConfig { *

    * See Section 4.5.5 of the JSR 380 specification, specifically * - *

    -         * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
    +         * 
    + * If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class), * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation. - * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller." - *
    + * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller. + * */ @WithDefault("false") boolean allowParameterConstraintsOnParallelMethods(); @@ -75,12 +75,12 @@ public interface HibernateValidatorMethodBuildTimeConfig { *

    * See Section 4.5.5 of the JSR 380 specification, specifically * - *

    -         * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy.
    +         * 
    + * One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy. * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations) * cannot mark the return value for cascaded validation if the return value has already been marked on the - * overridden method of the super type or interface." - *
    + * overridden method of the super type or interface. + * */ @WithDefault("false") boolean allowMultipleCascadedValidationOnReturnValues(); diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityBuildTimeConfig.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityBuildTimeConfig.java index dccb67a7bcf13..fe0593c55c396 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityBuildTimeConfig.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityBuildTimeConfig.java @@ -15,9 +15,9 @@ public class SecurityBuildTimeConfig { * E.g. if enabled, in the following bean, methodB will be denied. * *
    -     *   {@literal @}ApplicationScoped
    +     *   @ApplicationScoped
          *   public class A {
    -     *      {@literal @}RolesAllowed("admin")
    +     *      @RolesAllowed("admin")
          *      public void methodA() {
          *          ...
          *      }