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                String instantiationStrategy = mojo.instantiationStrategy().id();
508                if ( instantiationStrategy == null )
509                {
510                    /* Perhaps the original spelling is there. */
511                    instantiationStrategy = mojo.instanciationStrategy().id();
512                }
513                mojoDescriptor.setInstantiationStrategy( instantiationStrategy );
514    
515                mojoDescriptor.setAggregator( mojo.aggregator() );
516                mojoDescriptor.setDependencyResolutionRequired( mojo.requiresDependencyResolution().id() );
517                mojoDescriptor.setDependencyCollectionRequired( mojo.requiresDependencyCollection().id() );
518    
519                mojoDescriptor.setDirectInvocationOnly( mojo.requiresDirectInvocation() );
520                mojoDescriptor.setDeprecated( mojo.getDeprecated() );
521                mojoDescriptor.setThreadSafe( mojo.threadSafe() );
522    
523                ExecuteAnnotationContent execute = findExecuteInParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
524                if ( execute != null )
525                {
526                    mojoDescriptor.setExecuteGoal( execute.goal() );
527                    mojoDescriptor.setExecuteLifecycle( execute.lifecycle() );
528                    if ( execute.phase() != null )
529                    {
530                        mojoDescriptor.setExecutePhase( execute.phase().id() );
531                    }
532                }
533    
534                mojoDescriptor.setExecutionStrategy( mojo.executionStrategy() );
535                // ???
536                //mojoDescriptor.alwaysExecute(mojo.a)
537    
538                mojoDescriptor.setGoal( mojo.name() );
539                mojoDescriptor.setOnlineRequired( mojo.requiresOnline() );
540    
541                mojoDescriptor.setPhase( mojo.defaultPhase().id() );
542    
543                // Parameter annotations
544                Map<String, ParameterAnnotationContent> parameters =
545                    getParametersParentHierarchy( mojoAnnotatedClass, new HashMap<String, ParameterAnnotationContent>(),
546                                                  mojoAnnotatedClasses );
547    
548                for ( ParameterAnnotationContent parameterAnnotationContent : new TreeSet<ParameterAnnotationContent>(
549                    parameters.values() ) )
550                {
551                    org.apache.maven.plugin.descriptor.Parameter parameter =
552                        new org.apache.maven.plugin.descriptor.Parameter();
553                    parameter.setName( parameterAnnotationContent.getFieldName() );
554                    parameter.setAlias( parameterAnnotationContent.alias() );
555                    parameter.setDefaultValue( parameterAnnotationContent.defaultValue() );
556                    parameter.setDeprecated( parameterAnnotationContent.getDeprecated() );
557                    parameter.setDescription( parameterAnnotationContent.getDescription() );
558                    parameter.setEditable( !parameterAnnotationContent.readonly() );
559                    String property = parameterAnnotationContent.property();
560                    if ( StringUtils.contains( property, '$' ) || StringUtils.contains( property, '{' )
561                        || StringUtils.contains( property, '}' ) )
562                    {
563                        throw new InvalidParameterException(
564                            "Invalid property for parameter '" + parameter.getName() + "', " + "forbidden characters ${}: "
565                                + property, null );
566                    }
567                    parameter.setExpression( StringUtils.isEmpty( property ) ? "" : "${" + property + "}" );
568                    parameter.setType( parameterAnnotationContent.getClassName() );
569                    parameter.setSince( parameterAnnotationContent.getSince() );
570                    parameter.setRequired( parameterAnnotationContent.required() );
571    
572                    mojoDescriptor.addParameter( parameter );
573                }
574    
575                // Component annotations
576                Map<String, ComponentAnnotationContent> components =
577                    getComponentsParentHierarchy( mojoAnnotatedClass, new HashMap<String, ComponentAnnotationContent>(),
578                                                  mojoAnnotatedClasses );
579    
580                for ( ComponentAnnotationContent componentAnnotationContent : new TreeSet<ComponentAnnotationContent>(
581                    components.values() ) )
582                {
583                    org.apache.maven.plugin.descriptor.Parameter parameter =
584                        new org.apache.maven.plugin.descriptor.Parameter();
585                    parameter.setName( componentAnnotationContent.getFieldName() );
586    
587                    // recognize Maven-injected objects as components annotations instead of parameters
588                    String expression = PluginUtils.MAVEN_COMPONENTS.get( componentAnnotationContent.getRoleClassName() );
589                    if ( expression == null )
590                    {
591                        // normal component
592                        parameter.setRequirement( new Requirement( componentAnnotationContent.getRoleClassName(),
593                                                                   componentAnnotationContent.hint() ) );
594                    }
595                    else
596                    {
597                        // not a component but a Maven object to be transformed into an expression/property
598                        parameter.setDefaultValue( expression );
599                        parameter.setType( componentAnnotationContent.getRoleClassName() );
600                        parameter.setRequired( true );
601                    }
602                    parameter.setDeprecated( componentAnnotationContent.getDeprecated() );
603                    parameter.setSince( componentAnnotationContent.getSince() );
604    
605                    // same behaviour as JavaMojoDescriptorExtractor
606                    //parameter.setRequired( ... );
607                    parameter.setEditable( false );
608    
609                    mojoDescriptor.addParameter( parameter );
610                }
611    
612                mojoDescriptor.setPluginDescriptor( pluginDescriptor );
613    
614                mojoDescriptors.add( mojoDescriptor );
615            }
616            return mojoDescriptors;
617        }
618    
619        protected ExecuteAnnotationContent findExecuteInParentHierarchy( MojoAnnotatedClass mojoAnnotatedClass,
620                                                                         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
621        {
622    
623            if ( mojoAnnotatedClass.getExecute() != null )
624            {
625                return mojoAnnotatedClass.getExecute();
626            }
627            String parentClassName = mojoAnnotatedClass.getParentClassName();
628            if ( StringUtils.isEmpty( parentClassName ) )
629            {
630                return null;
631            }
632            MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
633            if ( parent == null )
634            {
635                return null;
636            }
637            return findExecuteInParentHierarchy( parent, mojoAnnotatedClasses );
638        }
639    
640    
641        protected Map<String, ParameterAnnotationContent> getParametersParentHierarchy(
642            MojoAnnotatedClass mojoAnnotatedClass, Map<String, ParameterAnnotationContent> parameters,
643            Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
644        {
645            List<ParameterAnnotationContent> parameterAnnotationContents = new ArrayList<ParameterAnnotationContent>();
646    
647            parameterAnnotationContents =
648                getParametersParent( mojoAnnotatedClass, parameterAnnotationContents, mojoAnnotatedClasses );
649    
650            // move to parent first to build the Map
651            Collections.reverse( parameterAnnotationContents );
652    
653            Map<String, ParameterAnnotationContent> map =
654                new HashMap<String, ParameterAnnotationContent>( parameterAnnotationContents.size() );
655    
656            for ( ParameterAnnotationContent parameterAnnotationContent : parameterAnnotationContents )
657            {
658                map.put( parameterAnnotationContent.getFieldName(), parameterAnnotationContent );
659            }
660            return map;
661        }
662    
663        protected List<ParameterAnnotationContent> getParametersParent( MojoAnnotatedClass mojoAnnotatedClass,
664                                                                        List<ParameterAnnotationContent> parameterAnnotationContents,
665                                                                        Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
666        {
667            parameterAnnotationContents.addAll( mojoAnnotatedClass.getParameters().values() );
668            String parentClassName = mojoAnnotatedClass.getParentClassName();
669            if ( parentClassName != null )
670            {
671                MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
672                if ( parent != null )
673                {
674                    return getParametersParent( parent, parameterAnnotationContents, mojoAnnotatedClasses );
675                }
676            }
677            return parameterAnnotationContents;
678        }
679    
680        protected Map<String, ComponentAnnotationContent> getComponentsParentHierarchy(
681            MojoAnnotatedClass mojoAnnotatedClass, Map<String, ComponentAnnotationContent> components,
682            Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
683        {
684            List<ComponentAnnotationContent> componentAnnotationContents = new ArrayList<ComponentAnnotationContent>();
685    
686            componentAnnotationContents =
687                getComponentParent( mojoAnnotatedClass, componentAnnotationContents, mojoAnnotatedClasses );
688    
689            // move to parent first to build the Map
690            Collections.reverse( componentAnnotationContents );
691    
692            Map<String, ComponentAnnotationContent> map =
693                new HashMap<String, ComponentAnnotationContent>( componentAnnotationContents.size() );
694    
695            for ( ComponentAnnotationContent componentAnnotationContent : componentAnnotationContents )
696            {
697                map.put( componentAnnotationContent.getFieldName(), componentAnnotationContent );
698            }
699            return map;
700        }
701    
702        protected List<ComponentAnnotationContent> getComponentParent( MojoAnnotatedClass mojoAnnotatedClass,
703                                                                       List<ComponentAnnotationContent> componentAnnotationContents,
704                                                                       Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
705        {
706            componentAnnotationContents.addAll( mojoAnnotatedClass.getComponents().values() );
707            String parentClassName = mojoAnnotatedClass.getParentClassName();
708            if ( parentClassName != null )
709            {
710                MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
711                if ( parent != null )
712                {
713                    return getComponentParent( parent, componentAnnotationContents, mojoAnnotatedClasses );
714                }
715            }
716            return componentAnnotationContents;
717        }
718    
719        protected MavenProject getFromProjectReferences( Artifact artifact, MavenProject project )
720        {
721            if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
722            {
723                return null;
724            }
725            @SuppressWarnings( "unchecked" ) Collection<MavenProject> mavenProjects =
726                project.getProjectReferences().values();
727            for ( MavenProject mavenProject : mavenProjects )
728            {
729                if ( StringUtils.equals( mavenProject.getId(), artifact.getId() ) )
730                {
731                    return mavenProject;
732                }
733            }
734            return null;
735        }
736    
737    }