001    package org.apache.maven.tools.plugin.extractor.java;
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 com.thoughtworks.qdox.model.Type;
027    
028    import org.apache.maven.plugin.descriptor.InvalidParameterException;
029    import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
030    import org.apache.maven.plugin.descriptor.MojoDescriptor;
031    import org.apache.maven.plugin.descriptor.Parameter;
032    import org.apache.maven.plugin.descriptor.PluginDescriptor;
033    import org.apache.maven.plugin.descriptor.Requirement;
034    import org.apache.maven.project.MavenProject;
035    import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
036    import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
037    import org.apache.maven.tools.plugin.PluginToolsRequest;
038    import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
039    import org.apache.maven.tools.plugin.extractor.ExtractionException;
040    import org.apache.maven.tools.plugin.util.PluginUtils;
041    
042    import org.codehaus.plexus.component.annotations.Component;
043    import org.codehaus.plexus.logging.AbstractLogEnabled;
044    import org.codehaus.plexus.util.StringUtils;
045    
046    import java.io.File;
047    import java.util.ArrayList;
048    import java.util.List;
049    import java.util.Map;
050    import java.util.TreeMap;
051    
052    /**
053     * Extracts Mojo descriptors from <a href="http://java.sun.com/">Java</a> sources.
054     * <br/>
055     * For more information about the usage tag, have a look to:
056     * <a href="http://maven.apache.org/developers/mojo-api-specification.html">
057     * http://maven.apache.org/developers/mojo-api-specification.html</a>
058     *
059     * @todo need to add validation directives so that systems embedding maven2 can
060     * get validation directives to help users in IDEs.
061     * @version $Id: JavaMojoDescriptorExtractor.java 1343166 2012-05-28 08:54:25Z hboutemy $
062     * @see org.apache.maven.plugin.descriptor.MojoDescriptor
063     */
064    @Component( role = MojoDescriptorExtractor.class, hint = "java" )
065    public class JavaMojoDescriptorExtractor
066        extends AbstractLogEnabled
067        implements MojoDescriptorExtractor, JavaMojoAnnotation
068    {
069        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#INSTANTIATION_STRATEGY} instead of. */
070        public static final String MAVEN_PLUGIN_INSTANTIATION = JavaMojoAnnotation.INSTANTIATION_STRATEGY;
071    
072        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#CONFIGURATOR} instead of. */
073        public static final String CONFIGURATOR = JavaMojoAnnotation.CONFIGURATOR;
074    
075        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER} instead of. */
076        public static final String PARAMETER = JavaMojoAnnotation.PARAMETER;
077    
078        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_EXPRESSION} instead of. */
079        public static final String PARAMETER_EXPRESSION = JavaMojoAnnotation.PARAMETER_EXPRESSION;
080    
081        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_DEFAULT_VALUE} instead of. */
082        public static final String PARAMETER_DEFAULT_VALUE = JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE;
083    
084        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_ALIAS} instead of. */
085        public static final String PARAMETER_ALIAS = JavaMojoAnnotation.PARAMETER_ALIAS;
086    
087        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#SINCE} instead of. */
088        public static final String SINCE = JavaMojoAnnotation.SINCE;
089    
090        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_IMPLEMENTATION} instead of. */
091        public static final String PARAMETER_IMPLEMENTATION = JavaMojoAnnotation.PARAMETER_IMPLEMENTATION;
092    
093        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRED} instead of. */
094        public static final String REQUIRED = JavaMojoAnnotation.REQUIRED;
095    
096        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#DEPRECATED} instead of. */
097        public static final String DEPRECATED = JavaMojoAnnotation.DEPRECATED;
098    
099        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#READONLY} instead of. */
100        public static final String READONLY = JavaMojoAnnotation.READONLY;
101    
102        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#GOAL} instead of. */
103        public static final String GOAL = JavaMojoAnnotation.GOAL;
104    
105        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PHASE} instead of. */
106        public static final String PHASE = JavaMojoAnnotation.PHASE;
107    
108        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#EXECUTE} instead of. */
109        public static final String EXECUTE = JavaMojoAnnotation.EXECUTE;
110    
111        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#EXECUTE_LIFECYCLE} instead of. */
112        public static final String EXECUTE_LIFECYCLE = JavaMojoAnnotation.EXECUTE_LIFECYCLE;
113    
114        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#EXECUTE_PHASE} instead of. */
115        public static final String EXECUTE_PHASE = JavaMojoAnnotation.EXECUTE_PHASE;
116    
117        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#EXECUTE_GOAL} instead of. */
118        public static final String EXECUTE_GOAL = JavaMojoAnnotation.EXECUTE_GOAL;
119    
120        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#DESCRIPTION} instead of. */
121        public static final String GOAL_DESCRIPTION = JavaMojoAnnotation.DESCRIPTION;
122    
123        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRES_DEPENDENCY_RESOLUTION} instead of. */
124        public static final String GOAL_REQUIRES_DEPENDENCY_RESOLUTION = JavaMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION;
125    
126        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRES_PROJECT} instead of. */
127        public static final String GOAL_REQUIRES_PROJECT = JavaMojoAnnotation.REQUIRES_PROJECT;
128    
129        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRES_REPORTS} instead of. */
130        public static final String GOAL_REQUIRES_REPORTS = JavaMojoAnnotation.REQUIRES_REPORTS;
131    
132        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#AGGREGATOR} instead of. */
133        public static final String GOAL_IS_AGGREGATOR = JavaMojoAnnotation.AGGREGATOR;
134    
135        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRES_ONLINE} instead of. */
136        public static final String GOAL_REQUIRES_ONLINE = JavaMojoAnnotation.REQUIRES_ONLINE;
137    
138        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#INHERIT_BY_DEFAULT} instead of. */
139        public static final String GOAL_INHERIT_BY_DEFAULT = JavaMojoAnnotation.INHERIT_BY_DEFAULT;
140    
141        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#MULTI_EXECUTION_STRATEGY} instead of. */
142        public static final String GOAL_MULTI_EXECUTION_STRATEGY = JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY;
143    
144        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRES_DIRECT_INVOCATION} instead of. */
145        public static final String GOAL_REQUIRES_DIRECT_INVOCATION = JavaMojoAnnotation.REQUIRES_DIRECT_INVOCATION;
146    
147        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#COMPONENT} instead of. */
148        public static final String COMPONENT = JavaMojoAnnotation.COMPONENT;
149    
150        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#COMPONENT_ROLE} instead of. */
151        public static final String COMPONENT_ROLE = JavaMojoAnnotation.COMPONENT_ROLE;
152    
153        /** @deprecated since 2.4, use {@link JavaMojoAnnotation#COMPONENT_ROLEHINT} instead of. */
154        public static final String COMPONENT_ROLEHINT = JavaMojoAnnotation.COMPONENT_ROLEHINT;
155    
156        /**
157         * @param parameter not null
158         * @param i positive number
159         * @throws InvalidParameterException if any
160         */
161        protected void validateParameter( Parameter parameter, int i )
162            throws InvalidParameterException
163        {
164            // TODO: remove when backward compatibility is no longer an issue.
165            String name = parameter.getName();
166    
167            if ( name == null )
168            {
169                throw new InvalidParameterException( "name", i );
170            }
171    
172            // TODO: remove when backward compatibility is no longer an issue.
173            String type = parameter.getType();
174    
175            if ( type == null )
176            {
177                throw new InvalidParameterException( "type", i );
178            }
179    
180            // TODO: remove when backward compatibility is no longer an issue.
181            String description = parameter.getDescription();
182    
183            if ( description == null )
184            {
185                throw new InvalidParameterException( "description", i );
186            }
187        }
188    
189        // ----------------------------------------------------------------------
190        // Mojo descriptor creation from @tags
191        // ----------------------------------------------------------------------
192    
193        /**
194         * @param javaClass not null
195         * @return a mojo descriptor
196         * @throws InvalidPluginDescriptorException if any
197         */
198        protected MojoDescriptor createMojoDescriptor( JavaClass javaClass )
199            throws InvalidPluginDescriptorException
200        {
201            ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor();
202            mojoDescriptor.setLanguage( "java" );
203            mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
204            mojoDescriptor.setDescription( javaClass.getComment() );
205    
206            // ----------------------------------------------------------------------
207            // Mojo annotations in alphabetical order
208            // ----------------------------------------------------------------------
209    
210            // Aggregator flag
211            DocletTag aggregator = findInClassHierarchy( javaClass, JavaMojoAnnotation.AGGREGATOR );
212            if ( aggregator != null )
213            {
214                mojoDescriptor.setAggregator( true );
215            }
216    
217            // Configurator hint
218            DocletTag configurator = findInClassHierarchy( javaClass, JavaMojoAnnotation.CONFIGURATOR );
219            if ( configurator != null )
220            {
221                mojoDescriptor.setComponentConfigurator( configurator.getValue() );
222            }
223    
224            // Additional phase to execute first
225            DocletTag execute = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTE );
226            if ( execute != null )
227            {
228                String executePhase = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_PHASE );
229                String executeGoal = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_GOAL );
230    
231                if ( executePhase == null && executeGoal == null )
232                {
233                    throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
234                        + ": @execute tag requires either a 'phase' or 'goal' parameter" );
235                }
236                else if ( executePhase != null && executeGoal != null )
237                {
238                    throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
239                        + ": @execute tag can have only one of a 'phase' or 'goal' parameter" );
240                }
241                mojoDescriptor.setExecutePhase( executePhase );
242                mojoDescriptor.setExecuteGoal( executeGoal );
243    
244                String lifecycle = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_LIFECYCLE );
245                if ( lifecycle != null )
246                {
247                    mojoDescriptor.setExecuteLifecycle( lifecycle );
248                    if ( mojoDescriptor.getExecuteGoal() != null )
249                    {
250                        throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
251                            + ": @execute lifecycle requires a phase instead of a goal" );
252                    }
253                }
254            }
255    
256            // Goal name
257            DocletTag goal = findInClassHierarchy( javaClass, JavaMojoAnnotation.GOAL );
258            if ( goal != null )
259            {
260                mojoDescriptor.setGoal( goal.getValue() );
261            }
262    
263            // inheritByDefault flag
264            boolean value =
265                getBooleanTagValue( javaClass, JavaMojoAnnotation.INHERIT_BY_DEFAULT,
266                                    mojoDescriptor.isInheritedByDefault() );
267            mojoDescriptor.setInheritedByDefault( value );
268    
269            // instantiationStrategy
270            DocletTag tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.INSTANTIATION_STRATEGY );
271            if ( tag != null )
272            {
273                mojoDescriptor.setInstantiationStrategy( tag.getValue() );
274            }
275    
276            // executionStrategy (and deprecated @attainAlways)
277            tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY );
278            if ( tag != null )
279            {
280                getLogger().warn( "@" + JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY + " in "
281                                      + javaClass.getFullyQualifiedName() + " is deprecated: please use '@"
282                                      + JavaMojoAnnotation.EXECUTION_STATEGY + " always' instead." );
283                mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
284            }
285            else
286            {
287                mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
288            }
289            tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTION_STATEGY );
290            if ( tag != null )
291            {
292                mojoDescriptor.setExecutionStrategy( tag.getValue() );
293            }
294    
295            // Phase name
296            DocletTag phase = findInClassHierarchy( javaClass, JavaMojoAnnotation.PHASE );
297            if ( phase != null )
298            {
299                mojoDescriptor.setPhase( phase.getValue() );
300            }
301    
302            // Dependency resolution flag
303            DocletTag requiresDependencyResolution =
304                findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION );
305            if ( requiresDependencyResolution != null )
306            {
307                String v = requiresDependencyResolution.getValue();
308    
309                if ( StringUtils.isEmpty( v ) )
310                {
311                    v = "runtime";
312                }
313    
314                mojoDescriptor.setDependencyResolutionRequired( v );
315            }
316    
317            // Dependency collection flag
318            DocletTag requiresDependencyCollection =
319                findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_COLLECTION );
320            if ( requiresDependencyCollection != null )
321            {
322                String v = requiresDependencyCollection.getValue();
323    
324                if ( StringUtils.isEmpty( v ) )
325                {
326                    v = "runtime";
327                }
328    
329                mojoDescriptor.setDependencyCollectionRequired( v );
330            }
331    
332            // requiresDirectInvocation flag
333            value =
334                getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_DIRECT_INVOCATION,
335                                    mojoDescriptor.isDirectInvocationOnly() );
336            mojoDescriptor.setDirectInvocationOnly( value );
337    
338            // Online flag
339            value =
340                getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
341            mojoDescriptor.setOnlineRequired( value );
342    
343            // Project flag
344            value =
345                getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
346            mojoDescriptor.setProjectRequired( value );
347    
348            // requiresReports flag
349            value =
350                getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_REPORTS, mojoDescriptor.isRequiresReports() );
351            mojoDescriptor.setRequiresReports( value );
352    
353            // ----------------------------------------------------------------------
354            // Javadoc annotations in alphabetical order
355            // ----------------------------------------------------------------------
356    
357            // Deprecation hint
358            DocletTag deprecated = javaClass.getTagByName( JavaMojoAnnotation.DEPRECATED );
359            if ( deprecated != null )
360            {
361                mojoDescriptor.setDeprecated( deprecated.getValue() );
362            }
363    
364            // What version it was introduced in
365            DocletTag since = findInClassHierarchy( javaClass, JavaMojoAnnotation.SINCE );
366            if ( since != null )
367            {
368                mojoDescriptor.setSince( since.getValue() );
369            }
370    
371            // Thread-safe mojo 
372    
373            value = getBooleanTagValue( javaClass, JavaMojoAnnotation.THREAD_SAFE, true, mojoDescriptor.isThreadSafe() );
374            mojoDescriptor.setThreadSafe( value );
375    
376            extractParameters( mojoDescriptor, javaClass );
377    
378            return mojoDescriptor;
379        }
380    
381        /**
382         * @param javaClass not null
383         * @param tagName not null
384         * @param defaultValue the wanted default value
385         * @return the boolean value of the given tagName
386         * @see #findInClassHierarchy(JavaClass, String)
387         */
388        private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultValue )
389        {
390            DocletTag tag = findInClassHierarchy( javaClass, tagName );
391    
392            if ( tag != null )
393            {
394                String value = tag.getValue();
395    
396                if ( StringUtils.isNotEmpty( value ) )
397                {
398                    defaultValue = Boolean.valueOf( value ).booleanValue();
399                }
400            }
401            return defaultValue;
402        }
403    
404        /**
405         * @param javaClass     not null
406         * @param tagName       not null
407         * @param defaultForTag The wanted default value when only the tagname is present
408         * @param defaultValue  the wanted default value when the tag is not specified
409         * @return the boolean value of the given tagName
410         * @see #findInClassHierarchy(JavaClass, String)
411         */
412        private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultForTag,
413                                                   boolean defaultValue )
414        {
415            DocletTag tag = findInClassHierarchy( javaClass, tagName );
416    
417            if ( tag != null )
418            {
419                String value = tag.getValue();
420    
421                if ( StringUtils.isNotEmpty( value ) )
422                {
423                    return Boolean.valueOf( value ).booleanValue();
424                }
425                else
426                {
427                    return defaultForTag;
428                }
429            }
430            return defaultValue;
431        }
432    
433        /**
434         * @param javaClass not null
435         * @param tagName not null
436         * @return docletTag instance
437         */
438        private static DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
439        {
440            DocletTag tag = javaClass.getTagByName( tagName );
441    
442            if ( tag == null )
443            {
444                JavaClass superClass = javaClass.getSuperJavaClass();
445    
446                if ( superClass != null )
447                {
448                    tag = findInClassHierarchy( superClass, tagName );
449                }
450            }
451    
452            return tag;
453        }
454    
455        /**
456         * @param mojoDescriptor not null
457         * @param javaClass not null
458         * @throws InvalidPluginDescriptorException if any
459         */
460        private void extractParameters( MojoDescriptor mojoDescriptor, JavaClass javaClass )
461            throws InvalidPluginDescriptorException
462        {
463            // ---------------------------------------------------------------------------------
464            // We're resolving class-level, ancestor-class-field, local-class-field order here.
465            // ---------------------------------------------------------------------------------
466    
467            Map<String, JavaField> rawParams = extractFieldParameterTags( javaClass );
468    
469            for ( Map.Entry<String, JavaField> entry : rawParams.entrySet() )
470            {
471                JavaField field = entry.getValue();
472    
473                Type type = field.getType();
474    
475                Parameter pd = new Parameter();
476    
477                pd.setName( entry.getKey() );
478    
479                if ( !type.isArray() )
480                {
481                    pd.setType( type.getValue() );
482                }
483                else
484                {
485                    StringBuilder value = new StringBuilder( type.getValue() );
486    
487                    int remaining = type.getDimensions();
488    
489                    while ( remaining-- > 0 )
490                    {
491                        value.append( "[]" );
492                    }
493    
494                    pd.setType( value.toString() );
495                }
496    
497                pd.setDescription( field.getComment() );
498    
499                DocletTag componentTag = field.getTagByName( JavaMojoAnnotation.COMPONENT );
500    
501                if ( componentTag != null )
502                {
503                    String role = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLE );
504    
505                    if ( role == null )
506                    {
507                        role = field.getType().toString();
508                    }
509    
510                    String roleHint = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLEHINT );
511    
512                    if ( roleHint == null )
513                    {
514                        // support alternate syntax for better compatibility with the Plexus CDC.
515                        roleHint = componentTag.getNamedParameter( "role-hint" );
516                    }
517    
518                    String expression = PluginUtils.MAVEN_COMPONENTS.get( role );
519    
520                    if ( expression == null )
521                    {
522                        pd.setRequirement( new Requirement( role, roleHint ) );
523                    }
524                    else
525                    {
526                        pd.setDefaultValue( expression );
527                        pd.setImplementation( role );
528                        pd.setType( role );
529                    }
530    
531                    pd.setEditable( false );
532                    /* TODO: or better like this? Need @component fields be editable for the user?
533                    pd.setEditable( field.getTagByName( READONLY ) == null );
534                    */
535                }
536                else
537                {
538                    DocletTag parameter = field.getTagByName( JavaMojoAnnotation.PARAMETER );
539    
540                    pd.setRequired( field.getTagByName( JavaMojoAnnotation.REQUIRED ) != null );
541    
542                    pd.setEditable( field.getTagByName( JavaMojoAnnotation.READONLY ) == null );
543    
544                    DocletTag deprecationTag = field.getTagByName( JavaMojoAnnotation.DEPRECATED );
545    
546                    if ( deprecationTag != null )
547                    {
548                        pd.setDeprecated( deprecationTag.getValue() );
549                    }
550    
551                    DocletTag sinceTag = field.getTagByName( JavaMojoAnnotation.SINCE );
552                    if ( sinceTag != null )
553                    {
554                        pd.setSince( sinceTag.getValue() );
555                    }
556    
557                    String alias = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_ALIAS );
558    
559                    if ( !StringUtils.isEmpty( alias ) )
560                    {
561                        pd.setAlias( alias );
562                    }
563    
564                    String expression = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_EXPRESSION );
565                    String property = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_PROPERTY );
566    
567                    if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
568                    {
569                        getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
570                        getLogger().error( "  Cannot use both:" );
571                        getLogger().error( "    @parameter expression=\"${property}\"" );
572                        getLogger().error( "  and" );
573                        getLogger().error( "    @parameter property=\"property\"" );
574                        getLogger().error( "  Second syntax is preferred." );
575                        throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
576                            + ": cannot" + " use both @parameter expression and property", null );
577                    }
578    
579                    if ( StringUtils.isNotEmpty( expression ) )
580                    {
581                        getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
582                        getLogger().warn( "  The syntax" );
583                        getLogger().warn( "    @parameter expression=\"${property}\"" );
584                        getLogger().warn( "  is deprecated, please use" );
585                        getLogger().warn( "    @parameter property=\"property\"" );
586                        getLogger().warn( "  instead." );
587    
588                    }
589                    else if ( StringUtils.isNotEmpty( property ) )
590                    {
591                        expression = "${" + property + "}";
592                    }
593    
594                    pd.setExpression( expression );
595    
596                    if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
597                    {
598                        getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
599                        getLogger().warn( "  The syntax" );
600                        getLogger().warn( "    @parameter expression=\"${component.<role>#<roleHint>}\"" );
601                        getLogger().warn( "  is deprecated, please use" );
602                        getLogger().warn( "    @component role=\"<role>\" roleHint=\"<roleHint>\"" );
603                        getLogger().warn( "  instead." );
604                    }
605    
606                    if ( "${reports}".equals( pd.getExpression() ) )
607                    {
608                        mojoDescriptor.setRequiresReports( true );
609                    }
610    
611                    pd.setDefaultValue( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
612    
613                    pd.setImplementation( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
614                }
615    
616                mojoDescriptor.addParameter( pd );
617            }
618        }
619    
620        /**
621         * extract fields that are either parameters or components.
622         * 
623         * @param javaClass not null
624         * @return map with Mojo parameters names as keys
625         */
626        private Map<String, JavaField> extractFieldParameterTags( JavaClass javaClass )
627        {
628            Map<String, JavaField> rawParams;
629    
630            // we have to add the parent fields first, so that they will be overwritten by the local fields if
631            // that actually happens...
632            JavaClass superClass = javaClass.getSuperJavaClass();
633    
634            if ( superClass != null )
635            {
636                rawParams = extractFieldParameterTags( superClass );
637            }
638            else
639            {
640                rawParams = new TreeMap<String, JavaField>();
641            }
642    
643            JavaField[] classFields = javaClass.getFields();
644    
645            if ( classFields != null )
646            {
647                for ( JavaField field : classFields )
648                {
649                    if ( field.getTagByName( JavaMojoAnnotation.PARAMETER ) != null
650                        || field.getTagByName( JavaMojoAnnotation.COMPONENT ) != null )
651                    {
652                        rawParams.put( field.getName(), field );
653                    }
654                }
655            }
656            return rawParams;
657        }
658    
659        /** {@inheritDoc} */
660        public List<MojoDescriptor> execute( MavenProject project, PluginDescriptor pluginDescriptor )
661            throws ExtractionException, InvalidPluginDescriptorException
662        {
663            return execute( new DefaultPluginToolsRequest( project, pluginDescriptor ) );
664        }
665        
666        /** {@inheritDoc} */
667        public List<MojoDescriptor> execute( PluginToolsRequest request )
668            throws ExtractionException, InvalidPluginDescriptorException
669        {
670            JavaClass[] javaClasses = discoverClasses( request );
671    
672            List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
673    
674            for ( JavaClass javaClass : javaClasses )
675            {
676                DocletTag tag = javaClass.getTagByName( GOAL );
677    
678                if ( tag != null )
679                {
680                    MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
681                    mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
682    
683                    // Validate the descriptor as best we can before allowing it to be processed.
684                    validate( mojoDescriptor );
685    
686                    descriptors.add( mojoDescriptor );
687                }
688            }
689    
690            return descriptors;
691        }
692    
693        /**
694         * @param request The plugin request.
695         * @return an array of java class
696         */
697        @SuppressWarnings( "unchecked" )
698        protected JavaClass[] discoverClasses( final PluginToolsRequest request )
699        {
700            JavaDocBuilder builder = new JavaDocBuilder();
701            builder.setEncoding( request.getEncoding() );
702            
703            MavenProject project = request.getProject();
704    
705            for ( String source : (List<String>) project.getCompileSourceRoots() )
706            {
707                builder.addSourceTree( new File( source ) );
708            }
709    
710            // TODO be more dynamic
711            File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
712            if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
713            {
714                builder.addSourceTree( generatedPlugin );
715            }
716    
717            return builder.getClasses();
718        }
719    
720        /**
721         * @param mojoDescriptor not null
722         * @throws InvalidParameterException if any
723         */
724        protected void validate( MojoDescriptor mojoDescriptor )
725            throws InvalidParameterException
726        {
727            @SuppressWarnings( "unchecked" )
728            List<Parameter> parameters = mojoDescriptor.getParameters();
729    
730            if ( parameters != null )
731            {
732                for ( int j = 0; j < parameters.size(); j++ )
733                {
734                    validateParameter( parameters.get( j ), j );
735                }
736            }
737        }
738    }