Coverage Report - org.apache.maven.tools.plugin.extractor.java.JavaMojoDescriptorExtractor
 
Classes in this File Line Coverage Branch Coverage Complexity
JavaMojoDescriptorExtractor
86 %
186/215
81 %
96/118
7
 
 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  5
 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  8
         String name = parameter.getName();
 166  
 
 167  8
         if ( name == null )
 168  
         {
 169  0
             throw new InvalidParameterException( "name", i );
 170  
         }
 171  
 
 172  
         // TODO: remove when backward compatibility is no longer an issue.
 173  8
         String type = parameter.getType();
 174  
 
 175  8
         if ( type == null )
 176  
         {
 177  0
             throw new InvalidParameterException( "type", i );
 178  
         }
 179  
 
 180  
         // TODO: remove when backward compatibility is no longer an issue.
 181  8
         String description = parameter.getDescription();
 182  
 
 183  8
         if ( description == null )
 184  
         {
 185  0
             throw new InvalidParameterException( "description", i );
 186  
         }
 187  8
     }
 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  5
         ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor();
 202  5
         mojoDescriptor.setLanguage( "java" );
 203  5
         mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
 204  5
         mojoDescriptor.setDescription( javaClass.getComment() );
 205  
 
 206  
         // ----------------------------------------------------------------------
 207  
         // Mojo annotations in alphabetical order
 208  
         // ----------------------------------------------------------------------
 209  
 
 210  
         // Aggregator flag
 211  5
         DocletTag aggregator = findInClassHierarchy( javaClass, JavaMojoAnnotation.AGGREGATOR );
 212  5
         if ( aggregator != null )
 213  
         {
 214  1
             mojoDescriptor.setAggregator( true );
 215  
         }
 216  
 
 217  
         // Configurator hint
 218  5
         DocletTag configurator = findInClassHierarchy( javaClass, JavaMojoAnnotation.CONFIGURATOR );
 219  5
         if ( configurator != null )
 220  
         {
 221  1
             mojoDescriptor.setComponentConfigurator( configurator.getValue() );
 222  
         }
 223  
 
 224  
         // Additional phase to execute first
 225  5
         DocletTag execute = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTE );
 226  5
         if ( execute != null )
 227  
         {
 228  1
             String executePhase = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_PHASE );
 229  1
             String executeGoal = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_GOAL );
 230  
 
 231  1
             if ( executePhase == null && executeGoal == null )
 232  
             {
 233  0
                 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
 234  
                     + ": @execute tag requires either a 'phase' or 'goal' parameter" );
 235  
             }
 236  1
             else if ( executePhase != null && executeGoal != null )
 237  
             {
 238  0
                 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
 239  
                     + ": @execute tag can have only one of a 'phase' or 'goal' parameter" );
 240  
             }
 241  1
             mojoDescriptor.setExecutePhase( executePhase );
 242  1
             mojoDescriptor.setExecuteGoal( executeGoal );
 243  
 
 244  1
             String lifecycle = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_LIFECYCLE );
 245  1
             if ( lifecycle != null )
 246  
             {
 247  1
                 mojoDescriptor.setExecuteLifecycle( lifecycle );
 248  1
                 if ( mojoDescriptor.getExecuteGoal() != null )
 249  
                 {
 250  0
                     throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
 251  
                         + ": @execute lifecycle requires a phase instead of a goal" );
 252  
                 }
 253  
             }
 254  
         }
 255  
 
 256  
         // Goal name
 257  5
         DocletTag goal = findInClassHierarchy( javaClass, JavaMojoAnnotation.GOAL );
 258  5
         if ( goal != null )
 259  
         {
 260  5
             mojoDescriptor.setGoal( goal.getValue() );
 261  
         }
 262  
 
 263  
         // inheritByDefault flag
 264  5
         boolean value =
 265  
             getBooleanTagValue( javaClass, JavaMojoAnnotation.INHERIT_BY_DEFAULT,
 266  
                                 mojoDescriptor.isInheritedByDefault() );
 267  5
         mojoDescriptor.setInheritedByDefault( value );
 268  
 
 269  
         // instantiationStrategy
 270  5
         DocletTag tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.INSTANTIATION_STRATEGY );
 271  5
         if ( tag != null )
 272  
         {
 273  1
             mojoDescriptor.setInstantiationStrategy( tag.getValue() );
 274  
         }
 275  
 
 276  
         // executionStrategy (and deprecated @attainAlways)
 277  5
         tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY );
 278  5
         if ( tag != null )
 279  
         {
 280  0
             getLogger().warn( "@" + JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY + " in "
 281  
                                   + javaClass.getFullyQualifiedName() + " is deprecated: please use '@"
 282  
                                   + JavaMojoAnnotation.EXECUTION_STATEGY + " always' instead." );
 283  0
             mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
 284  
         }
 285  
         else
 286  
         {
 287  5
             mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
 288  
         }
 289  5
         tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTION_STATEGY );
 290  5
         if ( tag != null )
 291  
         {
 292  1
             mojoDescriptor.setExecutionStrategy( tag.getValue() );
 293  
         }
 294  
 
 295  
         // Phase name
 296  5
         DocletTag phase = findInClassHierarchy( javaClass, JavaMojoAnnotation.PHASE );
 297  5
         if ( phase != null )
 298  
         {
 299  1
             mojoDescriptor.setPhase( phase.getValue() );
 300  
         }
 301  
 
 302  
         // Dependency resolution flag
 303  5
         DocletTag requiresDependencyResolution =
 304  
             findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION );
 305  5
         if ( requiresDependencyResolution != null )
 306  
         {
 307  3
             String v = requiresDependencyResolution.getValue();
 308  
 
 309  3
             if ( StringUtils.isEmpty( v ) )
 310  
             {
 311  0
                 v = "runtime";
 312  
             }
 313  
 
 314  3
             mojoDescriptor.setDependencyResolutionRequired( v );
 315  
         }
 316  
 
 317  
         // Dependency collection flag
 318  5
         DocletTag requiresDependencyCollection =
 319  
             findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_COLLECTION );
 320  5
         if ( requiresDependencyCollection != null )
 321  
         {
 322  3
             String v = requiresDependencyCollection.getValue();
 323  
 
 324  3
             if ( StringUtils.isEmpty( v ) )
 325  
             {
 326  0
                 v = "runtime";
 327  
             }
 328  
 
 329  3
             mojoDescriptor.setDependencyCollectionRequired( v );
 330  
         }
 331  
 
 332  
         // requiresDirectInvocation flag
 333  5
         value =
 334  
             getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_DIRECT_INVOCATION,
 335  
                                 mojoDescriptor.isDirectInvocationOnly() );
 336  5
         mojoDescriptor.setDirectInvocationOnly( value );
 337  
 
 338  
         // Online flag
 339  5
         value =
 340  
             getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
 341  5
         mojoDescriptor.setOnlineRequired( value );
 342  
 
 343  
         // Project flag
 344  5
         value =
 345  
             getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
 346  5
         mojoDescriptor.setProjectRequired( value );
 347  
 
 348  
         // requiresReports flag
 349  5
         value =
 350  
             getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_REPORTS, mojoDescriptor.isRequiresReports() );
 351  5
         mojoDescriptor.setRequiresReports( value );
 352  
 
 353  
         // ----------------------------------------------------------------------
 354  
         // Javadoc annotations in alphabetical order
 355  
         // ----------------------------------------------------------------------
 356  
 
 357  
         // Deprecation hint
 358  5
         DocletTag deprecated = javaClass.getTagByName( JavaMojoAnnotation.DEPRECATED );
 359  5
         if ( deprecated != null )
 360  
         {
 361  1
             mojoDescriptor.setDeprecated( deprecated.getValue() );
 362  
         }
 363  
 
 364  
         // What version it was introduced in
 365  5
         DocletTag since = findInClassHierarchy( javaClass, JavaMojoAnnotation.SINCE );
 366  5
         if ( since != null )
 367  
         {
 368  1
             mojoDescriptor.setSince( since.getValue() );
 369  
         }
 370  
 
 371  
         // Thread-safe mojo 
 372  
 
 373  5
         value = getBooleanTagValue( javaClass, JavaMojoAnnotation.THREAD_SAFE, true, mojoDescriptor.isThreadSafe() );
 374  5
         mojoDescriptor.setThreadSafe( value );
 375  
 
 376  5
         extractParameters( mojoDescriptor, javaClass );
 377  
 
 378  5
         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  25
         DocletTag tag = findInClassHierarchy( javaClass, tagName );
 391  
 
 392  25
         if ( tag != null )
 393  
         {
 394  5
             String value = tag.getValue();
 395  
 
 396  5
             if ( StringUtils.isNotEmpty( value ) )
 397  
             {
 398  5
                 defaultValue = Boolean.valueOf( value ).booleanValue();
 399  
             }
 400  
         }
 401  25
         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  5
         DocletTag tag = findInClassHierarchy( javaClass, tagName );
 416  
 
 417  5
         if ( tag != null )
 418  
         {
 419  3
             String value = tag.getValue();
 420  
 
 421  3
             if ( StringUtils.isNotEmpty( value ) )
 422  
             {
 423  0
                 return Boolean.valueOf( value ).booleanValue();
 424  
             }
 425  
             else
 426  
             {
 427  3
                 return defaultForTag;
 428  
             }
 429  
         }
 430  2
         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  203
         DocletTag tag = javaClass.getTagByName( tagName );
 441  
 
 442  203
         if ( tag == null )
 443  
         {
 444  177
             JavaClass superClass = javaClass.getSuperJavaClass();
 445  
 
 446  177
             if ( superClass != null )
 447  
             {
 448  118
                 tag = findInClassHierarchy( superClass, tagName );
 449  
             }
 450  
         }
 451  
 
 452  203
         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  5
         Map<String, JavaField> rawParams = extractFieldParameterTags( javaClass );
 468  
 
 469  5
         for ( Map.Entry<String, JavaField> entry : rawParams.entrySet() )
 470  
         {
 471  8
             JavaField field = entry.getValue();
 472  
 
 473  8
             Type type = field.getType();
 474  
 
 475  8
             Parameter pd = new Parameter();
 476  
 
 477  8
             pd.setName( entry.getKey() );
 478  
 
 479  8
             if ( !type.isArray() )
 480  
             {
 481  6
                 pd.setType( type.getValue() );
 482  
             }
 483  
             else
 484  
             {
 485  2
                 StringBuilder value = new StringBuilder( type.getValue() );
 486  
 
 487  2
                 int remaining = type.getDimensions();
 488  
 
 489  4
                 while ( remaining-- > 0 )
 490  
                 {
 491  2
                     value.append( "[]" );
 492  
                 }
 493  
 
 494  2
                 pd.setType( value.toString() );
 495  
             }
 496  
 
 497  8
             pd.setDescription( field.getComment() );
 498  
 
 499  8
             DocletTag componentTag = field.getTagByName( JavaMojoAnnotation.COMPONENT );
 500  
 
 501  8
             if ( componentTag != null )
 502  
             {
 503  2
                 String role = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLE );
 504  
 
 505  2
                 if ( role == null )
 506  
                 {
 507  1
                     role = field.getType().toString();
 508  
                 }
 509  
 
 510  2
                 String roleHint = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLEHINT );
 511  
 
 512  2
                 if ( roleHint == null )
 513  
                 {
 514  
                     // support alternate syntax for better compatibility with the Plexus CDC.
 515  1
                     roleHint = componentTag.getNamedParameter( "role-hint" );
 516  
                 }
 517  
 
 518  2
                 String expression = PluginUtils.MAVEN_COMPONENTS.get( role );
 519  
 
 520  2
                 if ( expression == null )
 521  
                 {
 522  2
                     pd.setRequirement( new Requirement( role, roleHint ) );
 523  
                 }
 524  
                 else
 525  
                 {
 526  0
                     pd.setDefaultValue( expression );
 527  0
                     pd.setImplementation( role );
 528  0
                     pd.setType( role );
 529  
                 }
 530  
 
 531  2
                 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  2
             }
 536  
             else
 537  
             {
 538  6
                 DocletTag parameter = field.getTagByName( JavaMojoAnnotation.PARAMETER );
 539  
 
 540  6
                 pd.setRequired( field.getTagByName( JavaMojoAnnotation.REQUIRED ) != null );
 541  
 
 542  6
                 pd.setEditable( field.getTagByName( JavaMojoAnnotation.READONLY ) == null );
 543  
 
 544  6
                 DocletTag deprecationTag = field.getTagByName( JavaMojoAnnotation.DEPRECATED );
 545  
 
 546  6
                 if ( deprecationTag != null )
 547  
                 {
 548  1
                     pd.setDeprecated( deprecationTag.getValue() );
 549  
                 }
 550  
 
 551  6
                 DocletTag sinceTag = field.getTagByName( JavaMojoAnnotation.SINCE );
 552  6
                 if ( sinceTag != null )
 553  
                 {
 554  1
                     pd.setSince( sinceTag.getValue() );
 555  
                 }
 556  
 
 557  6
                 String alias = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_ALIAS );
 558  
 
 559  6
                 if ( !StringUtils.isEmpty( alias ) )
 560  
                 {
 561  1
                     pd.setAlias( alias );
 562  
                 }
 563  
 
 564  6
                 String expression = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_EXPRESSION );
 565  6
                 String property = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_PROPERTY );
 566  
 
 567  6
                 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
 568  
                 {
 569  0
                     getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 570  0
                     getLogger().error( "  Cannot use both:" );
 571  0
                     getLogger().error( "    @parameter expression=\"${property}\"" );
 572  0
                     getLogger().error( "  and" );
 573  0
                     getLogger().error( "    @parameter property=\"property\"" );
 574  0
                     getLogger().error( "  Second syntax is preferred." );
 575  0
                     throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
 576  
                         + ": cannot" + " use both @parameter expression and property", null );
 577  
                 }
 578  
 
 579  6
                 if ( StringUtils.isNotEmpty( expression ) )
 580  
                 {
 581  1
                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 582  1
                     getLogger().warn( "  The syntax" );
 583  1
                     getLogger().warn( "    @parameter expression=\"${property}\"" );
 584  1
                     getLogger().warn( "  is deprecated, please use" );
 585  1
                     getLogger().warn( "    @parameter property=\"property\"" );
 586  1
                     getLogger().warn( "  instead." );
 587  
 
 588  
                 }
 589  5
                 else if ( StringUtils.isNotEmpty( property ) )
 590  
                 {
 591  1
                     expression = "${" + property + "}";
 592  
                 }
 593  
 
 594  6
                 pd.setExpression( expression );
 595  
 
 596  6
                 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
 597  
                 {
 598  0
                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 599  0
                     getLogger().warn( "  The syntax" );
 600  0
                     getLogger().warn( "    @parameter expression=\"${component.<role>#<roleHint>}\"" );
 601  0
                     getLogger().warn( "  is deprecated, please use" );
 602  0
                     getLogger().warn( "    @component role=\"<role>\" roleHint=\"<roleHint>\"" );
 603  0
                     getLogger().warn( "  instead." );
 604  
                 }
 605  
 
 606  6
                 if ( "${reports}".equals( pd.getExpression() ) )
 607  
                 {
 608  0
                     mojoDescriptor.setRequiresReports( true );
 609  
                 }
 610  
 
 611  6
                 pd.setDefaultValue( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
 612  
 
 613  6
                 pd.setImplementation( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
 614  
             }
 615  
 
 616  8
             mojoDescriptor.addParameter( pd );
 617  8
         }
 618  5
     }
 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  15
         JavaClass superClass = javaClass.getSuperJavaClass();
 633  
 
 634  15
         if ( superClass != null )
 635  
         {
 636  10
             rawParams = extractFieldParameterTags( superClass );
 637  
         }
 638  
         else
 639  
         {
 640  5
             rawParams = new TreeMap<String, JavaField>();
 641  
         }
 642  
 
 643  15
         JavaField[] classFields = javaClass.getFields();
 644  
 
 645  15
         if ( classFields != null )
 646  
         {
 647  35
             for ( JavaField field : classFields )
 648  
             {
 649  20
                 if ( field.getTagByName( JavaMojoAnnotation.PARAMETER ) != null
 650  
                     || field.getTagByName( JavaMojoAnnotation.COMPONENT ) != null )
 651  
                 {
 652  8
                     rawParams.put( field.getName(), field );
 653  
                 }
 654  
             }
 655  
         }
 656  15
         return rawParams;
 657  
     }
 658  
 
 659  
     /** {@inheritDoc} */
 660  
     public List<MojoDescriptor> execute( MavenProject project, PluginDescriptor pluginDescriptor )
 661  
         throws ExtractionException, InvalidPluginDescriptorException
 662  
     {
 663  0
         return execute( new DefaultPluginToolsRequest( project, pluginDescriptor ) );
 664  
     }
 665  
     
 666  
     /** {@inheritDoc} */
 667  
     public List<MojoDescriptor> execute( PluginToolsRequest request )
 668  
         throws ExtractionException, InvalidPluginDescriptorException
 669  
     {
 670  5
         JavaClass[] javaClasses = discoverClasses( request );
 671  
 
 672  5
         List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
 673  
 
 674  16
         for ( JavaClass javaClass : javaClasses )
 675  
         {
 676  11
             DocletTag tag = javaClass.getTagByName( GOAL );
 677  
 
 678  11
             if ( tag != null )
 679  
             {
 680  5
                 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
 681  5
                 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
 682  
 
 683  
                 // Validate the descriptor as best we can before allowing it to be processed.
 684  5
                 validate( mojoDescriptor );
 685  
 
 686  5
                 descriptors.add( mojoDescriptor );
 687  
             }
 688  
         }
 689  
 
 690  5
         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  5
         JavaDocBuilder builder = new JavaDocBuilder();
 701  5
         builder.setEncoding( request.getEncoding() );
 702  
         
 703  5
         MavenProject project = request.getProject();
 704  
 
 705  5
         for ( String source : (List<String>) project.getCompileSourceRoots() )
 706  
         {
 707  5
             builder.addSourceTree( new File( source ) );
 708  
         }
 709  
 
 710  
         // TODO be more dynamic
 711  5
         File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
 712  5
         if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
 713  
         {
 714  5
             builder.addSourceTree( generatedPlugin );
 715  
         }
 716  
 
 717  5
         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  5
         List<Parameter> parameters = mojoDescriptor.getParameters();
 729  
 
 730  5
         if ( parameters != null )
 731  
         {
 732  12
             for ( int j = 0; j < parameters.size(); j++ )
 733  
             {
 734  8
                 validateParameter( parameters.get( j ), j );
 735  
             }
 736  
         }
 737  5
     }
 738  
 }