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