diff --git a/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Description.java b/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Description.java new file mode 100644 index 000000000..c977555e9 --- /dev/null +++ b/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Description.java @@ -0,0 +1,58 @@ +package org.apache.maven.plugins.annotations; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Describes a {@code Mojo} or a Mojo’s {@code Parameter} when JavaDoc extraction is not feasible (because of deviating + * documentation goals) or not possible (e.g. for other JVM languages like Scala, Groovy or Kotlin). + */ +@Documented +@Retention( RetentionPolicy.CLASS ) +@Target( { ElementType.TYPE, ElementType.FIELD } ) +@Inherited +public @interface Description +{ + /** + * Description content for the {@code Mojo} or Mojo {@code Parameter}. + * + *

A "Safe HTML" subset can be used. This is achieved by running + * the content through the OWASP Java HTML Sanitizer + * before rendering.

+ * + * @return a description of the Mojo or the parameter. + */ + String value(); + + /** + * The version of the plugin since when this goal or parameter was introduced (inclusive, optional). + * + * @return The version of the plugin since when this goal or parameter was introduced (inclusive) or an empty string + * of no since version has been given. + */ + String since() default ""; + +} diff --git a/maven-plugin-plugin/src/site/apt/examples/using-annotations.apt.vm b/maven-plugin-plugin/src/site/apt/examples/using-annotations.apt.vm index d6a0df614..87fd9a15d 100644 --- a/maven-plugin-plugin/src/site/apt/examples/using-annotations.apt.vm +++ b/maven-plugin-plugin/src/site/apt/examples/using-annotations.apt.vm @@ -31,9 +31,9 @@ Using Plugin Tools Java Annotations Since version 3.0 of the maven-plugin-plugin, you can use Java5 annotations to generate the plugin descriptor file. - <> With annotations, your Mojo super class does not have to be in the same project. Provided that the super class also uses annotations, it + <> With annotations, your Mojo super class does not have to be in the same project: provided that the super class also uses annotations, it can now come from reactor projects or external dependencies. By default all dependencies are scanned, but this can be reduced with the <<>> - parameter. As javadoc doclets are still useful for <<<@since>>>, <<<@deprecated>>> and comments, the sources are still scanned. So if you use an external + parameter. BUT as javadoc doclets are still useful for goal an parameter description, <<<@since>>> and <<<@deprecated>>>, the sources are still scanned. So if you use an external dependency, you must still provide an artifact with sources (<<>> classifier) to provide documentation (the tooling will skip error if this artifact sources is missing). @@ -53,10 +53,12 @@ Using Plugin Tools Java Annotations * <<<@Parameter>>>: Used to configure your Mojo parameters, - * <<<@Component>>>: Used to configure injection of Plexus components or Maven context components. + * <<<@Component>>>: Used to configure injection of Plexus components or Maven context components, [] + * since 3.7, one optional <<<@Description>>> class and field level annotation to override javadoc doclet in Java, or add document when goal are written in Kotlin. + [] For more information on these annotations, see the diff --git a/maven-plugin-tools-annotations/pom.xml b/maven-plugin-tools-annotations/pom.xml index d20cf649e..a8c32f761 100644 --- a/maven-plugin-tools-annotations/pom.xml +++ b/maven-plugin-tools-annotations/pom.xml @@ -101,6 +101,13 @@ assertj-core test + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java index 422d58c62..a3c467102 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java @@ -288,16 +288,19 @@ protected void populateDataFromJavadoc( Map mojoAnno MojoAnnotationContent mojoAnnotationContent = entry.getValue().getMojo(); if ( mojoAnnotationContent != null ) { - mojoAnnotationContent.setDescription( javaClass.getComment() ); + if ( StringUtils.isEmpty( mojoAnnotationContent.getDescription() ) ) + { + mojoAnnotationContent.setDescription( javaClass.getComment() ); + } DocletTag since = findInClassHierarchy( javaClass, "since" ); - if ( since != null ) + if ( since != null && StringUtils.isEmpty( mojoAnnotationContent.getSince() ) ) { mojoAnnotationContent.setSince( since.getValue() ); } DocletTag deprecated = findInClassHierarchy( javaClass, "deprecated" ); - if ( deprecated != null ) + if ( deprecated != null && StringUtils.isEmpty( mojoAnnotationContent.getDeprecated() ) ) { mojoAnnotationContent.setDeprecated( deprecated.getValue() ); } diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/DescriptionAnnotationContent.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/DescriptionAnnotationContent.java new file mode 100644 index 000000000..802b8cd4b --- /dev/null +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/DescriptionAnnotationContent.java @@ -0,0 +1,63 @@ +package org.apache.maven.tools.plugin.extractor.annotations.datamodel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.StringJoiner; + +/** + * @author Benjamin Marwell + * @since 3.7.0 + */ +public class DescriptionAnnotationContent +{ + + private String value; + + private String since; + + public String value() + { + return value; + } + + public void value( String value ) + { + this.value = value; + } + + public String since() + { + return since; + } + + public void since( String since ) + { + this.since = since; + } + + @Override + public String toString() + { + return new StringJoiner( ", ", DescriptionAnnotationContent.class.getSimpleName() + "[", "]" ) + .add( "value='" + value + "'" ) + .add( "since='" + since + "'" ) + .toString(); + } +} diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java index 0407b8ea1..1c4db22ec 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java @@ -24,11 +24,13 @@ import org.apache.maven.artifact.Artifact; import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Description; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.tools.plugin.extractor.ExtractionException; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent; +import org.apache.maven.tools.plugin.extractor.annotations.datamodel.DescriptionAnnotationContent; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ParameterAnnotationContent; @@ -285,6 +287,17 @@ protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor ) mojoAnnotatedClass.setMojo( mojoAnnotationContent ); } + // @Description annotation + mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Description.class ); + if ( mojoAnnotationVisitor != null ) + { + DescriptionAnnotationContent descriptionAnnotationContent = new DescriptionAnnotationContent(); + populateAnnotationContent( descriptionAnnotationContent, mojoAnnotationVisitor ); + + mojoAnnotatedClass.getMojo().setDescription( descriptionAnnotationContent.value() ); + mojoAnnotatedClass.getMojo().setSince( descriptionAnnotationContent.since() ); + } + // @Execute annotation mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Execute.class ); if ( mojoAnnotationVisitor != null ) @@ -306,6 +319,17 @@ protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor ) populateAnnotationContent( parameterAnnotationContent, fieldAnnotationVisitor ); + DescriptionAnnotationContent descriptionAnnotationContent = new DescriptionAnnotationContent(); + final MojoAnnotationVisitor descriptionAnnotationVisitor = + annotationVisitorMap.get( Description.class.getName() ); + if ( descriptionAnnotationVisitor != null ) + { + populateAnnotationContent( descriptionAnnotationContent, descriptionAnnotationVisitor ); + } + + parameterAnnotationContent.setDescription( descriptionAnnotationContent.value() ); + parameterAnnotationContent.setSince( descriptionAnnotationContent.since() ); + if ( annotationVisitorMap.containsKey( Deprecated.class.getName() ) ) { parameterAnnotationContent.setDeprecated( EMPTY ); diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java index a4e76199c..43a27d203 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java @@ -20,6 +20,7 @@ */ import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Description; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @@ -38,10 +39,12 @@ public interface MojoAnnotationsScanner String ROLE = MojoAnnotationsScanner.class.getName(); List CLASS_LEVEL_ANNOTATIONS = Arrays.asList( Mojo.class.getName(), + Description.class.getName(), Execute.class.getName(), Deprecated.class.getName() ); List FIELD_LEVEL_ANNOTATIONS = Arrays.asList( Parameter.class.getName(), + Description.class.getName(), Component.class.getName(), Deprecated.class.getName() ); diff --git a/maven-plugin-tools-annotations/src/site/apt/index.apt b/maven-plugin-tools-annotations/src/site/apt/index.apt index db372a256..08adba66a 100644 --- a/maven-plugin-tools-annotations/src/site/apt/index.apt +++ b/maven-plugin-tools-annotations/src/site/apt/index.apt @@ -52,6 +52,7 @@ import org.apache.maven.settings.Settings; /** * Mojo Description. @Mojo( name = "" ) is the minimal required annotation. + * * @since * @deprecated */ @@ -72,10 +73,15 @@ import org.apache.maven.settings.Settings; @Execute( goal = "", phase = LifecyclePhase., lifecycle = "" ) +@Description( value = "Mojo description avoiding/overriding javadoc", + since = "equivalent of @since javadoc tag", + deprecated = "equivalent of @deprecated javadoc tag" ) // (since plugin-tools 3.7) public class MyMojo extends AbstractMojo { /** + * Parameter description. + * * @since * @deprecated */ diff --git a/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinAnnotationReaderTest.kt b/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinAnnotationReaderTest.kt new file mode 100644 index 000000000..957a29024 --- /dev/null +++ b/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinAnnotationReaderTest.kt @@ -0,0 +1,67 @@ +package org.apache.maven.tools.plugin.extractor.annotations + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.project.MavenProject +import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner +import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScannerRequest +import org.assertj.core.api.Assertions.assertThat +import org.codehaus.plexus.testing.PlexusExtension +import org.codehaus.plexus.testing.PlexusTest +import org.junit.jupiter.api.Test +import java.io.File +import java.util.* +import javax.inject.Inject + +@PlexusTest +class KotlinAnnotationReaderTest +{ + + @Inject + lateinit var mojoAnnotationsScanner: MojoAnnotationsScanner + + @Test + fun testReadKotlinMojo() + { + val request = MojoAnnotationsScannerRequest() + request.classesDirectories = listOf(File(PlexusExtension.getBasedir(), "target/test-classes")) + request.includePatterns = Arrays.asList("**/KotlinTestMojo.class") + request.project = MavenProject() + + val mojoAnnotatedClasses = mojoAnnotationsScanner.scan(request) + + assertThat(mojoAnnotatedClasses) + .hasSize(1) + + val mojoAnnotatedClass = mojoAnnotatedClasses.values.iterator().next() + + assertThat( mojoAnnotatedClass ) + .extracting( { it.className }, { it.mojo.name }, { it.mojo.description }, { it.mojo.since }, { it.parameters.size } ) + .contains( KotlinTestMojo::class.java.name, "kotlin", "KotlinTestMojo description", "3.7.0", 1 ) + + // test parameter description + val parameter = mojoAnnotatedClass.parameters.iterator().next() + assertThat( parameter ) + .extracting( { it.value.className }, { it.value.description }, { it.value.since } ) + .contains( String::class.java.name, "the cool bar to go", "3.7.0" ) + + } + +} diff --git a/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinTestMojo.kt b/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinTestMojo.kt new file mode 100644 index 000000000..d4032cc96 --- /dev/null +++ b/maven-plugin-tools-annotations/src/test/kotlin/org/apache/maven/tools/plugin/extractor/annotations/KotlinTestMojo.kt @@ -0,0 +1,50 @@ +package org.apache.maven.tools.plugin.extractor.annotations + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.AbstractMojo +import org.apache.maven.plugin.MojoExecutionException +import org.apache.maven.plugin.MojoFailureException +import org.apache.maven.plugins.annotations.Description +import org.apache.maven.plugins.annotations.Mojo +import org.apache.maven.plugins.annotations.Parameter + +/** + * This Javadoc description should not be used. + */ +@Mojo( name = "kotlin" ) +@Description( value = "KotlinTestMojo description", since = "3.7.0" ) +class KotlinTestMojo : AbstractMojo() +{ + /** + * the cool bar to go + * @since 1.0 + */ + @Parameter( property = "thebar", required = true, defaultValue = "coolbar" ) + @Description( value = "the cool bar to go", since = "3.7.0" ) + protected var bar: String? = null + + @Throws( MojoExecutionException::class, MojoFailureException::class ) + override fun execute() + { + throw UnsupportedOperationException( "invocation not supported." ) + } + +} diff --git a/maven-plugin-tools-java/src/site/apt/index.apt b/maven-plugin-tools-java/src/site/apt/index.apt index dac64e116..4c4e063a6 100644 --- a/maven-plugin-tools-java/src/site/apt/index.apt +++ b/maven-plugin-tools-java/src/site/apt/index.apt @@ -68,6 +68,8 @@ public class MyMojo extends AbstractMojo { /** + * Parameter description. + * * @parameter name="parameter" alias="myAlias" implementation="" property="aProperty" default-value="${anExpression}" * @readonly * @required diff --git a/pom.xml b/pom.xml index bf9bac7f1..f5f27c1e8 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,7 @@ 3.3.0 3.2.5 1.10.12 + 1.7.20 plugin-tools-archives/plugin-tools-LATEST 9.4 3.4.2 @@ -261,6 +262,13 @@ 1.1.0 test + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + test + @@ -275,8 +283,54 @@ true + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + 1.8 + + + + test-compile + test-compile + + test-compile + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.3.0 + + + add-jvm-lang-test-sources + initialize + + add-test-source + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/groovy + + + + + +