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.PluginDescriptor;
33  import org.apache.maven.plugin.descriptor.Requirement;
34  import org.apache.maven.project.MavenProject;
35  import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
36  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
37  import org.apache.maven.tools.plugin.PluginToolsRequest;
38  import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
39  import org.apache.maven.tools.plugin.extractor.ExtractionException;
40  import org.apache.maven.tools.plugin.util.PluginUtils;
41  
42  import org.codehaus.plexus.component.annotations.Component;
43  import org.codehaus.plexus.logging.AbstractLogEnabled;
44  import org.codehaus.plexus.util.StringUtils;
45  
46  import java.io.File;
47  import java.util.ArrayList;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.TreeMap;
51  
52  /**
53   * Extracts Mojo descriptors from <a href="http://java.sun.com/">Java</a> sources.
54   * <br/>
55   * For more information about the usage tag, have a look to:
56   * <a href="http://maven.apache.org/developers/mojo-api-specification.html">
57   * http://maven.apache.org/developers/mojo-api-specification.html</a>
58   *
59   * @todo need to add validation directives so that systems embedding maven2 can
60   * get validation directives to help users in IDEs.
61   * @version $Id: JavaMojoDescriptorExtractor.java 1343166 2012-05-28 08:54:25Z hboutemy $
62   * @see org.apache.maven.plugin.descriptor.MojoDescriptor
63   */
64  @Component( role = MojoDescriptorExtractor.class, hint = "java" )
65  public class JavaMojoDescriptorExtractor
66      extends AbstractLogEnabled
67      implements MojoDescriptorExtractor, JavaMojoAnnotation
68  {
69      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#INSTANTIATION_STRATEGY} instead of. */
70      public static final String MAVEN_PLUGIN_INSTANTIATION = JavaMojoAnnotation.INSTANTIATION_STRATEGY;
71  
72      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#CONFIGURATOR} instead of. */
73      public static final String CONFIGURATOR = JavaMojoAnnotation.CONFIGURATOR;
74  
75      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER} instead of. */
76      public static final String PARAMETER = JavaMojoAnnotation.PARAMETER;
77  
78      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_EXPRESSION} instead of. */
79      public static final String PARAMETER_EXPRESSION = JavaMojoAnnotation.PARAMETER_EXPRESSION;
80  
81      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_DEFAULT_VALUE} instead of. */
82      public static final String PARAMETER_DEFAULT_VALUE = JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE;
83  
84      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_ALIAS} instead of. */
85      public static final String PARAMETER_ALIAS = JavaMojoAnnotation.PARAMETER_ALIAS;
86  
87      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#SINCE} instead of. */
88      public static final String SINCE = JavaMojoAnnotation.SINCE;
89  
90      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_IMPLEMENTATION} instead of. */
91      public static final String PARAMETER_IMPLEMENTATION = JavaMojoAnnotation.PARAMETER_IMPLEMENTATION;
92  
93      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRED} instead of. */
94      public static final String REQUIRED = JavaMojoAnnotation.REQUIRED;
95  
96      /** @deprecated since 2.4, use {@link JavaMojoAnnotation#DEPRECATED} instead of. */
97      public static final String DEPRECATED = JavaMojoAnnotation.DEPRECATED;
98  
99      /** @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 }