diff --git a/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Parameter.java b/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Parameter.java index 0e643d6b2..f9eda41ee 100644 --- a/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Parameter.java +++ b/maven-plugin-annotations/src/main/java/org/apache/maven/plugins/annotations/Parameter.java @@ -40,7 +40,7 @@ */ @Documented @Retention( RetentionPolicy.CLASS ) -@Target( { ElementType.FIELD } ) +@Target( { ElementType.FIELD, ElementType.METHOD } ) @Inherited public @interface Parameter { diff --git a/maven-plugin-plugin/pom.xml b/maven-plugin-plugin/pom.xml index e15da895b..b06d58522 100644 --- a/maven-plugin-plugin/pom.xml +++ b/maven-plugin-plugin/pom.xml @@ -312,6 +312,7 @@ src/it/settings.xml true true + true 3.12.1 3.4.1 diff --git a/maven-plugin-plugin/src/it/annotation-with-inheritance-from-deps/verify.groovy b/maven-plugin-plugin/src/it/annotation-with-inheritance-from-deps/verify.groovy index b941b803f..90b4b6920 100644 --- a/maven-plugin-plugin/src/it/annotation-with-inheritance-from-deps/verify.groovy +++ b/maven-plugin-plugin/src/it/annotation-with-inheritance-from-deps/verify.groovy @@ -61,7 +61,7 @@ assert mojo.requirements.requirement[1].role.text() == 'org.apache.maven.project assert mojo.requirements.requirement[1].'role-hint'.text() == '' assert mojo.requirements.requirement[1].'field-name'.text() == 'projectHelper' -assert mojo.parameters.parameter.size() == 4 +assert mojo.parameters.parameter.size() == 7 def parameter = mojo.parameters.parameter.findAll{ it.name.text() == "aliasedParam"}[0] @@ -107,4 +107,37 @@ assert parameter.required.text() == 'false' assert parameter.editable.text() == 'true' assert parameter.description.text() == 'Interface type as parameter.' +parameter = mojo.parameters.parameter.findAll{ it.name.text() == "paramFromSetter"}[0] + +assert parameter.name.text() == 'paramFromSetter' +assert parameter.alias.isEmpty() +assert parameter.type.text() == 'java.lang.String' +assert parameter.implementation.isEmpty() +assert parameter.deprecated.isEmpty() +assert parameter.required.text() == 'false' +assert parameter.editable.text() == 'true' +assert parameter.description.text() == 'setter as parameter.' + +parameter = mojo.parameters.parameter.findAll{ it.name.text() == "paramFromAdd"}[0] + +assert parameter.name.text() == 'paramFromAdd' +assert parameter.alias.isEmpty() +assert parameter.type.text() == 'java.lang.String' +assert parameter.implementation.isEmpty() +assert parameter.deprecated.isEmpty() +assert parameter.required.text() == 'false' +assert parameter.editable.text() == 'true' +assert parameter.description.text() == 'add method as parameter.' + +parameter = mojo.parameters.parameter.findAll{ it.name.text() == "paramFromSetterDeprecated"}[0] + +assert parameter.name.text() == 'paramFromSetterDeprecated' +assert parameter.alias.isEmpty() +assert parameter.type.text() == 'java.util.List' +assert parameter.implementation.isEmpty() +assert parameter.deprecated.text() == 'reason of deprecation' +assert parameter.required.text() == 'false' +assert parameter.editable.text() == 'true' +assert parameter.description.text() == 'deprecated setter as parameter.' + return true; 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 11785c4f4..a8d9784b4 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 @@ -41,7 +41,7 @@ Using Plugin Tools Java Annotations Information for plugin descriptor generation is specified using 4 annotations: - * 2 class-level annotations: + * 2 class level annotations: * <<<@Mojo>>>: This annotation will mark your class as a Mojo, @@ -49,10 +49,14 @@ Using Plugin Tools Java Annotations [] - * 2 field-level annotations: + * 1 field or method level annotations: * <<<@Parameter>>>: Used to configure your Mojo parameters, + [] + + * 1 field level annotations: + * <<<@Component>>>: Used to configure injection of Plexus components or Maven context components. [] 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 16210b024..c2d839ca4 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 @@ -46,6 +46,8 @@ import com.thoughtworks.qdox.model.JavaAnnotatedElement; import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaField; +import com.thoughtworks.qdox.model.JavaMember; +import com.thoughtworks.qdox.model.JavaMethod; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; @@ -63,8 +65,8 @@ import org.apache.maven.tools.plugin.extractor.GroupKey; import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor; import org.apache.maven.tools.plugin.extractor.annotations.converter.ConverterContext; -import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocBlockTagsToXhtmlConverter; import org.apache.maven.tools.plugin.extractor.annotations.converter.JavaClassConverterContext; +import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocBlockTagsToXhtmlConverter; import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocInlineTagsToXhtmlConverter; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent; import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent; @@ -141,7 +143,7 @@ public List execute( PluginToolsRequest request ) JavaProjectBuilder builder = scanJavadoc( request, mojoAnnotatedClasses.values() ); Map javaClassesMap = discoverClasses( builder ); - + JavadocLinkGenerator linkGenerator = new JavadocLinkGenerator( request.getInternalJavadocBaseUrl(), request.getInternalJavadocVersion(), request.getExternalJavadocBaseUrls(), @@ -176,11 +178,11 @@ private JavaProjectBuilder scanJavadoc( PluginToolsRequest request, // if we need to scan sources from external artifacts Set externalArtifacts = new HashSet<>(); - + JavaProjectBuilder builder = new JavaProjectBuilder( new SortedClassLibraryBuilder( ) ); builder.setEncoding( request.getEncoding() ); extendJavaProjectBuilder( builder, request.getProject() ); - + for ( MojoAnnotatedClass mojoAnnotatedClass : mojoAnnotatedClasses ) { if ( Objects.equals( mojoAnnotatedClass.getArtifact().getArtifactId(), @@ -244,7 +246,7 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, Map javaClassesMap, JavadocLinkGenerator linkGenerator ) { - + for ( Map.Entry entry : mojoAnnotatedClasses.entrySet() ) { JavaClass javaClass = javaClassesMap.get( entry.getKey() ); @@ -256,11 +258,11 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, MojoAnnotationContent mojoAnnotationContent = entry.getValue().getMojo(); if ( mojoAnnotationContent != null ) { - JavaClassConverterContext context = + JavaClassConverterContext context = new JavaClassConverterContext( javaClass, javaProjectBuilder, - mojoAnnotatedClasses, linkGenerator, + mojoAnnotatedClasses, linkGenerator, javaClass.getLineNumber() ); - mojoAnnotationContent.setDescription( + mojoAnnotationContent.setDescription( getDescriptionFromElement( javaClass, context ) ); DocletTag since = findInClassHierarchy( javaClass, "since" ); @@ -276,7 +278,7 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, } } - Map fieldsMap = extractFieldParameterTags( javaClass, javaClassesMap ); + Map elementMap = extractParameterAnnotations( javaClass, javaClassesMap ); // populate parameters Map parameters = @@ -284,27 +286,27 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, parameters = new TreeMap<>( parameters ); for ( Map.Entry parameter : parameters.entrySet() ) { - JavaField javaField = fieldsMap.get( parameter.getKey() ); - if ( javaField == null ) + JavaAnnotatedElement element = elementMap.get( parameter.getKey() ); + if ( element == null ) { continue; } - JavaClassConverterContext context = - new JavaClassConverterContext( javaClass, javaField.getDeclaringClass(), - javaProjectBuilder, mojoAnnotatedClasses, - linkGenerator, javaField.getLineNumber() ); + JavaClassConverterContext context = + new JavaClassConverterContext( javaClass, ( (JavaMember) element ).getDeclaringClass(), + javaProjectBuilder, mojoAnnotatedClasses, + linkGenerator, element.getLineNumber() ); ParameterAnnotationContent parameterAnnotationContent = parameter.getValue(); - parameterAnnotationContent.setDescription( - getDescriptionFromElement( javaField, context ) ); + parameterAnnotationContent.setDescription( + getDescriptionFromElement( element, context ) ); - DocletTag deprecated = javaField.getTagByName( "deprecated" ); + DocletTag deprecated = element.getTagByName( "deprecated" ); if ( deprecated != null ) { parameterAnnotationContent.setDeprecated( getRawValueFromTaglet ( deprecated, context ) ); } - DocletTag since = javaField.getTagByName( "since" ); + DocletTag since = element.getTagByName( "since" ); if ( since != null ) { parameterAnnotationContent.setSince( getRawValueFromTaglet ( since, context ) ); @@ -315,27 +317,27 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, Map components = entry.getValue().getComponents(); for ( Map.Entry component : components.entrySet() ) { - JavaField javaField = fieldsMap.get( component.getKey() ); - if ( javaField == null ) + JavaAnnotatedElement element = elementMap.get( component.getKey() ); + if ( element == null ) { continue; } - JavaClassConverterContext context = - new JavaClassConverterContext( javaClass, javaField.getDeclaringClass(), - javaProjectBuilder, mojoAnnotatedClasses, - linkGenerator, javaClass.getLineNumber() ); + JavaClassConverterContext context = + new JavaClassConverterContext( javaClass, ( (JavaMember) element ).getDeclaringClass(), + javaProjectBuilder, mojoAnnotatedClasses, + linkGenerator, javaClass.getLineNumber() ); ComponentAnnotationContent componentAnnotationContent = component.getValue(); - componentAnnotationContent.setDescription( - getDescriptionFromElement( javaField, context ) ); + componentAnnotationContent.setDescription( + getDescriptionFromElement( element, context ) ); - DocletTag deprecated = javaField.getTagByName( "deprecated" ); + DocletTag deprecated = element.getTagByName( "deprecated" ); if ( deprecated != null ) { componentAnnotationContent.setDeprecated( getRawValueFromTaglet ( deprecated, context ) ); } - DocletTag since = javaField.getTagByName( "since" ); + DocletTag since = element.getTagByName( "since" ); if ( since != null ) { componentAnnotationContent.setSince( getRawValueFromTaglet ( since, context ) ); @@ -355,7 +357,7 @@ protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder, */ String getDescriptionFromElement( JavaAnnotatedElement element, JavaClassConverterContext context ) { - + String comment = element.getComment(); if ( comment == null ) { @@ -411,16 +413,17 @@ private DocletTag findInClassHierarchy( JavaClass javaClass, String tagName ) /** * extract fields that are either parameters or components. + * Also extract methods that are parameters * * @param javaClass not null * @return map with Mojo parameters names as keys */ - private Map extractFieldParameterTags( JavaClass javaClass, - Map javaClassesMap ) + private Map extractParameterAnnotations( JavaClass javaClass, + Map javaClassesMap ) { try { - Map rawParams = new TreeMap<>(); + Map rawParams = new TreeMap<>(); // we have to add the parent fields first, so that they will be overwritten by the local fields if // that actually happens... @@ -428,15 +431,15 @@ private Map extractFieldParameterTags( JavaClass javaClass, if ( superClass != null ) { - if ( superClass.getFields().size() > 0 ) + if ( !superClass.getFields().isEmpty() || !superClass.getMethods().isEmpty() ) { - rawParams = extractFieldParameterTags( superClass, javaClassesMap ); + rawParams = extractParameterAnnotations( superClass, javaClassesMap ); } // maybe sources comes from scan of sources artifact superClass = javaClassesMap.get( superClass.getFullyQualifiedName() ); - if ( superClass != null ) + if ( superClass != null && ( !superClass.getFields().isEmpty() || !superClass.getMethods().isEmpty() ) ) { - rawParams = extractFieldParameterTags( superClass, javaClassesMap ); + rawParams = extractParameterAnnotations( superClass, javaClassesMap ); } } else @@ -450,6 +453,15 @@ private Map extractFieldParameterTags( JavaClass javaClass, rawParams.put( field.getName(), field ); } + for ( JavaMethod method : javaClass.getMethods() ) + { + if ( isPublicSetterMethod( method ) ) + { + rawParams.put( + StringUtils.lowercaseFirstLetter( method.getName().substring( 3 ) ), method ); + } + } + return rawParams; } catch ( NoClassDefFoundError e ) @@ -459,6 +471,16 @@ private Map extractFieldParameterTags( JavaClass javaClass, } } + private boolean isPublicSetterMethod( JavaMethod method ) + { + return method.isPublic() + && !method.isStatic() + && method.getName().length() > 3 + && ( method.getName().startsWith( "add" ) || method.getName().startsWith( "set" ) ) + && "void".equals( method.getReturnType().getValue() ) + && method.getParameters().size() == 1; + } + protected Map discoverClasses( JavaProjectBuilder builder ) { Collection javaClasses = builder.getClasses(); @@ -477,7 +499,7 @@ protected Map discoverClasses( JavaProjectBuilder builder ) return javaClassMap; } - + protected void extendJavaProjectBuilderWithSourcesJar( JavaProjectBuilder builder, Artifact artifact, PluginToolsRequest request, String classifier ) @@ -488,7 +510,7 @@ protected void extendJavaProjectBuilderWithSourcesJar( JavaProjectBuilder builde Artifact sourcesArtifact = repositorySystem.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), classifier ); - + ArtifactResolutionRequest req = new ArtifactResolutionRequest(); req.setArtifact( sourcesArtifact ); req.setLocalRepository( request.getLocal() ); @@ -501,25 +523,25 @@ protected void extendJavaProjectBuilderWithSourcesJar( JavaProjectBuilder builde + artifact.getVersion() + ". Some javadoc tags (@since, @deprecated and comments) won't be used" ); return; } - + if ( sourcesArtifact.getFile() == null || !sourcesArtifact.getFile().exists() ) { // could not get artifact sources return; } - + // extract sources to target/maven-plugin-plugin-sources/${groupId}/${artifact}/sources File extractDirectory = new File( request.getProject().getBuild().getDirectory(), "maven-plugin-plugin-sources/" + sourcesArtifact.getGroupId() + "/" + sourcesArtifact.getArtifactId() + "/" + sourcesArtifact.getVersion() + "/" + sourcesArtifact.getClassifier() ); extractDirectory.mkdirs(); - + UnArchiver unArchiver = archiverManager.getUnArchiver( "jar" ); unArchiver.setSourceFile( sourcesArtifact.getFile() ); unArchiver.setDestDirectory( extractDirectory ); unArchiver.extract(); - + extendJavaProjectBuilder( builder, Arrays.asList( extractDirectory ), request.getDependencies() ); @@ -530,7 +552,7 @@ protected void extendJavaProjectBuilderWithSourcesJar( JavaProjectBuilder builde } } - private void extendJavaProjectBuilder( JavaProjectBuilder builder, + private void extendJavaProjectBuilder( JavaProjectBuilder builder, final MavenProject project ) { List sources = new ArrayList<>(); 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..2b441751c 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 @@ -35,6 +35,7 @@ import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoAnnotationVisitor; import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoClassVisitor; import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoFieldVisitor; +import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoParameterVisitor; import org.codehaus.plexus.logging.AbstractLogEnabled; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.StringUtils; @@ -56,6 +57,8 @@ import java.util.zip.ZipInputStream; /** + * Mojo scanner with java annotations. + * * @author Olivier Lamy * @since 3.0 */ @@ -206,7 +209,7 @@ private void analyzeClassStream( Map mojoAnnotatedCl Artifact artifact, boolean excludeMojo, String source, String file ) throws IOException, ExtractionException { - MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() ); + MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( ); try { @@ -295,13 +298,13 @@ protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor ) } // @Parameter annotations - List mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Parameter.class ); - for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors ) + List mojoParameterVisitors = mojoClassVisitor.findParameterVisitors(); + for ( MojoParameterVisitor parameterVisitor : mojoParameterVisitors ) { ParameterAnnotationContent parameterAnnotationContent = - new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() ); + new ParameterAnnotationContent( parameterVisitor.getFieldName(), parameterVisitor.getClassName() ); - Map annotationVisitorMap = mojoFieldVisitor.getAnnotationVisitorMap(); + Map annotationVisitorMap = parameterVisitor.getAnnotationVisitorMap(); MojoAnnotationVisitor fieldAnnotationVisitor = annotationVisitorMap.get( Parameter.class.getName() ); populateAnnotationContent( parameterAnnotationContent, fieldAnnotationVisitor ); @@ -316,7 +319,7 @@ protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor ) } // @Component annotations - mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Component.class ); + List mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Component.class ); for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors ) { ComponentAnnotationContent componentAnnotationContent = 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..e1eb5f357 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 @@ -45,6 +45,9 @@ public interface MojoAnnotationsScanner Component.class.getName(), Deprecated.class.getName() ); + List METHOD_LEVEL_ANNOTATIONS = Arrays.asList( Parameter.class.getName(), + Deprecated.class.getName() ); + /** * Scan classes for mojo annotations. * diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java index 296cc01a2..225053c12 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java @@ -22,27 +22,25 @@ import java.util.HashMap; import java.util.Map; -import org.codehaus.plexus.logging.Logger; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Opcodes; /** + * Visitor for annotations. + * * @author Olivier Lamy * @since 3.0 */ public class MojoAnnotationVisitor extends AnnotationVisitor { - private Logger logger; - private String annotationClassName; private Map annotationValues = new HashMap<>(); - MojoAnnotationVisitor( Logger logger, String annotationClassName ) + MojoAnnotationVisitor( String annotationClassName ) { super( Opcodes.ASM9 ); - this.logger = logger; this.annotationClassName = annotationClassName; } @@ -66,23 +64,12 @@ public void visitEnum( String name, String desc, String value ) @Override public AnnotationVisitor visitAnnotation( String name, String desc ) { - return new MojoAnnotationVisitor( logger, this.annotationClassName ); + return new MojoAnnotationVisitor( this.annotationClassName ); } @Override public AnnotationVisitor visitArray( String s ) { - return new MojoAnnotationVisitor( logger, this.annotationClassName ); - } - - @Override - public void visitEnd() - { - // no op - } - - public String getAnnotationClassName() - { - return annotationClassName; + return new MojoAnnotationVisitor( this.annotationClassName ); } } diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoClassVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoClassVisitor.java index 401203474..9376564d5 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoClassVisitor.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoClassVisitor.java @@ -19,41 +19,44 @@ * under the License. */ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotatedClass; import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner; -import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.StringUtils; import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** + * Visitor for Mojo classes. + * * @author Olivier Lamy * @since 3.0 */ public class MojoClassVisitor extends ClassVisitor { - private Logger logger; - private MojoAnnotatedClass mojoAnnotatedClass; private Map annotationVisitorMap = new HashMap<>(); private List fieldVisitors = new ArrayList<>(); - public MojoClassVisitor( Logger logger ) + private List methodVisitors = new ArrayList<>(); + + public MojoClassVisitor() { super( Opcodes.ASM9 ); - this.logger = logger; } public MojoAnnotatedClass getMojoAnnotatedClass() @@ -61,53 +64,30 @@ public MojoAnnotatedClass getMojoAnnotatedClass() return mojoAnnotatedClass; } - public void setMojoAnnotatedClass( MojoAnnotatedClass mojoAnnotatedClass ) - { - this.mojoAnnotatedClass = mojoAnnotatedClass; - } - - public Map getAnnotationVisitorMap() - { - return annotationVisitorMap; - } - public MojoAnnotationVisitor getAnnotationVisitor( Class annotation ) { return annotationVisitorMap.get( annotation.getName() ); } - public void setAnnotationVisitorMap( Map annotationVisitorMap ) - { - this.annotationVisitorMap = annotationVisitorMap; - } - - public List getFieldVisitors() - { - return fieldVisitors; - } - - public void setFieldVisitors( List fieldVisitors ) - { - this.fieldVisitors = fieldVisitors; - } - public List findFieldWithAnnotation( Class annotation ) { String annotationClassName = annotation.getName(); - List mojoFieldVisitors = new ArrayList<>(); - - for ( MojoFieldVisitor mojoFieldVisitor : this.fieldVisitors ) - { - Map filedVisitorMap = mojoFieldVisitor.getAnnotationVisitorMap(); + return fieldVisitors.stream() + .filter( field -> field.getAnnotationVisitorMap().containsKey( annotationClassName ) ) + .collect( Collectors.toList() ); + } - if ( filedVisitorMap.containsKey( annotationClassName ) ) - { - mojoFieldVisitors.add( mojoFieldVisitor ); - } - } + public List findParameterVisitors() + { + String annotationClassName = Parameter.class.getName(); - return mojoFieldVisitors; + return Stream + .concat( + findFieldWithAnnotation( Parameter.class ).stream(), + methodVisitors.stream() + .filter( method -> method.getAnnotationVisitorMap().containsKey( annotationClassName ) ) ) + .collect( Collectors.toList() ); } @Override @@ -129,7 +109,7 @@ public AnnotationVisitor visitAnnotation( String desc, boolean visible ) { return null; } - MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor( logger, annotationClassName ); + MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor( annotationClassName ); annotationVisitorMap.put( annotationClassName, mojoAnnotationVisitor ); return mojoAnnotationVisitor; } @@ -137,7 +117,7 @@ public AnnotationVisitor visitAnnotation( String desc, boolean visible ) @Override public FieldVisitor visitField( int access, String name, String desc, String signature, Object value ) { - MojoFieldVisitor mojoFieldVisitor = new MojoFieldVisitor( logger, name, Type.getType( desc ).getClassName() ); + MojoFieldVisitor mojoFieldVisitor = new MojoFieldVisitor( name, Type.getType( desc ).getClassName() ); fieldVisitors.add( mojoFieldVisitor ); return mojoFieldVisitor; } @@ -145,38 +125,29 @@ public FieldVisitor visitField( int access, String name, String desc, String sig @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions ) { - // we don't need methods informations - return null; - } + if ( ( access & Opcodes.ACC_PUBLIC ) != Opcodes.ACC_PUBLIC + || ( access & Opcodes.ACC_STATIC ) == Opcodes.ACC_STATIC ) + { + return null; + } - @Override - public void visitAttribute( Attribute attr ) - { - // no op - } + if ( name.length() < 4 || !( name.startsWith( "add" ) || name.startsWith( "set" ) ) ) + { + return null; + } - @Override - public void visitSource( String source, String debug ) - { - // no op - } + Type type = Type.getType( desc ); - @Override - public void visitOuterClass( String owner, String name, String desc ) - { - // no op - } + if ( "void".equals( type.getReturnType().getClassName() ) && type.getArgumentTypes().length == 1 ) + { + String fieldName = StringUtils.lowercaseFirstLetter( name.substring( 3 ) ); + String className = type.getArgumentTypes()[0].getClassName(); - @Override - public void visitInnerClass( String name, String outerName, String innerName, int access ) - { - // no op - } + MojoMethodVisitor mojoMethodVisitor = new MojoMethodVisitor( fieldName, className ); + methodVisitors.add( mojoMethodVisitor ); + return mojoMethodVisitor; + } - @Override - public void visitEnd() - { - // no op + return null; } - } diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoFieldVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoFieldVisitor.java index f69ae0605..550bd579d 100644 --- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoFieldVisitor.java +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoFieldVisitor.java @@ -23,41 +23,40 @@ import java.util.Map; import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner; -import org.codehaus.plexus.logging.Logger; import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.Attribute; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** + * Visitors for fields. + * * @author Olivier Lamy * @since 3.0 */ public class MojoFieldVisitor - extends FieldVisitor + extends FieldVisitor implements MojoParameterVisitor { - private Logger logger; - private String fieldName; private Map annotationVisitorMap = new HashMap<>(); private String className; - MojoFieldVisitor( Logger logger, String fieldName, String className ) + MojoFieldVisitor( String fieldName, String className ) { super( Opcodes.ASM9 ); - this.logger = logger; this.fieldName = fieldName; this.className = className; } + @Override public Map getAnnotationVisitorMap() { return annotationVisitorMap; } + @Override public String getFieldName() { return fieldName; @@ -71,30 +70,14 @@ public AnnotationVisitor visitAnnotation( String desc, boolean visible ) { return null; } - MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor( logger, annotationClassName ); + MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor( annotationClassName ); annotationVisitorMap.put( annotationClassName, mojoAnnotationVisitor ); return mojoAnnotationVisitor; } @Override - public void visitAttribute( Attribute attribute ) - { - // no op - } - - @Override - public void visitEnd() - { - // no op - } - public String getClassName() { return className; } - - public void setClassName( String className ) - { - this.className = className; - } } diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoMethodVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoMethodVisitor.java new file mode 100644 index 000000000..448bb7880 --- /dev/null +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoMethodVisitor.java @@ -0,0 +1,81 @@ +package org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors; + +/* + * 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.HashMap; +import java.util.Map; + +import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Method visitor. + * + * @author Slawomir Jaranowski + */ +public class MojoMethodVisitor extends MethodVisitor implements MojoParameterVisitor +{ + private final String className; + private final String fieldName; + + private Map annotationVisitorMap = new HashMap<>(); + + public MojoMethodVisitor( String fieldName, String className ) + { + super( Opcodes.ASM9 ); + this.fieldName = fieldName; + this.className = className; + } + + @Override + public AnnotationVisitor visitAnnotation( String desc, boolean visible ) + { + String annotationClassName = Type.getType( desc ).getClassName(); + if ( !MojoAnnotationsScanner.METHOD_LEVEL_ANNOTATIONS.contains( annotationClassName ) ) + { + return null; + } + + MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor( annotationClassName ); + annotationVisitorMap.put( annotationClassName, mojoAnnotationVisitor ); + return mojoAnnotationVisitor; + } + + @Override + public String getFieldName() + { + return fieldName; + } + + @Override + public String getClassName() + { + return className; + } + + @Override + public Map getAnnotationVisitorMap() + { + return annotationVisitorMap; + } +} diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoParameterVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoParameterVisitor.java new file mode 100644 index 000000000..c95ac05c2 --- /dev/null +++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoParameterVisitor.java @@ -0,0 +1,36 @@ +package org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors; + +/* + * 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.Map; + +/** + * Common interface for field and method visitors. + * + * @author Slawomir Jaranowski + */ +public interface MojoParameterVisitor +{ + String getFieldName(); + + String getClassName(); + + Map getAnnotationVisitorMap(); +} diff --git a/maven-plugin-tools-annotations/src/site/apt/index.apt b/maven-plugin-tools-annotations/src/site/apt/index.apt index 7f86885b9..a46c37101 100644 --- a/maven-plugin-tools-annotations/src/site/apt/index.apt +++ b/maven-plugin-tools-annotations/src/site/apt/index.apt @@ -118,6 +118,15 @@ public class MyMojo @Parameter( defaultValue = "${project.build.directory}", readonly = true ) private File target; + /** + * @Parameter for methods can be used only with public setter methods + */ + @Parameter( ... ) + public setOutput( File output ) + { + ... + } + public void execute() { ... diff --git a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/FooMojo.java b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/FooMojo.java index 36dd97379..c044d161e 100644 --- a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/FooMojo.java +++ b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/FooMojo.java @@ -19,6 +19,8 @@ * under the License. */ +import java.util.List; + import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -57,6 +59,45 @@ public class FooMojo @Parameter( property = "thebeer", defaultValue = "coolbeer" ) protected String beer; + /** + * setter as parameter. + */ + @Parameter( property = "props.paramFromSetter" ) + public void setParamFromSetter(String value) + { + // empty + } + + /** + * add method as parameter. + */ + @Parameter( property = "props.paramFromAdd" ) + public void addParamFromAdd(String value) + { + // empty + } + + /** + * deprecated setter as parameter. + * + * @deprecated reason of deprecation + */ + @Deprecated + @Parameter( property = "props.paramFromSetterDeprecated" ) + public void setParamFromSetterDeprecated( List value) + { + // empty + } + + /** + * Static methods should be excluded. + */ + @Parameter + public static void setStaticMethod( String value ) + { + // empty + } + /** * */ @@ -69,4 +110,10 @@ public void execute() { // nothing } + + @Deprecated + public void deprecatedMethod(String value) + { + + } } diff --git a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/TestAnnotationsReader.java b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/TestAnnotationsReader.java index 797f46e7b..81796881c 100644 --- a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/TestAnnotationsReader.java +++ b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/TestAnnotationsReader.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import org.apache.maven.plugins.annotations.Execute; @@ -94,8 +95,8 @@ void testReadMojoClass() Collection parameters = mojoAnnotatedClass.getParameters().values(); assertThat( parameters ).isNotNull() .isNotEmpty() - .hasSize( 3 ) - .contains( + .hasSize( 6 ) + .containsExactlyInAnyOrder( new ParameterAnnotationContent( "bar", null, "thebar", "coolbar", null, true, false, String.class.getName() ), new ParameterAnnotationContent( "beer", null, "thebeer", "coolbeer", null, false, false, @@ -103,7 +104,19 @@ void testReadMojoClass() new ParameterAnnotationContent( "fooInterface", null, "fooInterface", null, FooInterfaceImpl.class, false, - false, FooInterface.class.getName() ) + false, FooInterface.class.getName() ), + new ParameterAnnotationContent( "paramFromSetter", null, "props.paramFromSetter", null, + null, + false, + false, String.class.getName() ), + new ParameterAnnotationContent( "paramFromAdd", null, "props.paramFromAdd", null, + null, + false, + false, String.class.getName() ), + new ParameterAnnotationContent( "paramFromSetterDeprecated", null, "props.paramFromSetterDeprecated", null, + null, + false, + false, List.class.getName() ) ); } }