001package 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
022import com.thoughtworks.qdox.JavaDocBuilder;
023import com.thoughtworks.qdox.model.DocletTag;
024import com.thoughtworks.qdox.model.JavaClass;
025import com.thoughtworks.qdox.model.JavaField;
026import org.apache.maven.artifact.Artifact;
027import org.apache.maven.artifact.factory.ArtifactFactory;
028import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
029import org.apache.maven.artifact.resolver.ArtifactResolutionException;
030import org.apache.maven.artifact.resolver.ArtifactResolver;
031import org.apache.maven.plugin.descriptor.DuplicateParameterException;
032import org.apache.maven.plugin.descriptor.InvalidParameterException;
033import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
034import org.apache.maven.plugin.descriptor.MojoDescriptor;
035import org.apache.maven.plugin.descriptor.PluginDescriptor;
036import org.apache.maven.plugin.descriptor.Requirement;
037import org.apache.maven.project.MavenProject;
038import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
039import org.apache.maven.tools.plugin.PluginToolsRequest;
040import org.apache.maven.tools.plugin.annotations.datamodel.ComponentAnnotationContent;
041import org.apache.maven.tools.plugin.annotations.datamodel.ExecuteAnnotationContent;
042import org.apache.maven.tools.plugin.annotations.datamodel.MojoAnnotationContent;
043import org.apache.maven.tools.plugin.annotations.datamodel.ParameterAnnotationContent;
044import org.apache.maven.tools.plugin.annotations.scanner.MojoAnnotatedClass;
045import org.apache.maven.tools.plugin.annotations.scanner.MojoAnnotationsScanner;
046import org.apache.maven.tools.plugin.annotations.scanner.MojoAnnotationsScannerRequest;
047import org.apache.maven.tools.plugin.extractor.ExtractionException;
048import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
049import org.apache.maven.tools.plugin.util.PluginUtils;
050import org.codehaus.plexus.archiver.UnArchiver;
051import org.codehaus.plexus.archiver.manager.ArchiverManager;
052import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
053import org.codehaus.plexus.component.annotations.Component;
054import org.codehaus.plexus.logging.AbstractLogEnabled;
055import org.codehaus.plexus.util.StringUtils;
056
057import java.io.File;
058import java.util.ArrayList;
059import java.util.Arrays;
060import java.util.Collection;
061import java.util.Collections;
062import java.util.HashMap;
063import java.util.HashSet;
064import java.util.List;
065import java.util.Map;
066import java.util.Set;
067import java.util.TreeMap;
068import java.util.TreeSet;
069
070/**
071 * JavaMojoDescriptorExtractor, a MojoDescriptor extractor to read descriptors from java classes with annotations.
072 * Notice that source files are also parsed to get description, since and deprecation information.
073 *
074 * @author Olivier Lamy
075 * @since 3.0
076 */
077@Component( role = MojoDescriptorExtractor.class, hint = "java-annotations" )
078public 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( PluginToolsRequest request )
096        throws ExtractionException, InvalidPluginDescriptorException
097    {
098        Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = scanAnnotations( request );
099
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}