View Javadoc
1   package org.apache.maven.tools.plugin.extractor.annotations;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Objects;
35  import java.util.Set;
36  import java.util.TreeMap;
37  import java.util.TreeSet;
38  
39  import org.apache.maven.artifact.Artifact;
40  import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
41  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
42  import org.apache.maven.plugin.descriptor.DuplicateParameterException;
43  import org.apache.maven.plugin.descriptor.InvalidParameterException;
44  import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
45  import org.apache.maven.plugin.descriptor.MojoDescriptor;
46  import org.apache.maven.plugin.descriptor.PluginDescriptor;
47  import org.apache.maven.plugin.descriptor.Requirement;
48  import org.apache.maven.project.MavenProject;
49  import org.apache.maven.repository.RepositorySystem;
50  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
51  import org.apache.maven.tools.plugin.PluginToolsRequest;
52  import org.apache.maven.tools.plugin.extractor.ExtractionException;
53  import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
54  import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
55  import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
56  import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
57  import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ParameterAnnotationContent;
58  import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotatedClass;
59  import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner;
60  import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScannerRequest;
61  import org.apache.maven.tools.plugin.util.PluginUtils;
62  import org.codehaus.plexus.archiver.ArchiverException;
63  import org.codehaus.plexus.archiver.UnArchiver;
64  import org.codehaus.plexus.archiver.manager.ArchiverManager;
65  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
66  import org.codehaus.plexus.component.annotations.Component;
67  import org.codehaus.plexus.logging.AbstractLogEnabled;
68  import org.codehaus.plexus.util.StringUtils;
69  
70  import com.thoughtworks.qdox.JavaProjectBuilder;
71  import com.thoughtworks.qdox.library.SortedClassLibraryBuilder;
72  import com.thoughtworks.qdox.model.DocletTag;
73  import com.thoughtworks.qdox.model.JavaClass;
74  import com.thoughtworks.qdox.model.JavaField;
75  
76  /**
77   * JavaMojoDescriptorExtractor, a MojoDescriptor extractor to read descriptors from java classes with annotations.
78   * Notice that source files are also parsed to get description, since and deprecation information.
79   *
80   * @author Olivier Lamy
81   * @since 3.0
82   */
83  @Component( role = MojoDescriptorExtractor.class, hint = "java-annotations" )
84  public class JavaAnnotationsMojoDescriptorExtractor
85      extends AbstractLogEnabled
86      implements MojoDescriptorExtractor
87  {
88  
89      @org.codehaus.plexus.component.annotations.Requirement
90      private MojoAnnotationsScanner mojoAnnotationsScanner;
91  
92      @org.codehaus.plexus.component.annotations.Requirement
93      private RepositorySystem repositorySystem;
94  
95      @org.codehaus.plexus.component.annotations.Requirement
96      private ArchiverManager archiverManager;
97  
98      @Override
99      public List<MojoDescriptor> execute( PluginToolsRequest request )
100         throws ExtractionException, InvalidPluginDescriptorException
101     {
102         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = scanAnnotations( request );
103 
104         Map<String, JavaClass> javaClassesMap = scanJavadoc( request, mojoAnnotatedClasses.values() );
105 
106         populateDataFromJavadoc( mojoAnnotatedClasses, javaClassesMap );
107 
108         return toMojoDescriptors( mojoAnnotatedClasses, request.getPluginDescriptor() );
109     }
110 
111     private Map<String, MojoAnnotatedClass> scanAnnotations( PluginToolsRequest request )
112         throws ExtractionException
113     {
114         MojoAnnotationsScannerRequest mojoAnnotationsScannerRequest = new MojoAnnotationsScannerRequest();
115 
116         File output = new File( request.getProject().getBuild().getOutputDirectory() );
117         mojoAnnotationsScannerRequest.setClassesDirectories( Arrays.asList( output ) );
118 
119         mojoAnnotationsScannerRequest.setDependencies( request.getDependencies() );
120 
121         mojoAnnotationsScannerRequest.setProject( request.getProject() );
122 
123         return mojoAnnotationsScanner.scan( mojoAnnotationsScannerRequest );
124     }
125 
126     private Map<String, JavaClass> scanJavadoc( PluginToolsRequest request,
127                                                 Collection<MojoAnnotatedClass> mojoAnnotatedClasses )
128         throws ExtractionException
129     {
130         // found artifact from reactors to scan sources
131         // we currently only scan sources from reactors
132         List<MavenProject> mavenProjects = new ArrayList<>();
133 
134         // if we need to scan sources from external artifacts
135         Set<Artifact> externalArtifacts = new HashSet<>();
136 
137         for ( MojoAnnotatedClass mojoAnnotatedClass : mojoAnnotatedClasses )
138         {
139             if ( Objects.equals( mojoAnnotatedClass.getArtifact().getArtifactId(),
140                                      request.getProject().getArtifact().getArtifactId() ) )
141             {
142                 continue;
143             }
144 
145             if ( !isMojoAnnnotatedClassCandidate( mojoAnnotatedClass ) )
146             {
147                 // we don't scan sources for classes without mojo annotations
148                 continue;
149             }
150 
151             MavenProject mavenProject =
152                 getFromProjectReferences( mojoAnnotatedClass.getArtifact(), request.getProject() );
153 
154             if ( mavenProject != null )
155             {
156                 mavenProjects.add( mavenProject );
157             }
158             else
159             {
160                 externalArtifacts.add( mojoAnnotatedClass.getArtifact() );
161             }
162         }
163 
164         Map<String, JavaClass> javaClassesMap = new HashMap<String, JavaClass>();
165 
166         // try to get artifact with sources classifier, extract somewhere then scan for @since, @deprecated
167         for ( Artifact artifact : externalArtifacts )
168         {
169             // parameter for test-sources too ?? olamy I need that for it test only
170             if ( StringUtils.equalsIgnoreCase( "tests", artifact.getClassifier() ) )
171             {
172                 javaClassesMap.putAll( discoverClassesFromSourcesJar( artifact, request, "test-sources" ) );
173             }
174             else
175             {
176                 javaClassesMap.putAll( discoverClassesFromSourcesJar( artifact, request, "sources" ) );
177             }
178 
179         }
180 
181         for ( MavenProject mavenProject : mavenProjects )
182         {
183             javaClassesMap.putAll( discoverClasses( request.getEncoding(), mavenProject ) );
184         }
185 
186         javaClassesMap.putAll( discoverClasses( request ) );
187 
188         return javaClassesMap;
189     }
190 
191     private boolean isMojoAnnnotatedClassCandidate( MojoAnnotatedClass mojoAnnotatedClass )
192     {
193         return mojoAnnotatedClass != null && mojoAnnotatedClass.hasAnnotations();
194     }
195 
196     protected Map<String, JavaClass> discoverClassesFromSourcesJar( Artifact artifact, PluginToolsRequest request,
197                                                                     String classifier )
198         throws ExtractionException
199     {
200         try
201         {
202             Artifact sourcesArtifact =
203                 repositorySystem.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(),
204                                                                artifact.getVersion(), artifact.getType(), classifier );
205 
206             ArtifactResolutionRequest req = new ArtifactResolutionRequest();
207             req.setArtifact( sourcesArtifact );
208             req.setLocalRepository( request.getLocal() );
209             req.setRemoteRepositories( request.getRemoteRepos() );
210             ArtifactResolutionResult res = repositorySystem.resolve( req );
211             if ( res.hasMissingArtifacts() || res.hasExceptions() )
212             {
213                 getLogger().warn(
214                     "Unable to get sources artifact for " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
215                     + artifact.getVersion() + ". Some javadoc tags (@since, @deprecated and comments) won't be used" );
216                 return Collections.emptyMap();
217             }
218 
219             if ( sourcesArtifact.getFile() == null || !sourcesArtifact.getFile().exists() )
220             {
221                 // could not get artifact sources
222                 return Collections.emptyMap();
223             }
224 
225             // extract sources to target/maven-plugin-plugin-sources/${groupId}/${artifact}/sources
226             File extractDirectory = new File( request.getProject().getBuild().getDirectory(),
227                                               "maven-plugin-plugin-sources/" + sourcesArtifact.getGroupId() + "/"
228                                                   + sourcesArtifact.getArtifactId() + "/" + sourcesArtifact.getVersion()
229                                                   + "/" + sourcesArtifact.getClassifier() );
230             extractDirectory.mkdirs();
231 
232             UnArchiver unArchiver = archiverManager.getUnArchiver( "jar" );
233             unArchiver.setSourceFile( sourcesArtifact.getFile() );
234             unArchiver.setDestDirectory( extractDirectory );
235             unArchiver.extract();
236 
237             return discoverClasses( request.getEncoding(), Arrays.asList( extractDirectory ),
238                                     request.getDependencies() );
239         }
240         catch ( ArchiverException | NoSuchArchiverException e )
241         {
242             throw new ExtractionException( e.getMessage(), e );
243         }
244     }
245 
246     /**
247      * from sources scan to get @since and @deprecated and description of classes and fields.
248      */
249     protected void populateDataFromJavadoc( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses,
250                                             Map<String, JavaClass> javaClassesMap )
251     {
252 
253         for ( Map.Entry<String, MojoAnnotatedClass> entry : mojoAnnotatedClasses.entrySet() )
254         {
255             JavaClass javaClass = javaClassesMap.get( entry.getKey() );
256             if ( javaClass == null )
257             {
258                 continue;
259             }
260 
261             // populate class-level content
262             MojoAnnotationContent mojoAnnotationContent = entry.getValue().getMojo();
263             if ( mojoAnnotationContent != null )
264             {
265                 mojoAnnotationContent.setDescription( javaClass.getComment() );
266 
267                 DocletTag since = findInClassHierarchy( javaClass, "since" );
268                 if ( since != null )
269                 {
270                     mojoAnnotationContent.setSince( since.getValue() );
271                 }
272 
273                 DocletTag deprecated = findInClassHierarchy( javaClass, "deprecated" );
274                 if ( deprecated != null )
275                 {
276                     mojoAnnotationContent.setDeprecated( deprecated.getValue() );
277                 }
278             }
279 
280             Map<String, JavaField> fieldsMap = extractFieldParameterTags( javaClass, javaClassesMap );
281 
282             // populate parameters
283             Map<String, ParameterAnnotationContent> parameters =
284                     getParametersParentHierarchy( entry.getValue(), mojoAnnotatedClasses );
285             parameters = new TreeMap<>( parameters );
286             for ( Map.Entry<String, ParameterAnnotationContent> parameter : parameters.entrySet() )
287             {
288                 JavaField javaField = fieldsMap.get( parameter.getKey() );
289                 if ( javaField == null )
290                 {
291                     continue;
292                 }
293 
294                 ParameterAnnotationContent parameterAnnotationContent = parameter.getValue();
295                 parameterAnnotationContent.setDescription( javaField.getComment() );
296 
297                 DocletTag deprecated = javaField.getTagByName( "deprecated" );
298                 if ( deprecated != null )
299                 {
300                     parameterAnnotationContent.setDeprecated( deprecated.getValue() );
301                 }
302 
303                 DocletTag since = javaField.getTagByName( "since" );
304                 if ( since != null )
305                 {
306                     parameterAnnotationContent.setSince( since.getValue() );
307                 }
308             }
309 
310             // populate components
311             Map<String, ComponentAnnotationContent> components = entry.getValue().getComponents();
312             for ( Map.Entry<String, ComponentAnnotationContent> component : components.entrySet() )
313             {
314                 JavaField javaField = fieldsMap.get( component.getKey() );
315                 if ( javaField == null )
316                 {
317                     continue;
318                 }
319 
320                 ComponentAnnotationContent componentAnnotationContent = component.getValue();
321                 componentAnnotationContent.setDescription( javaField.getComment() );
322 
323                 DocletTag deprecated = javaField.getTagByName( "deprecated" );
324                 if ( deprecated != null )
325                 {
326                     componentAnnotationContent.setDeprecated( deprecated.getValue() );
327                 }
328 
329                 DocletTag since = javaField.getTagByName( "since" );
330                 if ( since != null )
331                 {
332                     componentAnnotationContent.setSince( since.getValue() );
333                 }
334             }
335 
336         }
337 
338     }
339 
340     /**
341      * @param javaClass not null
342      * @param tagName   not null
343      * @return docletTag instance
344      */
345     private DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
346     {
347         try
348         {
349             DocletTag tag = javaClass.getTagByName( tagName );
350 
351             if ( tag == null )
352             {
353                 JavaClass superClass = javaClass.getSuperJavaClass();
354 
355                 if ( superClass != null )
356                 {
357                     tag = findInClassHierarchy( superClass, tagName );
358                 }
359             }
360 
361             return tag;
362         }
363         catch ( NoClassDefFoundError e )
364         {
365             getLogger().warn( "Failed extracting tag '" + tagName + "' from class " + javaClass );
366             throw e;
367         }
368     }
369 
370     /**
371      * extract fields that are either parameters or components.
372      *
373      * @param javaClass not null
374      * @return map with Mojo parameters names as keys
375      */
376     private Map<String, JavaField> extractFieldParameterTags( JavaClass javaClass,
377                                                               Map<String, JavaClass> javaClassesMap )
378     {
379         try
380         {
381             Map<String, JavaField> rawParams = new TreeMap<>();
382 
383             // we have to add the parent fields first, so that they will be overwritten by the local fields if
384             // that actually happens...
385             JavaClass superClass = javaClass.getSuperJavaClass();
386 
387             if ( superClass != null )
388             {
389                 if ( superClass.getFields().size() > 0 )
390                 {
391                     rawParams = extractFieldParameterTags( superClass, javaClassesMap );
392                 }
393                 // maybe sources comes from scan of sources artifact
394                 superClass = javaClassesMap.get( superClass.getFullyQualifiedName() );
395                 if ( superClass != null )
396                 {
397                     rawParams = extractFieldParameterTags( superClass, javaClassesMap );
398                 }
399             }
400             else
401             {
402 
403                 rawParams = new TreeMap<>();
404             }
405 
406             for ( JavaField field : javaClass.getFields() )
407             {
408                 rawParams.put( field.getName(), field );
409             }
410 
411             return rawParams;
412         }
413         catch ( NoClassDefFoundError e )
414         {
415             getLogger().warn( "Failed extracting parameters from " + javaClass );
416             throw e;
417         }
418     }
419 
420     protected Map<String, JavaClass> discoverClasses( final PluginToolsRequest request )
421     {
422         return discoverClasses( request.getEncoding(), request.getProject() );
423     }
424 
425     protected Map<String, JavaClass> discoverClasses( final String encoding, final MavenProject project )
426     {
427         List<File> sources = new ArrayList<>();
428 
429         for ( String source : project.getCompileSourceRoots() )
430         {
431             sources.add( new File( source ) );
432         }
433 
434         // TODO be more dynamic
435         File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
436         if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() )
437             && generatedPlugin.exists() )
438         {
439             sources.add( generatedPlugin );
440         }
441 
442         return discoverClasses( encoding, sources,  project.getArtifacts() );
443     }
444 
445     protected Map<String, JavaClass> discoverClasses( final String encoding, List<File> sourceDirectories,
446                                                       Set<Artifact> artifacts )
447     {
448         JavaProjectBuilder builder = new JavaProjectBuilder( new SortedClassLibraryBuilder() );
449         builder.setEncoding( encoding );
450 
451         // Build isolated Classloader with only the artifacts of the project (none of this plugin) 
452         List<URL> urls = new ArrayList<>( artifacts.size() );
453         for ( Artifact artifact : artifacts )
454         {
455             try
456             {
457                 urls.add( artifact.getFile().toURI().toURL() );
458             }
459             catch ( MalformedURLException e )
460             {
461                 // noop
462             }
463         }
464         builder.addClassLoader( new URLClassLoader( urls.toArray( new URL[0] ), ClassLoader.getSystemClassLoader() ) );
465 
466         for ( File source : sourceDirectories )
467         {
468             builder.addSourceTree( source );
469         }
470 
471         Collection<JavaClass> javaClasses = builder.getClasses();
472 
473         if ( javaClasses == null || javaClasses.size() < 1 )
474         {
475             return Collections.emptyMap();
476         }
477 
478         Map<String, JavaClass> javaClassMap = new HashMap<>( javaClasses.size() );
479 
480         for ( JavaClass javaClass : javaClasses )
481         {
482             javaClassMap.put( javaClass.getFullyQualifiedName(), javaClass );
483         }
484 
485         return javaClassMap;
486     }
487 
488     private List<MojoDescriptor> toMojoDescriptors( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses,
489                                                     PluginDescriptor pluginDescriptor )
490         throws DuplicateParameterException, InvalidParameterException
491     {
492         List<MojoDescriptor> mojoDescriptors = new ArrayList<>( mojoAnnotatedClasses.size() );
493         for ( MojoAnnotatedClass mojoAnnotatedClass : mojoAnnotatedClasses.values() )
494         {
495             // no mojo so skip it
496             if ( mojoAnnotatedClass.getMojo() == null )
497             {
498                 continue;
499             }
500 
501             ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor();
502 
503             //mojoDescriptor.setRole( mojoAnnotatedClass.getClassName() );
504             //mojoDescriptor.setRoleHint( "default" );
505             mojoDescriptor.setImplementation( mojoAnnotatedClass.getClassName() );
506             mojoDescriptor.setLanguage( "java" );
507 
508             MojoAnnotationContent mojo = mojoAnnotatedClass.getMojo();
509 
510             mojoDescriptor.setDescription( mojo.getDescription() );
511             mojoDescriptor.setSince( mojo.getSince() );
512             mojo.setDeprecated( mojo.getDeprecated() );
513 
514             mojoDescriptor.setProjectRequired( mojo.requiresProject() );
515 
516             mojoDescriptor.setRequiresReports( mojo.requiresReports() );
517 
518             mojoDescriptor.setComponentConfigurator( mojo.configurator() );
519 
520             mojoDescriptor.setInheritedByDefault( mojo.inheritByDefault() );
521 
522             mojoDescriptor.setInstantiationStrategy( mojo.instantiationStrategy().id() );
523 
524             mojoDescriptor.setAggregator( mojo.aggregator() );
525             mojoDescriptor.setDependencyResolutionRequired( mojo.requiresDependencyResolution().id() );
526             mojoDescriptor.setDependencyCollectionRequired( mojo.requiresDependencyCollection().id() );
527 
528             mojoDescriptor.setDirectInvocationOnly( mojo.requiresDirectInvocation() );
529             mojoDescriptor.setDeprecated( mojo.getDeprecated() );
530             mojoDescriptor.setThreadSafe( mojo.threadSafe() );
531 
532             ExecuteAnnotationContent execute = findExecuteInParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
533             if ( execute != null )
534             {
535                 mojoDescriptor.setExecuteGoal( execute.goal() );
536                 mojoDescriptor.setExecuteLifecycle( execute.lifecycle() );
537                 if ( execute.phase() != null )
538                 {
539                     mojoDescriptor.setExecutePhase( execute.phase().id() );
540                 }
541             }
542 
543             mojoDescriptor.setExecutionStrategy( mojo.executionStrategy() );
544             // ???
545             //mojoDescriptor.alwaysExecute(mojo.a)
546 
547             mojoDescriptor.setGoal( mojo.name() );
548             mojoDescriptor.setOnlineRequired( mojo.requiresOnline() );
549 
550             mojoDescriptor.setPhase( mojo.defaultPhase().id() );
551 
552             // Parameter annotations
553             Map<String, ParameterAnnotationContent> parameters =
554                     getParametersParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
555 
556             for ( ParameterAnnotationContent parameterAnnotationContent : new TreeSet<>( parameters.values() ) )
557             {
558                 org.apache.maven.plugin.descriptor.Parameter parameter =
559                     new org.apache.maven.plugin.descriptor.Parameter();
560                 String name =
561                     StringUtils.isEmpty( parameterAnnotationContent.name() ) ? parameterAnnotationContent.getFieldName()
562                                     : parameterAnnotationContent.name();
563                 parameter.setName( name );
564                 parameter.setAlias( parameterAnnotationContent.alias() );
565                 parameter.setDefaultValue( parameterAnnotationContent.defaultValue() );
566                 parameter.setDeprecated( parameterAnnotationContent.getDeprecated() );
567                 parameter.setDescription( parameterAnnotationContent.getDescription() );
568                 parameter.setEditable( !parameterAnnotationContent.readonly() );
569                 String property = parameterAnnotationContent.property();
570                 if ( StringUtils.contains( property, '$' ) || StringUtils.contains( property, '{' )
571                     || StringUtils.contains( property, '}' ) )
572                 {
573                     throw new InvalidParameterException(
574                         "Invalid property for parameter '" + parameter.getName() + "', " + "forbidden characters ${}: "
575                             + property, null );
576                 }
577                 parameter.setExpression( StringUtils.isEmpty( property ) ? "" : "${" + property + "}" );
578                 parameter.setType( parameterAnnotationContent.getClassName() );
579                 parameter.setSince( parameterAnnotationContent.getSince() );
580                 parameter.setRequired( parameterAnnotationContent.required() );
581 
582                 mojoDescriptor.addParameter( parameter );
583             }
584 
585             // Component annotations
586             Map<String, ComponentAnnotationContent> components =
587                     getComponentsParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
588 
589             for ( ComponentAnnotationContent componentAnnotationContent : new TreeSet<>( components.values() ) )
590             {
591                 org.apache.maven.plugin.descriptor.Parameter parameter =
592                     new org.apache.maven.plugin.descriptor.Parameter();
593                 parameter.setName( componentAnnotationContent.getFieldName() );
594 
595                 // recognize Maven-injected objects as components annotations instead of parameters
596                 String expression = PluginUtils.MAVEN_COMPONENTS.get( componentAnnotationContent.getRoleClassName() );
597                 if ( expression == null )
598                 {
599                     // normal component
600                     parameter.setRequirement( new Requirement( componentAnnotationContent.getRoleClassName(),
601                                                                componentAnnotationContent.hint() ) );
602                 }
603                 else
604                 {
605                     // not a component but a Maven object to be transformed into an expression/property: deprecated
606                     getLogger().warn( "Deprecated @Component annotation for '" + parameter.getName() + "' field in "
607                                           + mojoAnnotatedClass.getClassName()
608                                           + ": replace with @Parameter( defaultValue = \"" + expression
609                                           + "\", readonly = true )" );
610                     parameter.setDefaultValue( expression );
611                     parameter.setType( componentAnnotationContent.getRoleClassName() );
612                     parameter.setRequired( true );
613                 }
614                 parameter.setDeprecated( componentAnnotationContent.getDeprecated() );
615                 parameter.setSince( componentAnnotationContent.getSince() );
616 
617                 // same behaviour as JavaMojoDescriptorExtractor
618                 //parameter.setRequired( ... );
619                 parameter.setEditable( false );
620 
621                 mojoDescriptor.addParameter( parameter );
622             }
623 
624             mojoDescriptor.setPluginDescriptor( pluginDescriptor );
625 
626             mojoDescriptors.add( mojoDescriptor );
627         }
628         return mojoDescriptors;
629     }
630 
631     protected ExecuteAnnotationContent findExecuteInParentHierarchy( MojoAnnotatedClass mojoAnnotatedClass,
632                                                                  Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
633     {
634         if ( mojoAnnotatedClass.getExecute() != null )
635         {
636             return mojoAnnotatedClass.getExecute();
637         }
638         String parentClassName = mojoAnnotatedClass.getParentClassName();
639         if ( StringUtils.isEmpty( parentClassName ) )
640         {
641             return null;
642         }
643         MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
644         if ( parent == null )
645         {
646             return null;
647         }
648         return findExecuteInParentHierarchy( parent, mojoAnnotatedClasses );
649     }
650 
651 
652     protected Map<String, ParameterAnnotationContent> getParametersParentHierarchy(
653             MojoAnnotatedClass mojoAnnotatedClass,
654             Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
655     {
656         List<ParameterAnnotationContent> parameterAnnotationContents = new ArrayList<>();
657 
658         parameterAnnotationContents =
659             getParametersParent( mojoAnnotatedClass, parameterAnnotationContents, mojoAnnotatedClasses );
660 
661         // move to parent first to build the Map
662         Collections.reverse( parameterAnnotationContents );
663 
664         Map<String, ParameterAnnotationContent> map = new HashMap<>( parameterAnnotationContents.size() );
665 
666         for ( ParameterAnnotationContent parameterAnnotationContent : parameterAnnotationContents )
667         {
668             map.put( parameterAnnotationContent.getFieldName(), parameterAnnotationContent );
669         }
670         return map;
671     }
672 
673     protected List<ParameterAnnotationContent> getParametersParent( MojoAnnotatedClass mojoAnnotatedClass,
674                                                         List<ParameterAnnotationContent> parameterAnnotationContents,
675                                                         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
676     {
677         parameterAnnotationContents.addAll( mojoAnnotatedClass.getParameters().values() );
678         String parentClassName = mojoAnnotatedClass.getParentClassName();
679         if ( parentClassName != null )
680         {
681             MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
682             if ( parent != null )
683             {
684                 return getParametersParent( parent, parameterAnnotationContents, mojoAnnotatedClasses );
685             }
686         }
687         return parameterAnnotationContents;
688     }
689 
690     protected Map<String, ComponentAnnotationContent> getComponentsParentHierarchy(
691             MojoAnnotatedClass mojoAnnotatedClass,
692             Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
693     {
694         List<ComponentAnnotationContent> componentAnnotationContents = new ArrayList<>();
695 
696         componentAnnotationContents =
697             getComponentParent( mojoAnnotatedClass, componentAnnotationContents, mojoAnnotatedClasses );
698 
699         // move to parent first to build the Map
700         Collections.reverse( componentAnnotationContents );
701 
702         Map<String, ComponentAnnotationContent> map = new HashMap<>( componentAnnotationContents.size() );
703 
704         for ( ComponentAnnotationContent componentAnnotationContent : componentAnnotationContents )
705         {
706             map.put( componentAnnotationContent.getFieldName(), componentAnnotationContent );
707         }
708         return map;
709     }
710 
711     protected List<ComponentAnnotationContent> getComponentParent( MojoAnnotatedClass mojoAnnotatedClass,
712                                                        List<ComponentAnnotationContent> componentAnnotationContents,
713                                                        Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
714     {
715         componentAnnotationContents.addAll( mojoAnnotatedClass.getComponents().values() );
716         String parentClassName = mojoAnnotatedClass.getParentClassName();
717         if ( parentClassName != null )
718         {
719             MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
720             if ( parent != null )
721             {
722                 return getComponentParent( parent, componentAnnotationContents, mojoAnnotatedClasses );
723             }
724         }
725         return componentAnnotationContents;
726     }
727 
728     protected MavenProject getFromProjectReferences( Artifact artifact, MavenProject project )
729     {
730         if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
731         {
732             return null;
733         }
734         Collection<MavenProject> mavenProjects = project.getProjectReferences().values();
735         for ( MavenProject mavenProject : mavenProjects )
736         {
737             if ( Objects.equals( mavenProject.getId(), artifact.getId() ) )
738             {
739                 return mavenProject;
740             }
741         }
742         return null;
743     }
744 
745 }