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