diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 65aa987db0d5c..300b6a542efe7 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -399,6 +399,23 @@ maven-compiler-plugin + + io.quarkus + quarkus-extension-deployment-maven-plugin + ${project.version} + + + generate-config-metadata + package + + attach-config-metadata + + + ${skipDocs} + + + + org.codehaus.mojo properties-maven-plugin diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/ConfigDocExtensionProcessor.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/ConfigDocExtensionProcessor.java index 75c1e996e5ebd..df78c7759f882 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/ConfigDocExtensionProcessor.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/ConfigDocExtensionProcessor.java @@ -84,12 +84,12 @@ public void finalizeProcessing() { // the model is not written in the jar file JavadocElements javadocElements = configResolver.resolveJavadoc(); - if (!javadocElements.elements().isEmpty()) { + if (!javadocElements.isEmpty()) { utils.filer().writeModel(Outputs.QUARKUS_CONFIG_DOC_JAVADOC, javadocElements); } ResolvedModel resolvedModel = configResolver.resolveModel(); - if (!resolvedModel.getConfigRoots().isEmpty()) { + if (!resolvedModel.isEmpty()) { Path resolvedModelPath = utils.filer().writeModel(Outputs.QUARKUS_CONFIG_DOC_MODEL, resolvedModel); if (config.isDebug()) { @@ -101,5 +101,14 @@ public void finalizeProcessing() { } } } + + // Generate files that will be consumed by the Maven plugin present in the deployment module of each extension. + // These files will be included in the jars (for now). + if (!javadocElements.isEmpty()) { + utils.filer().writeYaml(Outputs.META_INF_QUARKUS_CONFIG_JAVADOC_YAML, javadocElements); + } + if (!resolvedModel.isEmpty()) { + utils.filer().writeYaml(Outputs.META_INF_QUARKUS_CONFIG_MODEL_YAML, resolvedModel); + } } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java index cbff969922b77..636bde16d9671 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java @@ -1,16 +1,16 @@ package io.quarkus.annotation.processor.documentation.config.discovery; import io.quarkus.annotation.processor.documentation.config.model.Deprecation; -import io.quarkus.annotation.processor.documentation.config.model.SourceType; +import io.quarkus.annotation.processor.documentation.config.model.SourceElementType; import io.quarkus.annotation.processor.documentation.config.util.TypeUtil; import io.quarkus.annotation.processor.util.Strings; public class DiscoveryConfigProperty { private final String path; - private final String sourceClass; - private final String sourceName; - private final SourceType sourceType; + private final String sourceType; + private final String sourceElementName; + private final SourceElementType sourceElementType; private final String defaultValue; private final String defaultValueForDoc; private final Deprecation deprecation; @@ -22,15 +22,16 @@ public class DiscoveryConfigProperty { private final boolean section; private final boolean sectionGenerated; - public DiscoveryConfigProperty(String path, String sourceClass, String sourceName, SourceType sourceType, + public DiscoveryConfigProperty(String path, String sourceType, String sourceElementName, + SourceElementType sourceElementType, String defaultValue, String defaultValueForDoc, Deprecation deprecation, String mapKey, boolean unnamedMapKey, ResolvedType type, boolean converted, boolean enforceHyphenateEnumValue, boolean section, boolean sectionGenerated) { this.path = path; - this.sourceClass = sourceClass; - this.sourceName = sourceName; this.sourceType = sourceType; + this.sourceElementName = sourceElementName; + this.sourceElementType = sourceElementType; this.defaultValue = defaultValue; this.defaultValueForDoc = defaultValueForDoc; this.deprecation = deprecation; @@ -47,16 +48,16 @@ public String getPath() { return path; } - public String getSourceClass() { - return sourceClass; + public String getSourceType() { + return sourceType; } - public String getSourceName() { - return sourceName; + public String getSourceElementName() { + return sourceElementName; } - public SourceType getSourceType() { - return sourceType; + public SourceElementType getSourceElementType() { + return sourceElementType; } public String getDefaultValue() { @@ -110,8 +111,8 @@ public String toString() { public String toString(String prefix) { StringBuilder sb = new StringBuilder(); sb.append(prefix + "name = " + path + "\n"); - sb.append(prefix + "sourceClass = " + sourceClass + "\n"); - sb.append(prefix + "sourceName = " + sourceName + "\n"); + sb.append(prefix + "sourceType = " + sourceType + "\n"); + sb.append(prefix + "sourceElementName = " + sourceElementName + "\n"); sb.append(prefix + "type = " + type + "\n"); if (defaultValue != null) { sb.append(prefix + "defaultValue = " + defaultValue + "\n"); @@ -135,16 +136,17 @@ public String toString(String prefix) { return sb.toString(); } - public static Builder builder(String sourceClass, String sourceName, SourceType sourceType, ResolvedType type) { - return new Builder(sourceClass, sourceName, sourceType, type); + public static Builder builder(String sourceType, String sourceElementName, SourceElementType sourceElementType, + ResolvedType type) { + return new Builder(sourceType, sourceElementName, sourceElementType, type); } public static class Builder { private String name; - private final String sourceClass; - private final String sourceName; - private final SourceType sourceType; + private final String sourceType; + private final String sourceElementName; + private final SourceElementType sourceElementType; private final ResolvedType type; private String defaultValue; private String defaultValueForDoc; @@ -156,10 +158,10 @@ public static class Builder { private boolean section = false; private boolean sectionGenerated = false; - public Builder(String sourceClass, String sourceName, SourceType sourceType, ResolvedType type) { - this.sourceClass = sourceClass; - this.sourceName = sourceName; + public Builder(String sourceType, String sourceElementName, SourceElementType sourceElementType, ResolvedType type) { this.sourceType = sourceType; + this.sourceElementName = sourceElementName; + this.sourceElementType = sourceElementType; this.type = type; } @@ -217,7 +219,8 @@ public DiscoveryConfigProperty build() { defaultValue = TypeUtil.normalizeDurationValue(defaultValue); } - return new DiscoveryConfigProperty(name, sourceClass, sourceName, sourceType, defaultValue, defaultValueForDoc, + return new DiscoveryConfigProperty(name, sourceType, sourceElementName, sourceElementType, defaultValue, + defaultValueForDoc, deprecation, mapKey, unnamedMapKey, type, converted, enforceHyphenateEnumValue, section, sectionGenerated); } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryRootElement.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryRootElement.java index 8a0b39ccd483c..001a04786cfc1 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryRootElement.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryRootElement.java @@ -38,7 +38,7 @@ public String getQualifiedName() { } public void addProperty(DiscoveryConfigProperty discoveryConfigProperty) { - properties.put(discoveryConfigProperty.getSourceName(), discoveryConfigProperty); + properties.put(discoveryConfigProperty.getSourceElementName(), discoveryConfigProperty); } public Map getProperties() { diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/formatter/JavadocToMarkdownTransformer.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/formatter/JavadocToMarkdownTransformer.java index fd5796ed7b8b0..f543e98e7ba41 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/formatter/JavadocToMarkdownTransformer.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/formatter/JavadocToMarkdownTransformer.java @@ -1,6 +1,11 @@ package io.quarkus.annotation.processor.documentation.config.formatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.javadoc.Javadoc; @@ -9,29 +14,62 @@ import com.github.javaparser.javadoc.description.JavadocInlineTag; import io.quarkus.annotation.processor.documentation.config.model.JavadocFormat; +import io.quarkus.annotation.processor.util.Strings; public class JavadocToMarkdownTransformer { private static final Pattern START_OF_LINE = Pattern.compile("^", Pattern.MULTILINE); + private static final Map ADMONITIONS = Map.of( + "CAUTION", "🔥", + "IMPORTANT", "❗", + "NOTE", "📌", + "TIP", "💡", + "WARNING", "⚠️"); + + private static final Pattern HEADER_PATTERN = Pattern.compile("^(=+) (.+)$"); + private static final Pattern LIST_ITEM_PATTERN = Pattern.compile("^(\\*+|\\.+) (.+)$"); + private static final Pattern IMAGE_BLOCK_PATTERN = Pattern.compile("^image::([^\\s]+)\\[(.*)\\]$"); + private static final Pattern IMAGE_INLINE_PATTERN = Pattern.compile("image:([^\\s]+)\\[(.*)\\]"); + private static final Pattern ADMONITION_BLOCK_START_PATTERN = Pattern + .compile("^\\[(" + String.join("|", ADMONITIONS.keySet()) + ")\\]$"); + private static final String ADMONITION_BLOCK_DELIMITER = "===="; + private static final Pattern ADMONITION_INLINE_PATTERN = Pattern + .compile("^(" + String.join("|", ADMONITIONS.keySet()) + "): (.*)$"); + private static final Pattern BOLD_PATTERN = Pattern.compile("(?<=^|\\s)\\*(.+?)\\*(?=\\s|$)"); + private static final Pattern ITALIC_PATTERN = Pattern.compile("__(.+?)__"); + private static final Pattern BLOCK_TITLE_PATTERN = Pattern.compile("^\\.([a-z0-9].*)$"); + private static final Pattern SOURCE_BLOCK_START_PATTERN = Pattern.compile("^\\[source(?:,[ ]*([a-z]+))?.*\\]$"); + private static final Pattern SOURCE_BLOCK_DELIMITER_PATTERN = Pattern.compile("^(-----*)$"); + private static final Pattern QUOTE_BLOCK_START_PATTERN = Pattern.compile("^\\[quote(?:, (.*?))?(?:, (.*?))?]$"); + private static final Pattern QUOTE_BLOCK_DELIMITER_PATTERN = Pattern.compile("^(_____*)$"); + private static final Pattern LINK_PATTERN = Pattern.compile("(?:link:)([^\\[]+)\\[(.*?)\\]"); + private static final Pattern URL_PATTERN = Pattern.compile("\\b(http[^\\[]+)\\[(.*?)\\]"); + private static final Pattern XREF_PATTERN = Pattern.compile("xref:([^\\[]+)\\[(.*?)\\]"); + private static final Pattern ICON_PATTERN = Pattern.compile("\\bicon:([a-z0-9_-]+)\\[(?:role=([a-z0-9_-]+))?\\](?=\\s|$)"); + private static final Pattern DESCRIPTION_LIST_PATTERN = Pattern.compile("^([a-z0-9][a-z0-9_ -]+)::(?=\\s|$)", + Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + public static String toMarkdown(String javadoc, JavadocFormat format) { if (javadoc == null || javadoc.isBlank()) { return null; } - if (format == JavadocFormat.MARKDOWN) { - return javadoc; - } else if (format == JavadocFormat.JAVADOC) { - // the parser expects all the lines to start with "* " - // we add it as it has been previously removed - Javadoc parsedJavadoc = StaticJavaParser.parseJavadoc(START_OF_LINE.matcher(javadoc).replaceAll("* ")); + switch (format) { + case MARKDOWN: + return javadoc; + case JAVADOC: + // the parser expects all the lines to start with "* " + // we add it as it has been previously removed + Javadoc parsedJavadoc = StaticJavaParser.parseJavadoc(START_OF_LINE.matcher(javadoc).replaceAll("* ")); - // HTML is valid Javadoc but we need to drop the Javadoc tags e.g. {@link ...} - return simplifyJavadoc(parsedJavadoc.getDescription()); + // HTML is valid Javadoc but we need to drop the Javadoc tags e.g. {@link ...} + return simplifyJavadoc(parsedJavadoc.getDescription()); + case ASCIIDOC: + return asciidocToMarkdown(javadoc); + default: + throw new IllegalArgumentException("Converting from " + format + " to Markdown is not supported"); } - - // it's Asciidoc, the fun begins... - return ""; } /** @@ -84,4 +122,285 @@ private static String escapeHtml(String s) { } return out.toString(); } + + /** + * This obviously don't handle the whole complexity of Asciidoc but should handle most cases. + *

+ * One thing that might be worth adding is support for titles for source blocks and admonitions but we can add it later on. + *

+ * It doesn't support tables (yet). + */ + private static String asciidocToMarkdown(String asciidoc) { + List lines = asciidoc.lines().toList(); + List result = new ArrayList<>(); + String currentAdmonition = null; + boolean inAdmonitionPreamble = false; + boolean inAdmonitionBlock = false; + String currentSourceBlockLanguage = null; + boolean inSourcePreamble = false; + boolean inSourceBlock = false; + String currentSourceBlockTitle = null; + String currentSourceBlockDelimiter = null; + boolean inQuoteBlock = false; + boolean quoteStarted = false; + String currentQuoteBlockDelimiter = null; + String quoteAuthor = null; + String quoteSource = null; + + String linePrefix = ""; + + for (String line : lines) { + String markdownLine = line; + + if (inAdmonitionPreamble) { + if (ADMONITION_BLOCK_DELIMITER.equals(line)) { + inAdmonitionBlock = true; + inAdmonitionPreamble = false; + result.add("> [!" + currentAdmonition + "]"); + continue; + } else { + // we haven't found a proper delimiter so we ignore the admonition altogether + inAdmonitionPreamble = false; + } + } + + if (inAdmonitionBlock) { + if (ADMONITION_BLOCK_DELIMITER.equals(line)) { + inAdmonitionBlock = false; + currentAdmonition = null; + linePrefix = ""; + continue; + } else { + linePrefix = "> "; + } + } + + if (inSourcePreamble) { + Matcher blockTitleMatcher = BLOCK_TITLE_PATTERN.matcher(line); + if (blockTitleMatcher.matches()) { + currentSourceBlockTitle = blockTitleMatcher.group(1); + } + } + + if (inSourceBlock) { + if (currentSourceBlockDelimiter.equals(line)) { + // End of source block + result.add(linePrefix + "```"); + inSourcePreamble = false; + inSourceBlock = false; + currentSourceBlockLanguage = null; + currentSourceBlockDelimiter = null; + currentSourceBlockTitle = null; + continue; + } else { + // Inside source block + result.add(linePrefix + markdownLine); + continue; + } + } + + Matcher sourceBlockStartMatcher = SOURCE_BLOCK_START_PATTERN.matcher(line); + if (sourceBlockStartMatcher.matches()) { + if (!Strings.isBlank(sourceBlockStartMatcher.group(1))) { + currentSourceBlockLanguage = sourceBlockStartMatcher.group(1).trim(); + } + inSourcePreamble = true; + // Skip the start marker + continue; + } + + Matcher sourceBlockDelimiterMatcher = SOURCE_BLOCK_DELIMITER_PATTERN.matcher(line); + if (sourceBlockDelimiterMatcher.matches()) { + currentSourceBlockDelimiter = sourceBlockDelimiterMatcher.group(0); + // Start of code block + if (!Strings.isBlank(currentSourceBlockTitle)) { + result.add(linePrefix + "**" + currentSourceBlockTitle + "**"); + result.add(linePrefix + ""); + } + result.add( + linePrefix + "```" + (!Strings.isBlank(currentSourceBlockLanguage) ? currentSourceBlockLanguage : "")); + inSourcePreamble = false; + inSourceBlock = true; + continue; + } + + if (inQuoteBlock) { + Matcher quoteBlockDelimiterMatcher = QUOTE_BLOCK_DELIMITER_PATTERN.matcher(line); + if (!quoteStarted && quoteBlockDelimiterMatcher.matches()) { + currentQuoteBlockDelimiter = quoteBlockDelimiterMatcher.group(0); + continue; + } else if (line.equals(currentQuoteBlockDelimiter)) { + // End of quote block + if (quoteAuthor != null || quoteSource != null) { + result.add(linePrefix + ">"); + result.add(linePrefix + "> — " + (quoteAuthor != null ? quoteAuthor : "") + + (quoteSource != null ? ", " + quoteSource : "")); + } + inQuoteBlock = false; + quoteStarted = false; + currentQuoteBlockDelimiter = null; + continue; + } else { + // Inside quote block + result.add(linePrefix + "> " + line); + quoteStarted = true; + continue; + } + } + + Matcher quoteBlockStartMatcher = QUOTE_BLOCK_START_PATTERN.matcher(line); + if (quoteBlockStartMatcher.matches()) { + // Start of quote block + quoteAuthor = quoteBlockStartMatcher.group(1); + quoteSource = quoteBlockStartMatcher.group(2); + inQuoteBlock = true; + continue; + } + + Matcher admonitionBlockStartMatcher = ADMONITION_BLOCK_START_PATTERN.matcher(line); + if (admonitionBlockStartMatcher.matches()) { + currentAdmonition = admonitionBlockStartMatcher.group(1); + inAdmonitionPreamble = true; + // Skip the start marker + continue; + } + + // Convert headings + Matcher headingMatcher = HEADER_PATTERN.matcher(line); + if (headingMatcher.find()) { + int level = headingMatcher.group(1).length(); + String text = headingMatcher.group(2); + markdownLine = "#".repeat(level) + " " + text; + } + + // Convert list items + Matcher listItemMatcher = LIST_ITEM_PATTERN.matcher(line); + if (listItemMatcher.find()) { + String marker = listItemMatcher.group(1); + String text = listItemMatcher.group(2); + if (marker.startsWith("*")) { + markdownLine = "- " + text; + } else if (marker.startsWith(".")) { + markdownLine = "1. " + text; + } + } + + // Convert italic and bold + markdownLine = convertInline(markdownLine, ITALIC_PATTERN, "*"); + markdownLine = convertInline(markdownLine, BOLD_PATTERN, "**"); + + // Inline Admonitions + if (!inAdmonitionBlock) { + Matcher admonitionInlineMatcher = ADMONITION_INLINE_PATTERN.matcher(line); + if (admonitionInlineMatcher.find()) { + String admonition = admonitionInlineMatcher.group(1); + if (ADMONITIONS.containsKey(admonition)) { + markdownLine = "> " + ADMONITIONS.get(admonition) + " " + admonitionInlineMatcher.group(2); + } else { + markdownLine = "> " + markdownLine; + } + } + } + + // Convert block images + Matcher blockImageMatcher = IMAGE_BLOCK_PATTERN.matcher(line); + if (blockImageMatcher.find()) { + String target = blockImageMatcher.group(1); + String altText = blockImageMatcher.group(2); + markdownLine = "![" + altText + "](" + target + ")"; + } + + // Convert inline images + Matcher inlineImageMatcher = IMAGE_INLINE_PATTERN.matcher(line); + if (inlineImageMatcher.find()) { + String target = inlineImageMatcher.group(1); + String altText = inlineImageMatcher.group(2); + markdownLine = line.replace(inlineImageMatcher.group(), "![" + altText + "](" + target + ")"); + } + + // Convert links + markdownLine = convertLinksAndXrefs(markdownLine, LINK_PATTERN, "link"); + // Convert direct URL links + markdownLine = convertLinksAndXrefs(markdownLine, URL_PATTERN, "url"); + // Convert xrefs + markdownLine = convertLinksAndXrefs(markdownLine, XREF_PATTERN, "xref"); + + // Convert icons + markdownLine = convertIcons(markdownLine); + + // Convert description lists: we only convert the title + markdownLine = convertDescriptionLists(markdownLine); + + result.add(linePrefix + markdownLine); + } + + return result.stream().collect(Collectors.joining("\n")); + } + + private static String convertInline(String line, Pattern pattern, String markdownDelimiter) { + Matcher matcher = pattern.matcher(line); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(sb, markdownDelimiter + matcher.group(1) + markdownDelimiter); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private static String convertLinksAndXrefs(String line, Pattern pattern, String type) { + Matcher matcher = pattern.matcher(line); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + if (type.equals("link") || type.equals("url")) { + matcher.appendReplacement(sb, "[" + matcher.group(2) + "](" + matcher.group(1) + ")"); + } else if (type.equals("xref")) { + String xref = matcher.group(1); + if (xref.contains(".adoc")) { + xref = "https://quarkus.io/guides/" + xref.replace(".adoc", ""); + } else { + xref = "#" + xref; + } + + matcher.appendReplacement(sb, "[" + matcher.group(2) + "](" + xref + ")"); + } + } + matcher.appendTail(sb); + return sb.toString(); + } + + private static String convertIcons(String line) { + Matcher matcher = ICON_PATTERN.matcher(line); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String icon = matcher.group(1); + String emoji; + + switch (icon) { + case "check": + emoji = "✅"; + break; + case "times": + emoji = "❌"; + break; + default: + // TODO we probably need to collect the errors and log them instead + throw new IllegalArgumentException("Icon " + matcher.group(1) + " is not mapped."); + } + + matcher.appendReplacement(sb, emoji); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private static String convertDescriptionLists(String line) { + Matcher matcher = DESCRIPTION_LIST_PATTERN.matcher(line); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String descriptionTitle = matcher.group(1); + matcher.appendReplacement(sb, "**" + descriptionTitle + "**"); + } + matcher.appendTail(sb); + return sb.toString(); + } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java index 4deff0ca7c3be..a101d7a8caf76 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java @@ -43,12 +43,29 @@ public static MergedModel mergeModel(List buildOutputDirectories) { * target/ directories found in the parent directory scanned). */ public static MergedModel mergeModel(JavadocRepository javadocRepository, List buildOutputDirectories) { + return mergeModel(javadocRepository, buildOutputDirectories, false); + } + + /** + * Merge all the resolved models obtained from a list of build output directories (e.g. in the case of Maven, the list of + * target/ directories found in the parent directory scanned). + */ + public static MergedModel mergeModel(List buildOutputDirectories, boolean mergeCommonOrInternalExtensions) { + return mergeModel(null, buildOutputDirectories, mergeCommonOrInternalExtensions); + } + + /** + * Merge all the resolved models obtained from a list of build output directories (e.g. in the case of Maven, the list of + * target/ directories found in the parent directory scanned). + */ + public static MergedModel mergeModel(JavadocRepository javadocRepository, List buildOutputDirectories, + boolean mergeCommonOrInternalExtensions) { // keyed on extension and then top level prefix Map> configRoots = new HashMap<>(); // keyed on file name Map configRootsInSpecificFile = new TreeMap<>(); // keyed on extension - Map> generatedConfigSections = new TreeMap<>(); + Map> generatedConfigSections = new HashMap<>(); for (Path buildOutputDirectory : buildOutputDirectories) { Path resolvedModelPath = buildOutputDirectory.resolve(Outputs.QUARKUS_CONFIG_DOC_MODEL); @@ -85,7 +102,8 @@ public static MergedModel mergeModel(JavadocRepository javadocRepository, List

extensionConfigRoots = configRoots.computeIfAbsent(configRoot.getExtension(), + Map extensionConfigRoots = configRoots.computeIfAbsent( + normalizeExtension(configRoot.getExtension(), mergeCommonOrInternalExtensions), e -> new TreeMap<>()); ConfigRootKey configRootKey = getConfigRootKey(javadocRepository, configRoot); @@ -102,6 +120,7 @@ public static MergedModel mergeModel(JavadocRepository javadocRepository, List

> extensionConfigRootsEntry : configRoots.entrySet()) { @@ -116,6 +135,18 @@ public static MergedModel mergeModel(JavadocRepository javadocRepository, List

> retainBestExtensionKey( Map> configRoots) { return configRoots.entrySet().stream().collect(Collectors.toMap(e -> { diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/AbstractConfigItem.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/AbstractConfigItem.java index 643ed96d28620..a454e64e58647 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/AbstractConfigItem.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/AbstractConfigItem.java @@ -7,35 +7,36 @@ public sealed abstract class AbstractConfigItem implements Comparable permits ConfigProperty, ConfigSection { - protected final String sourceClass; - protected final String sourceName; - protected final SourceType sourceType; + protected final String sourceType; + protected final String sourceElementName; + protected final SourceElementType sourceElementType; protected final Path path; protected final String type; protected Deprecation deprecation; - public AbstractConfigItem(String sourceClass, String sourceName, SourceType sourceType, Path path, String type, + public AbstractConfigItem(String sourceType, String sourceElementName, SourceElementType sourceElementType, Path path, + String type, Deprecation deprecation) { - this.sourceClass = sourceClass; - this.sourceName = sourceName; this.sourceType = sourceType; + this.sourceElementName = sourceElementName; + this.sourceElementType = sourceElementType; this.path = path; this.type = type; this.deprecation = deprecation; } - public String getSourceClass() { - return sourceClass; + public String getSourceType() { + return sourceType; } - public String getSourceName() { - return sourceName; + public String getSourceElementName() { + return sourceElementName; } - public SourceType getSourceType() { - return sourceType; + public SourceElementType getSourceElementType() { + return sourceElementType; } public Path getPath() { diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigProperty.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigProperty.java index 1be3576d4e27d..ab49c16be665a 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigProperty.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigProperty.java @@ -29,14 +29,14 @@ public final class ConfigProperty extends AbstractConfigItem { private final String javadocSiteLink; - public ConfigProperty(ConfigPhase phase, String sourceClass, String sourceName, SourceType sourceType, PropertyPath path, - List additionalPaths, String type, String typeDescription, boolean map, boolean list, - boolean optional, + public ConfigProperty(ConfigPhase phase, String sourceType, String sourceElementName, SourceElementType sourceElementType, + PropertyPath path, List additionalPaths, String type, String typeDescription, boolean map, + boolean list, boolean optional, String mapKey, boolean unnamedMapKey, boolean withinMap, boolean converted, @JsonProperty("enum") boolean isEnum, EnumAcceptedValues enumAcceptedValues, String defaultValue, String javadocSiteLink, Deprecation deprecation) { - super(sourceClass, sourceName, sourceType, path, type, deprecation); + super(sourceType, sourceElementName, sourceElementType, path, type, deprecation); this.phase = phase; this.additionalPaths = additionalPaths != null ? Collections.unmodifiableList(additionalPaths) : List.of(); this.typeDescription = typeDescription; diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java index da33958996e84..4b0774ef6ac1c 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java @@ -11,9 +11,9 @@ public final class ConfigSection extends AbstractConfigItem implements ConfigIte private final List items = new ArrayList<>(); private final int level; - public ConfigSection(String sourceClass, String sourceName, SourceType sourceType, SectionPath path, String type, int level, - boolean generated, Deprecation deprecation) { - super(sourceClass, sourceName, sourceType, path, type, deprecation); + public ConfigSection(String sourceType, String sourceElementName, SourceElementType sourceElementType, SectionPath path, + String type, int level, boolean generated, Deprecation deprecation) { + super(sourceType, sourceElementName, sourceElementType, path, type, deprecation); this.generated = generated; this.level = level; } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/Extension.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/Extension.java index fcb44039f24de..8472659071881 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/Extension.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/Extension.java @@ -5,10 +5,24 @@ import com.fasterxml.jackson.annotation.JsonIgnore; public record Extension(String groupId, String artifactId, String name, - NameSource nameSource, boolean detected) implements Comparable { + NameSource nameSource, boolean commonOrInternal, boolean detected) implements Comparable { + + private static final String ARTIFACT_COMMON_SUFFIX = "-common"; + private static final String ARTIFACT_INTERNAL_SUFFIX = "-internal"; + + public static Extension of(String groupId, String artifactId, String name, + NameSource nameSource) { + boolean commonOrInternal = artifactId.endsWith(ARTIFACT_COMMON_SUFFIX) || artifactId.endsWith(ARTIFACT_INTERNAL_SUFFIX); + if (commonOrInternal) { + nameSource = nameSource == NameSource.EXTENSION_METADATA ? NameSource.EXTENSION_METADATA_COMMON_INTERNAL + : (nameSource == NameSource.POM_XML ? NameSource.POM_XML_COMMON_INTERNAL : nameSource); + } + + return new Extension(groupId, artifactId, name, nameSource, commonOrInternal, true); + } public static Extension createNotDetected() { - return new Extension("not.detected", "not.detected", "Not detected", NameSource.NONE, false); + return new Extension("not.detected", "not.detected", "Not detected", NameSource.NONE, false, false); } @Override @@ -50,6 +64,27 @@ public boolean splitOnConfigRootDescription() { return "io.quarkus".equals(groupId) && "quarkus-core".equals(artifactId); } + @JsonIgnore + public Extension normalizeCommonOrInternal() { + if (!commonOrInternal()) { + return this; + } + + String normalizedArtifactId = artifactId; + if (artifactId.endsWith(ARTIFACT_COMMON_SUFFIX)) { + normalizedArtifactId = artifactId.substring(0, artifactId.length() - ARTIFACT_COMMON_SUFFIX.length()); + } + if (artifactId.endsWith(ARTIFACT_INTERNAL_SUFFIX)) { + normalizedArtifactId = artifactId.substring(0, artifactId.length() - ARTIFACT_INTERNAL_SUFFIX.length()); + } + + if (normalizedArtifactId.equals(artifactId)) { + return this; + } + + return new Extension(groupId, normalizedArtifactId, name, nameSource, commonOrInternal, detected); + } + @Override public int compareTo(Extension other) { if (name != null && other.name != null) { diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceType.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceElementType.java similarity index 74% rename from core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceType.java rename to core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceElementType.java index 6da161b75714e..8d3fc0d573038 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceType.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/SourceElementType.java @@ -1,6 +1,6 @@ package io.quarkus.annotation.processor.documentation.config.model; -public enum SourceType { +public enum SourceElementType { METHOD, FIELD; diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java index d6fa963d6dd12..34131fcf17a6c 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java @@ -134,8 +134,8 @@ private void resolveProperty(ConfigRoot configRoot, Map e if (configSection != null) { configSection.appendState(discoveryConfigProperty.isSectionGenerated(), deprecation); } else { - configSection = new ConfigSection(discoveryConfigProperty.getSourceClass(), - discoveryConfigProperty.getSourceName(), discoveryConfigProperty.getSourceType(), + configSection = new ConfigSection(discoveryConfigProperty.getSourceType(), + discoveryConfigProperty.getSourceElementName(), discoveryConfigProperty.getSourceElementType(), new SectionPath(path), typeQualifiedName, context.getSectionLevel(), discoveryConfigProperty.isSectionGenerated(), deprecation); context.getItemCollection().addItem(configSection); @@ -202,9 +202,9 @@ private void resolveProperty(ConfigRoot configRoot, Map e // this is a standard property ConfigProperty configProperty = new ConfigProperty(phase, - discoveryConfigProperty.getSourceClass(), - discoveryConfigProperty.getSourceName(), discoveryConfigProperty.getSourceType(), + discoveryConfigProperty.getSourceElementName(), + discoveryConfigProperty.getSourceElementType(), propertyPath, additionalPropertyPaths, typeQualifiedName, typeSimplifiedName, discoveryConfigProperty.getType().isMap(), discoveryConfigProperty.getType().isList(), diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java index 4ab20e4ee24e3..4eb92d7e84277 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java @@ -67,7 +67,7 @@ public void onResolvedEnum(TypeElement enumTypeElement) { } protected void handleCommonPropertyAnnotations(DiscoveryConfigProperty.Builder builder, - Map propertyAnnotations, ResolvedType resolvedType, String sourceName) { + Map propertyAnnotations, ResolvedType resolvedType, String sourceElementName) { AnnotationMirror deprecatedAnnotation = propertyAnnotations.get(Deprecated.class.getName()); if (deprecatedAnnotation != null) { diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java index 1847c8e203503..cda99142c602e 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java @@ -15,7 +15,7 @@ import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryRootElement; import io.quarkus.annotation.processor.documentation.config.discovery.ResolvedType; import io.quarkus.annotation.processor.documentation.config.model.ConfigPhase; -import io.quarkus.annotation.processor.documentation.config.model.SourceType; +import io.quarkus.annotation.processor.documentation.config.model.SourceElementType; import io.quarkus.annotation.processor.documentation.config.util.ConfigNamingUtil; import io.quarkus.annotation.processor.documentation.config.util.Markers; import io.quarkus.annotation.processor.documentation.config.util.Types; @@ -125,11 +125,11 @@ public void onEnclosedMethod(DiscoveryRootElement discoveryRootElement, TypeElem Map methodAnnotations = utils.element().getAnnotations(method); - String sourceName = method.getSimpleName().toString(); + String sourceElementName = method.getSimpleName().toString(); DiscoveryConfigProperty.Builder builder = DiscoveryConfigProperty.builder(clazz.getQualifiedName().toString(), - sourceName, SourceType.METHOD, resolvedType); + sourceElementName, SourceElementType.METHOD, resolvedType); - String name = ConfigNamingUtil.hyphenate(sourceName); + String name = ConfigNamingUtil.hyphenate(sourceElementName); AnnotationMirror withNameAnnotation = methodAnnotations.get(Types.ANNOTATION_CONFIG_WITH_NAME); if (withNameAnnotation != null) { name = withNameAnnotation.getElementValues().values().iterator().next().getValue().toString(); @@ -152,7 +152,7 @@ public void onEnclosedMethod(DiscoveryRootElement discoveryRootElement, TypeElem } if (resolvedType.isMap()) { - String mapKey = ConfigNamingUtil.hyphenate(sourceName); + String mapKey = ConfigNamingUtil.hyphenate(sourceElementName); AnnotationMirror configDocMapKeyAnnotation = methodAnnotations.get(Types.ANNOTATION_CONFIG_DOC_MAP_KEY); if (configDocMapKeyAnnotation != null) { mapKey = configDocMapKeyAnnotation.getElementValues().values().iterator().next().getValue().toString(); @@ -169,7 +169,7 @@ public void onEnclosedMethod(DiscoveryRootElement discoveryRootElement, TypeElem builder.converted(); } - handleCommonPropertyAnnotations(builder, methodAnnotations, resolvedType, sourceName); + handleCommonPropertyAnnotations(builder, methodAnnotations, resolvedType, sourceElementName); discoveryRootElement.addProperty(builder.build()); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java index 6a1ca922d3da3..2410b8c62b9d6 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java @@ -16,7 +16,7 @@ import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryRootElement; import io.quarkus.annotation.processor.documentation.config.discovery.ResolvedType; import io.quarkus.annotation.processor.documentation.config.model.ConfigPhase; -import io.quarkus.annotation.processor.documentation.config.model.SourceType; +import io.quarkus.annotation.processor.documentation.config.model.SourceElementType; import io.quarkus.annotation.processor.documentation.config.util.ConfigNamingUtil; import io.quarkus.annotation.processor.documentation.config.util.Markers; import io.quarkus.annotation.processor.documentation.config.util.Types; @@ -123,11 +123,11 @@ public void onEnclosedField(DiscoveryRootElement discoveryRootElement, TypeEleme Map fieldAnnotations = utils.element().getAnnotations(field); - String sourceName = field.getSimpleName().toString(); - String name = ConfigNamingUtil.hyphenate(sourceName); + String sourceElementName = field.getSimpleName().toString(); + String name = ConfigNamingUtil.hyphenate(sourceElementName); DiscoveryConfigProperty.Builder builder = DiscoveryConfigProperty.builder(clazz.getQualifiedName().toString(), - sourceName, SourceType.FIELD, resolvedType); + sourceElementName, SourceElementType.FIELD, resolvedType); AnnotationMirror configItemAnnotation = fieldAnnotations.get(Types.ANNOTATION_CONFIG_ITEM); if (configItemAnnotation != null) { @@ -158,7 +158,7 @@ public void onEnclosedField(DiscoveryRootElement discoveryRootElement, TypeEleme builder.name(name); if (resolvedType.isMap()) { - String mapKey = ConfigNamingUtil.hyphenate(sourceName); + String mapKey = ConfigNamingUtil.hyphenate(sourceElementName); AnnotationMirror configDocMapKeyAnnotation = fieldAnnotations.get(Types.ANNOTATION_CONFIG_DOC_MAP_KEY); if (configDocMapKeyAnnotation != null) { mapKey = configDocMapKeyAnnotation.getElementValues().values().iterator().next().getValue().toString(); @@ -171,7 +171,7 @@ public void onEnclosedField(DiscoveryRootElement discoveryRootElement, TypeEleme builder.converted(); } - handleCommonPropertyAnnotations(builder, fieldAnnotations, resolvedType, sourceName); + handleCommonPropertyAnnotations(builder, fieldAnnotations, resolvedType, sourceElementName); discoveryRootElement.addProperty(builder.build()); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/util/ExtensionUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/util/ExtensionUtil.java index 6634c70469e59..f30eca643686e 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/util/ExtensionUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/util/ExtensionUtil.java @@ -1,11 +1,15 @@ package io.quarkus.annotation.processor.util; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Map; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.tools.Diagnostic.Kind; +import javax.tools.StandardLocation; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -19,14 +23,12 @@ public final class ExtensionUtil { + private static final String RUNTIME_MARKER_FILE = "META-INF/quarkus.properties"; + private static final String ARTIFACT_DEPLOYMENT_SUFFIX = "-deployment"; - private static final String ARTIFACT_COMMON_SUFFIX = "-common"; - private static final String ARTIFACT_INTERNAL_SUFFIX = "-internal"; private static final String NAME_QUARKUS_PREFIX = "Quarkus - "; private static final String NAME_RUNTIME_SUFFIX = " - Runtime"; private static final String NAME_DEPLOYMENT_SUFFIX = " - Deployment"; - private static final String NAME_COMMON_SUFFIX = " - Common"; - private static final String NAME_INTERNAL_SUFFIX = " - Internal"; private final ProcessingEnvironment processingEnv; private final FilerUtil filerUtil; @@ -111,27 +113,19 @@ private Extension getExtensionFromPom(Path pom, Document doc) { return Extension.createNotDetected(); } - boolean commonOrInternal = false; + boolean runtime = isRuntime(); - if (artifactId.endsWith(ARTIFACT_DEPLOYMENT_SUFFIX)) { + if (!runtime && artifactId.endsWith(ARTIFACT_DEPLOYMENT_SUFFIX)) { artifactId = artifactId.substring(0, artifactId.length() - ARTIFACT_DEPLOYMENT_SUFFIX.length()); } - if (artifactId.endsWith(ARTIFACT_COMMON_SUFFIX)) { - artifactId = artifactId.substring(0, artifactId.length() - ARTIFACT_COMMON_SUFFIX.length()); - commonOrInternal = true; - } - if (artifactId.endsWith(ARTIFACT_INTERNAL_SUFFIX)) { - artifactId = artifactId.substring(0, artifactId.length() - ARTIFACT_INTERNAL_SUFFIX.length()); - commonOrInternal = true; - } NameSource nameSource; Optional nameFromExtensionMetadata = getExtensionNameFromExtensionMetadata(); if (nameFromExtensionMetadata.isPresent()) { name = nameFromExtensionMetadata.get(); - nameSource = commonOrInternal ? NameSource.EXTENSION_METADATA_COMMON_INTERNAL : NameSource.EXTENSION_METADATA; + nameSource = NameSource.EXTENSION_METADATA; } else if (name != null) { - nameSource = commonOrInternal ? NameSource.POM_XML_COMMON_INTERNAL : NameSource.POM_XML; + nameSource = NameSource.POM_XML; } else { nameSource = NameSource.NONE; } @@ -140,18 +134,15 @@ private Extension getExtensionFromPom(Path pom, Document doc) { if (name.startsWith(NAME_QUARKUS_PREFIX)) { name = name.substring(NAME_QUARKUS_PREFIX.length()).trim(); } - if (name.endsWith(NAME_DEPLOYMENT_SUFFIX)) { + if (!runtime && name.endsWith(NAME_DEPLOYMENT_SUFFIX)) { name = name.substring(0, name.length() - NAME_DEPLOYMENT_SUFFIX.length()); - } else if (name.endsWith(NAME_RUNTIME_SUFFIX)) { + } + if (runtime && name.endsWith(NAME_RUNTIME_SUFFIX)) { name = name.substring(0, name.length() - NAME_RUNTIME_SUFFIX.length()); - } else if (name.endsWith(NAME_COMMON_SUFFIX)) { - name = name.substring(0, name.length() - NAME_COMMON_SUFFIX.length()); - } else if (name.endsWith(NAME_INTERNAL_SUFFIX)) { - name = name.substring(0, name.length() - NAME_INTERNAL_SUFFIX.length()); } } - return new Extension(groupId, artifactId, name, nameSource, true); + return Extension.of(groupId, artifactId, name, nameSource); } private Optional getExtensionNameFromExtensionMetadata() { @@ -168,4 +159,14 @@ private Optional getExtensionNameFromExtensionMetadata() { return Optional.of(extensionName.trim()); } + + private boolean isRuntime() { + try { + Path runtimeMarkerFile = Paths + .get(processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", RUNTIME_MARKER_FILE).toUri()); + return Files.exists(runtimeMarkerFile); + } catch (IOException e) { + return false; + } + } } diff --git a/core/processor/src/test/java/io/quarkus/annotation/processor/documentation/config/formatter/AsciidocToMarkdownTransformerTest.java b/core/processor/src/test/java/io/quarkus/annotation/processor/documentation/config/formatter/AsciidocToMarkdownTransformerTest.java new file mode 100644 index 0000000000000..f0ab9d73c7013 --- /dev/null +++ b/core/processor/src/test/java/io/quarkus/annotation/processor/documentation/config/formatter/AsciidocToMarkdownTransformerTest.java @@ -0,0 +1,200 @@ +package io.quarkus.annotation.processor.documentation.config.formatter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import io.quarkus.annotation.processor.documentation.config.model.JavadocFormat; + +public class AsciidocToMarkdownTransformerTest { + + @Test + public void testSourceBlocks() { + String asciidoc = """ + [source,java] + ---- + System.out.println("Hello, World!"); + ----"""; + String expectedMarkdown = """ + ```java + System.out.println("Hello, World!"); + ```"""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testQuoteBlocks() { + String asciidoc = """ + [quote, John Doe] + ____ + This is a quote. + ____"""; + String expectedMarkdown = """ + > This is a quote. + > + > — John Doe"""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testAdmonitions() { + String asciidoc = "NOTE: This is a note."; + String expectedMarkdown = "> 📌 This is a note."; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testAdmonitionBlock() { + String asciidoc = """ + [NOTE] + ==== + This is an admonition block. + ===="""; + String expectedMarkdown = """ + > [!NOTE] + > This is an admonition block."""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testQuotes() { + String asciidoc = "_This is italic_, *This is bold*"; + String expectedMarkdown = "_This is italic_, **This is bold**"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testLists() { + String asciidoc = """ + * Item 1 + * Item 2 + * Item 3"""; + String expectedMarkdown = """ + - Item 1 + - Item 2 + - Item 3"""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testLinks() { + String asciidoc = "https://example.com[Example]"; + String expectedMarkdown = "[Example](https://example.com)"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + + asciidoc = "link:https://example.com[Example]"; + expectedMarkdown = "[Example](https://example.com)"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + + // TODO render the attributes + asciidoc = "link:{hibernate-search-docs-url}#backend-elasticsearch-configuration-index-settings[this section of the reference documentation]"; + expectedMarkdown = "[this section of the reference documentation]({hibernate-search-docs-url}#backend-elasticsearch-configuration-index-settings)"; + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testXrefs() { + String asciidoc = "xref:example.adoc[Example]"; + String expectedMarkdown = "[Example](https://quarkus.io/guides/example)"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + + asciidoc = "xref:example.adoc#test[Example]"; + expectedMarkdown = "[Example](https://quarkus.io/guides/example#test)"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + + asciidoc = "xref:test[Example]"; + expectedMarkdown = "[Example](#test)"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testComposite() { + String asciidoc = """ + Here is some regular text. + + *This is bold text* and visit https://example.com[Example] for more info. + + [source,java] + ---- + System.out.println("Hello, World!"); + ---- + + A quote block: + [quote, John Doe] + ____ + This is a quote. + ____ + + A list: + * Item 1 + * Item 2 + * Item 3"""; + String expectedMarkdown = """ + Here is some regular text. + + **This is bold text** and visit [Example](https://example.com) for more info. + + ```java + System.out.println("Hello, World!"); + ``` + + A quote block: + > This is a quote. + > + > — John Doe + + A list: + - Item 1 + - Item 2 + - Item 3"""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testIcons() { + String asciidoc = "This looks right with role: icon:check[role=lime] and without role: icon:check[] and this looks bad with role: icon:times[role=red] and without role: icon:times[]"; + String expectedMarkdown = "This looks right with role: ✅ and without role: ✅ and this looks bad with role: ❌ and without role: ❌"; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } + + @Test + public void testDescriptionLists() { + String asciidoc = """ + This is a simple paragraph. + + Item title1:: item description 1 + Item title2:: item description 2 + + Item title1:: + Item description 1 + + Item title2:: + Item description 2"""; + String expectedMarkdown = """ + This is a simple paragraph. + + **Item title1** item description 1 + **Item title2** item description 2 + + **Item title1** + Item description 1 + + **Item title2** + Item description 2"""; + + assertEquals(expectedMarkdown, JavadocToMarkdownTransformer.toMarkdown(asciidoc, JavadocFormat.ASCIIDOC)); + } +} diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java index 3fd3c90adbb37..852ff6024d9bb 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java @@ -92,7 +92,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { List targetDirectories = findTargetDirectories(resolvedScanDirectory); JavadocRepository javadocRepository = JavadocMerger.mergeJavadocElements(targetDirectories); - MergedModel mergedModel = ModelMerger.mergeModel(javadocRepository, targetDirectories); + MergedModel mergedModel = ModelMerger.mergeModel(javadocRepository, targetDirectories, true); Format normalizedFormat = Format.normalizeFormat(format); diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/generator/AbstractFormatter.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/generator/AbstractFormatter.java index 51d45e63baebe..a19b5b07db5b7 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/generator/AbstractFormatter.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/generator/AbstractFormatter.java @@ -37,8 +37,8 @@ public boolean displayConfigRootDescription(ConfigRootKey configRootKey, int map @Override public String formatDescription(ConfigProperty configProperty) { - Optional javadocElement = javadocRepository.getElement(configProperty.getSourceClass(), - configProperty.getSourceName()); + Optional javadocElement = javadocRepository.getElement(configProperty.getSourceType(), + configProperty.getSourceElementName()); if (javadocElement.isEmpty()) { return null; @@ -193,19 +193,21 @@ public String toAnchor(String value) { @Override public String formatSectionTitle(ConfigSection configSection) { - Optional javadocElement = javadocRepository.getElement(configSection.getSourceClass(), - configSection.getSourceName()); + Optional javadocElement = javadocRepository.getElement(configSection.getSourceType(), + configSection.getSourceElementName()); if (javadocElement.isEmpty()) { throw new IllegalStateException( - "Couldn't find section title for: " + configSection.getSourceClass() + "#" + configSection.getSourceName()); + "Couldn't find section title for: " + configSection.getSourceType() + "#" + + configSection.getSourceElementName()); } String javadoc = JavadocTransformer.transform(javadocElement.get().description(), javadocElement.get().format(), javadocFormat()); if (javadoc == null || javadoc.isBlank()) { throw new IllegalStateException( - "Couldn't find section title for: " + configSection.getSourceClass() + "#" + configSection.getSourceName()); + "Couldn't find section title for: " + configSection.getSourceType() + "#" + + configSection.getSourceElementName()); } return trimFinalDot(javadoc); diff --git a/devtools/extension-deployment-maven-plugin/pom.xml b/devtools/extension-deployment-maven-plugin/pom.xml new file mode 100644 index 0000000000000..2bd2ae1ae65a4 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/pom.xml @@ -0,0 +1,180 @@ + + 4.0.0 + + io.quarkus + quarkus-devtools-all + 999-SNAPSHOT + + quarkus-extension-deployment-maven-plugin + Quarkus - Extension deployment Maven plugin + maven-plugin + + 3.9.8 + + + + + + maven-compiler-plugin + + + maven-javadoc-plugin + + true + none + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + io.quarkus + quarkus-enforcer-rules + ${project.version} + + + + + enforce + ${maven-enforcer-plugin.phase} + + + + + classpath:enforcer-rules/quarkus-require-java-version.xml + + + classpath:enforcer-rules/quarkus-require-maven-version.xml + + + classpath:enforcer-rules/quarkus-banned-dependencies.xml + + + + + com.google.code.findbugs:jsr305 + + com.google.guava:listenablefuture + + + + + + enforce + + + + + + + + + org.eclipse.sisu + sisu-maven-plugin + 0.9.0.M3 + + + index-project + + main-index + test-index + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + quarkus-config-doc + true + + + + help-goal + + helpmojo + + + + default-config-doc + generate-resources + + + + + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + javax.inject + javax.inject + + + org.apache.maven + maven-plugin-api + provided + + + org.apache.maven.plugin-tools + maven-plugin-annotations + + + org.apache.maven + maven-core + provided + + + org.apache.maven + maven-embedder + provided + + + org.apache.maven + maven-settings-builder + provided + + + org.apache.maven + maven-resolver-provider + provided + + + org.apache.maven + maven-model + provided + + + org.apache.maven + maven-model-builder + provided + + + org.apache.maven + maven-artifact + provided + + + org.apache.maven + maven-settings + provided + + + org.apache.maven + maven-builder-support + provided + + + org.apache.maven + maven-repository-metadata + provided + + + diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/AttachConfigMetadataMojo.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/AttachConfigMetadataMojo.java new file mode 100644 index 0000000000000..a3b3af24b11d0 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/AttachConfigMetadataMojo.java @@ -0,0 +1,238 @@ +package io.quarkus.maven.extension.deployment.metadata; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; + +import io.quarkus.annotation.processor.documentation.config.formatter.JavadocTransformer; +import io.quarkus.annotation.processor.documentation.config.merger.JavadocMerger; +import io.quarkus.annotation.processor.documentation.config.merger.JavadocRepository; +import io.quarkus.annotation.processor.documentation.config.merger.MergedModel; +import io.quarkus.annotation.processor.documentation.config.merger.MergedModel.ConfigRootKey; +import io.quarkus.annotation.processor.documentation.config.merger.ModelMerger; +import io.quarkus.annotation.processor.documentation.config.model.AbstractConfigItem; +import io.quarkus.annotation.processor.documentation.config.model.ConfigItemVisitor; +import io.quarkus.annotation.processor.documentation.config.model.ConfigProperty; +import io.quarkus.annotation.processor.documentation.config.model.ConfigProperty.PropertyPath; +import io.quarkus.annotation.processor.documentation.config.model.ConfigRoot; +import io.quarkus.annotation.processor.documentation.config.model.Extension; +import io.quarkus.annotation.processor.documentation.config.model.Extension.NameSource; +import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; +import io.quarkus.annotation.processor.documentation.config.model.JavadocFormat; +import io.quarkus.annotation.processor.documentation.config.model.SourceElementType; +import io.quarkus.annotation.processor.documentation.config.util.JacksonMappers; +import io.quarkus.maven.extension.deployment.metadata.model.spring.QuarkusConfigAdditionalMetadataProperty; +import io.quarkus.maven.extension.deployment.metadata.model.spring.QuarkusConfigAdditionalMetadataProperty.ConfigPhase; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataDeprecation; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataGroup; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataHint; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataHintValue; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataModel; +import io.quarkus.maven.extension.deployment.metadata.model.spring.SpringConfigMetadataProperty; + +@Mojo(name = "attach-config-metadata", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE) +public class AttachConfigMetadataMojo extends AbstractMojo { + + private static final String DEPLOYMENT_ARTIFACT_SUFFIX = "-deployment"; + private static final String META_INF = "META-INF"; + private static final Path OUTPUT_FILE = Path.of("config-metadata.json"); + + @Parameter(defaultValue = "${session}", readonly = true) + private MavenSession session; + + @Parameter(defaultValue = "${project}", readonly = true) + private MavenProject project; + + @Component + private MavenProjectHelper projectHelper; + + @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) + protected File targetDirectory; + + @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true) + protected File outputDirectory; + + @Parameter(defaultValue = "false") + private boolean skip; + + private final List openZipFileSystems = new ArrayList<>(); + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (skip) { + return; + } + + try { + List metaInfDirectories = getMetaInfDirectories(); + SpringConfigMetadataModel springConfigMetadataModel = generateSpringConfigMetadataModel(metaInfDirectories); + + Path springConfigMetadataModelPath = targetDirectory.toPath().resolve(OUTPUT_FILE); + JacksonMappers.jsonObjectWriter().writeValue(springConfigMetadataModelPath.toFile(), springConfigMetadataModel); + + // attach the metadata to the build + // note that we probably want to attach a zip/jar containing all sorts of metadata rather than a single file + projectHelper.attachArtifact(project, "json", "config-metadata", springConfigMetadataModelPath.toFile()); + } catch (Exception e) { + getLog().error("Unable to generate the metadata", e); + } finally { + for (FileSystem openZipFileSystem : openZipFileSystems) { + try { + openZipFileSystem.close(); + } catch (IOException e) { + getLog().error("Unable to close jar file system", e); + } + } + } + } + + private SpringConfigMetadataModel generateSpringConfigMetadataModel(List metaInfDirectories) { + MergedModel mergedModel = ModelMerger.mergeModel(metaInfDirectories); + if (mergedModel.isEmpty()) { + return SpringConfigMetadataModel.empty(); + } + + Map configRoots = mergedModel.getConfigRoots().get(getExtension()); + if (configRoots == null || configRoots.isEmpty()) { + return SpringConfigMetadataModel.empty(); + } + + JavadocRepository javadocRepository = JavadocMerger.mergeJavadocElements(metaInfDirectories); + + // TODO: for now we don't generate the groups, we will see how it goes + List groups = new ArrayList<>(); + List properties = new ArrayList<>(); + List hints = new ArrayList<>(); + + for (ConfigRoot configRoot : configRoots.values()) { + configRoot.walk(new ConfigItemVisitor() { + + @Override + public void visit(AbstractConfigItem configItem) { + if (configItem instanceof ConfigProperty configProperty) { + // TODO: we will need to add more metadata on our side + SpringConfigMetadataDeprecation deprecation = configProperty.isDeprecated() + ? new SpringConfigMetadataDeprecation("warning", + configProperty.getDeprecation().reason(), + configProperty.getDeprecation().replacement(), + configProperty.getDeprecation().since()) + : null; + + String description = getJavadoc(javadocRepository, configProperty.getSourceType(), + configProperty.getSourceElementName()); + + List hintValues = List.of(); + + ConfigPhase phase = ConfigPhase.of(configProperty.getPhase()); + + properties.add(new SpringConfigMetadataProperty(configProperty.getPath().property(), + configProperty.getType(), description, + configProperty.getSourceType(), + configProperty.getSourceElementType() == SourceElementType.FIELD + ? configProperty.getSourceElementName() + : null, + configProperty.getSourceElementType() == SourceElementType.METHOD + ? configProperty.getSourceElementName() + : null, + configProperty.getDefaultValue(), + deprecation, new QuarkusConfigAdditionalMetadataProperty(phase, + configProperty.getPath().environmentVariable(), configProperty.isOptional()))); + if (configProperty.isEnum()) { + hintValues = configProperty.getEnumAcceptedValues().values().entrySet().stream() + .map(e -> new SpringConfigMetadataHintValue(e.getValue().configValue(), + getJavadoc(javadocRepository, configProperty.getType(), e.getKey()))) + .toList(); + hints.add(new SpringConfigMetadataHint(configProperty.getPath().property(), hintValues)); + } + + for (PropertyPath additionalPath : configProperty.getAdditionalPaths()) { + properties.add( + new SpringConfigMetadataProperty(additionalPath.property(), configProperty.getType(), + description, configProperty.getSourceType(), + configProperty.getSourceElementType() == SourceElementType.FIELD + ? configProperty.getSourceElementName() + : null, + configProperty.getSourceElementType() == SourceElementType.METHOD + ? configProperty.getSourceElementName() + : null, + configProperty.getDefaultValue(), + deprecation, new QuarkusConfigAdditionalMetadataProperty(phase, + additionalPath.environmentVariable(), configProperty.isOptional()))); + if (configProperty.isEnum()) { + hints.add(new SpringConfigMetadataHint(additionalPath.property(), hintValues)); + } + } + } + } + }); + } + + return new SpringConfigMetadataModel(groups, properties, hints); + } + + private List getMetaInfDirectories() { + List classPathElements = new ArrayList<>(); + classPathElements.add(outputDirectory.toPath().resolve(META_INF)); + + for (Artifact dependency : project.getArtifacts()) { + if (!dependency.getArtifactHandler().isAddedToClasspath()) { + continue; + } + + Path artifactPath = dependency.getFile().toPath(); + + if (Files.isDirectory(artifactPath)) { + classPathElements.add(dependency.getFile().toPath().resolve(META_INF)); + } else { + // it's a jar + try { + FileSystem jarFs = FileSystems.newFileSystem(artifactPath); + openZipFileSystems.add(jarFs); + classPathElements.add(jarFs.getPath("/" + META_INF)); + } catch (IOException e) { + getLog().error("Unable to open jar: " + artifactPath, e); + } + } + } + + return Collections.unmodifiableList(classPathElements); + } + + private Extension getExtension() { + return new Extension(project.getGroupId(), + project.getArtifactId().substring(0, project.getArtifactId().length() - DEPLOYMENT_ARTIFACT_SUFFIX.length()), + null, NameSource.NONE, false, true); + } + + private String getJavadoc(JavadocRepository javadocRepository, String sourceType, String sourceElementName) { + Optional javadocElement = javadocRepository + .getElement(sourceType, sourceElementName); + if (javadocElement.isEmpty()) { + return null; + } + + return JavadocTransformer.transform(javadocElement.get().description(), + javadocElement.get().format(), JavadocFormat.MARKDOWN); + } +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/QuarkusConfigAdditionalMetadataProperty.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/QuarkusConfigAdditionalMetadataProperty.java new file mode 100644 index 0000000000000..78b33c5f816e7 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/QuarkusConfigAdditionalMetadataProperty.java @@ -0,0 +1,24 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +public record QuarkusConfigAdditionalMetadataProperty(ConfigPhase phase, String environmentVariable, boolean optional) { + + public enum ConfigPhase { + RUN_TIME, + BUILD_TIME, + BUILD_AND_RUN_TIME_FIXED; + + public static ConfigPhase of(io.quarkus.annotation.processor.documentation.config.model.ConfigPhase phase) { + switch (phase) { + case BUILD_AND_RUN_TIME_FIXED: + return ConfigPhase.BUILD_AND_RUN_TIME_FIXED; + case BUILD_TIME: + return ConfigPhase.BUILD_TIME; + case RUN_TIME: + return ConfigPhase.RUN_TIME; + default: + throw new IllegalStateException( + "Phase " + phase + " not supported in " + ConfigPhase.class.getSimpleName()); + } + } + } +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataDeprecation.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataDeprecation.java new file mode 100644 index 0000000000000..26a21f32a1026 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataDeprecation.java @@ -0,0 +1,5 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +public record SpringConfigMetadataDeprecation(String level, String reason, String replacement, String since) { + +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataGroup.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataGroup.java new file mode 100644 index 0000000000000..68c7947db2be3 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataGroup.java @@ -0,0 +1,5 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +public record SpringConfigMetadataGroup(String name, String type, String description, String sourceType, String sourceMethod) { + +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHint.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHint.java new file mode 100644 index 0000000000000..bf0a539446665 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHint.java @@ -0,0 +1,7 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +import java.util.List; + +public record SpringConfigMetadataHint(String name, List values) { + +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHintValue.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHintValue.java new file mode 100644 index 0000000000000..353787ae68794 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataHintValue.java @@ -0,0 +1,5 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +public record SpringConfigMetadataHintValue(String value, String description) { + +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataModel.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataModel.java new file mode 100644 index 0000000000000..302d9c8659552 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataModel.java @@ -0,0 +1,12 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +import java.util.List; + +public record SpringConfigMetadataModel(List groups, + List properties, + List hints) { + + public static SpringConfigMetadataModel empty() { + return new SpringConfigMetadataModel(List.of(), List.of(), List.of()); + } +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataProperty.java b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataProperty.java new file mode 100644 index 0000000000000..bdd199acb3ffd --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/java/io/quarkus/maven/extension/deployment/metadata/model/spring/SpringConfigMetadataProperty.java @@ -0,0 +1,9 @@ +package io.quarkus.maven.extension.deployment.metadata.model.spring; + +public record SpringConfigMetadataProperty(String name, String type, String description, + String sourceType, + // these two are not in the Spring format but were explicitly requested + String sourceField, String sourceMethod, String defaultValue, + SpringConfigMetadataDeprecation deprecation, QuarkusConfigAdditionalMetadataProperty quarkus) { + +} diff --git a/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml new file mode 100644 index 0000000000000..89e04568801b0 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml @@ -0,0 +1,17 @@ + + + + + + attach-deployment-metadata + + + + + true + false + + + + + \ No newline at end of file diff --git a/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/plexus/components.xml b/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/plexus/components.xml new file mode 100644 index 0000000000000..fde05a043dd99 --- /dev/null +++ b/devtools/extension-deployment-maven-plugin/src/main/resources/META-INF/plexus/components.xml @@ -0,0 +1,20 @@ + + + + org.apache.maven.lifecycle.Lifecycle + org.apache.maven.lifecycle.Lifecycle + quarkus-extension-deployment + + attach-config-metadata + + my-plugin-not-used-phase + + + + io.quarkus:quarkus-extension-deployment-maven-plugin:${project.version}:attach-config-metadata + + + + + + diff --git a/devtools/pom.xml b/devtools/pom.xml index b1cd04e9e4a38..a232ce226f2f3 100644 --- a/devtools/pom.xml +++ b/devtools/pom.xml @@ -27,5 +27,6 @@ gradle cli config-doc-maven-plugin + extension-deployment-maven-plugin diff --git a/extensions/hibernate-orm/deployment/pom.xml b/extensions/hibernate-orm/deployment/pom.xml index e17abfe0a5389..1829df031af8e 100644 --- a/extensions/hibernate-orm/deployment/pom.xml +++ b/extensions/hibernate-orm/deployment/pom.xml @@ -118,6 +118,10 @@ + + io.quarkus + quarkus-extension-deployment-maven-plugin + maven-surefire-plugin diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/pom.xml b/extensions/hibernate-search-orm-elasticsearch/deployment/pom.xml index 6862482c6010a..e7f2a9942cf95 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/pom.xml +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/pom.xml @@ -73,6 +73,10 @@ + + io.quarkus + quarkus-extension-deployment-maven-plugin + maven-compiler-plugin diff --git a/extensions/hibernate-validator/deployment/pom.xml b/extensions/hibernate-validator/deployment/pom.xml index 3b525bd111dbc..01a2c73ec73c0 100644 --- a/extensions/hibernate-validator/deployment/pom.xml +++ b/extensions/hibernate-validator/deployment/pom.xml @@ -70,6 +70,10 @@ + + io.quarkus + quarkus-extension-deployment-maven-plugin + maven-compiler-plugin diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/deployment/pom.tpl.qute.xml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/deployment/pom.tpl.qute.xml index 83e3cf1eb8451..e928303dbf396 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/deployment/pom.tpl.qute.xml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/deployment/pom.tpl.qute.xml @@ -36,6 +36,16 @@ + {#if quarkus.version.or(quarkus.bom.version).compareVersionTo("3.16") >= 0} + + io.quarkus + quarkus-extension-deployment-maven-plugin + {#if quarkus.version} + $\{quarkus.version} + {/if} + true + + {/if} maven-compiler-plugin