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