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 1353231 2012-06-24 08:36:58Z 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 deprecationTag = field.getTagByName( JavaMojoAnnotation.DEPRECATED );
500 
501             if ( deprecationTag != null )
502             {
503                 pd.setDeprecated( deprecationTag.getValue() );
504             }
505 
506             DocletTag sinceTag = field.getTagByName( JavaMojoAnnotation.SINCE );
507             if ( sinceTag != null )
508             {
509                 pd.setSince( sinceTag.getValue() );
510             }
511 
512             DocletTag componentTag = field.getTagByName( JavaMojoAnnotation.COMPONENT );
513 
514             if ( componentTag != null )
515             {
516                 // Component tag
517                 String role = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLE );
518 
519                 if ( role == null )
520                 {
521                     role = field.getType().toString();
522                 }
523 
524                 String roleHint = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLEHINT );
525 
526                 if ( roleHint == null )
527                 {
528                     // support alternate syntax for better compatibility with the Plexus CDC.
529                     roleHint = componentTag.getNamedParameter( "role-hint" );
530                 }
531 
532                 // recognize Maven-injected objects as components annotations instead of parameters
533                 String expression = PluginUtils.MAVEN_COMPONENTS.get( role );
534 
535                 if ( expression == null )
536                 {
537                     // normal component
538                     pd.setRequirement( new Requirement( role, roleHint ) );
539                 }
540                 else
541                 {
542                     // not a component but a Maven object to be transformed into an expression/property
543                     pd.setDefaultValue( expression );
544                     pd.setType( role );
545                     pd.setRequired( true );
546                 }
547 
548                 pd.setEditable( false );
549                 /* TODO: or better like this? Need @component fields be editable for the user?
550                 pd.setEditable( field.getTagByName( READONLY ) == null );
551                 */
552             }
553             else
554             {
555                 // Parameter tag
556                 DocletTag parameter = field.getTagByName( JavaMojoAnnotation.PARAMETER );
557 
558                 pd.setRequired( field.getTagByName( JavaMojoAnnotation.REQUIRED ) != null );
559 
560                 pd.setEditable( field.getTagByName( JavaMojoAnnotation.READONLY ) == null );
561 
562                 String alias = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_ALIAS );
563 
564                 if ( !StringUtils.isEmpty( alias ) )
565                 {
566                     pd.setAlias( alias );
567                 }
568 
569                 String expression = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_EXPRESSION );
570                 String property = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_PROPERTY );
571 
572                 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
573                 {
574                     getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
575                     getLogger().error( "  Cannot use both:" );
576                     getLogger().error( "    @parameter expression=\"${property}\"" );
577                     getLogger().error( "  and" );
578                     getLogger().error( "    @parameter property=\"property\"" );
579                     getLogger().error( "  Second syntax is preferred." );
580                     throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
581                         + ": cannot" + " use both @parameter expression and property", null );
582                 }
583 
584                 if ( StringUtils.isNotEmpty( expression ) )
585                 {
586                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
587                     getLogger().warn( "  The syntax" );
588                     getLogger().warn( "    @parameter expression=\"${property}\"" );
589                     getLogger().warn( "  is deprecated, please use" );
590                     getLogger().warn( "    @parameter property=\"property\"" );
591                     getLogger().warn( "  instead." );
592 
593                 }
594                 else if ( StringUtils.isNotEmpty( property ) )
595                 {
596                     expression = "${" + property + "}";
597                 }
598 
599                 pd.setExpression( expression );
600 
601                 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
602                 {
603                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
604                     getLogger().warn( "  The syntax" );
605                     getLogger().warn( "    @parameter expression=\"${component.<role>#<roleHint>}\"" );
606                     getLogger().warn( "  is deprecated, please use" );
607                     getLogger().warn( "    @component role=\"<role>\" roleHint=\"<roleHint>\"" );
608                     getLogger().warn( "  instead." );
609                 }
610 
611                 if ( "${reports}".equals( pd.getExpression() ) )
612                 {
613                     mojoDescriptor.setRequiresReports( true );
614                 }
615 
616                 pd.setDefaultValue( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
617 
618                 pd.setImplementation( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
619             }
620 
621             mojoDescriptor.addParameter( pd );
622         }
623     }
624 
625     /**
626      * extract fields that are either parameters or components.
627      * 
628      * @param javaClass not null
629      * @return map with Mojo parameters names as keys
630      */
631     private Map<String, JavaField> extractFieldParameterTags( JavaClass javaClass )
632     {
633         Map<String, JavaField> rawParams;
634 
635         // we have to add the parent fields first, so that they will be overwritten by the local fields if
636         // that actually happens...
637         JavaClass superClass = javaClass.getSuperJavaClass();
638 
639         if ( superClass != null )
640         {
641             rawParams = extractFieldParameterTags( superClass );
642         }
643         else
644         {
645             rawParams = new TreeMap<String, JavaField>();
646         }
647 
648         JavaField[] classFields = javaClass.getFields();
649 
650         if ( classFields != null )
651         {
652             for ( JavaField field : classFields )
653             {
654                 if ( field.getTagByName( JavaMojoAnnotation.PARAMETER ) != null
655                     || field.getTagByName( JavaMojoAnnotation.COMPONENT ) != null )
656                 {
657                     rawParams.put( field.getName(), field );
658                 }
659             }
660         }
661         return rawParams;
662     }
663 
664     /** {@inheritDoc} */
665     public List<MojoDescriptor> execute( MavenProject project, PluginDescriptor pluginDescriptor )
666         throws ExtractionException, InvalidPluginDescriptorException
667     {
668         return execute( new DefaultPluginToolsRequest( project, pluginDescriptor ) );
669     }
670     
671     /** {@inheritDoc} */
672     public List<MojoDescriptor> execute( PluginToolsRequest request )
673         throws ExtractionException, InvalidPluginDescriptorException
674     {
675         JavaClass[] javaClasses = discoverClasses( request );
676 
677         List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
678 
679         for ( JavaClass javaClass : javaClasses )
680         {
681             DocletTag tag = javaClass.getTagByName( GOAL );
682 
683             if ( tag != null )
684             {
685                 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
686                 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
687 
688                 // Validate the descriptor as best we can before allowing it to be processed.
689                 validate( mojoDescriptor );
690 
691                 descriptors.add( mojoDescriptor );
692             }
693         }
694 
695         return descriptors;
696     }
697 
698     /**
699      * @param request The plugin request.
700      * @return an array of java class
701      */
702     @SuppressWarnings( "unchecked" )
703     protected JavaClass[] discoverClasses( final PluginToolsRequest request )
704     {
705         JavaDocBuilder builder = new JavaDocBuilder();
706         builder.setEncoding( request.getEncoding() );
707         
708         MavenProject project = request.getProject();
709 
710         for ( String source : (List<String>) project.getCompileSourceRoots() )
711         {
712             builder.addSourceTree( new File( source ) );
713         }
714 
715         // TODO be more dynamic
716         File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
717         if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
718         {
719             builder.addSourceTree( generatedPlugin );
720         }
721 
722         return builder.getClasses();
723     }
724 
725     /**
726      * @param mojoDescriptor not null
727      * @throws InvalidParameterException if any
728      */
729     protected void validate( MojoDescriptor mojoDescriptor )
730         throws InvalidParameterException
731     {
732         @SuppressWarnings( "unchecked" )
733         List<Parameter> parameters = mojoDescriptor.getParameters();
734 
735         if ( parameters != null )
736         {
737             for ( int j = 0; j < parameters.size(); j++ )
738             {
739                 validateParameter( parameters.get( j ), j );
740             }
741         }
742     }
743 }