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 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  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 deprecationTag = field.getTagByName( JavaMojoAnnotation.DEPRECATED );
 500  
 
 501  8
             if ( deprecationTag != null )
 502  
             {
 503  1
                 pd.setDeprecated( deprecationTag.getValue() );
 504  
             }
 505  
 
 506  8
             DocletTag sinceTag = field.getTagByName( JavaMojoAnnotation.SINCE );
 507  8
             if ( sinceTag != null )
 508  
             {
 509  1
                 pd.setSince( sinceTag.getValue() );
 510  
             }
 511  
 
 512  8
             DocletTag componentTag = field.getTagByName( JavaMojoAnnotation.COMPONENT );
 513  
 
 514  8
             if ( componentTag != null )
 515  
             {
 516  
                 // Component tag
 517  2
                 String role = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLE );
 518  
 
 519  2
                 if ( role == null )
 520  
                 {
 521  1
                     role = field.getType().toString();
 522  
                 }
 523  
 
 524  2
                 String roleHint = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLEHINT );
 525  
 
 526  2
                 if ( roleHint == null )
 527  
                 {
 528  
                     // support alternate syntax for better compatibility with the Plexus CDC.
 529  1
                     roleHint = componentTag.getNamedParameter( "role-hint" );
 530  
                 }
 531  
 
 532  
                 // recognize Maven-injected objects as components annotations instead of parameters
 533  2
                 String expression = PluginUtils.MAVEN_COMPONENTS.get( role );
 534  
 
 535  2
                 if ( expression == null )
 536  
                 {
 537  
                     // normal component
 538  2
                     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  0
                     pd.setDefaultValue( expression );
 544  0
                     pd.setType( role );
 545  0
                     pd.setRequired( true );
 546  
                 }
 547  
 
 548  2
                 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  2
             }
 553  
             else
 554  
             {
 555  
                 // Parameter tag
 556  6
                 DocletTag parameter = field.getTagByName( JavaMojoAnnotation.PARAMETER );
 557  
 
 558  6
                 pd.setRequired( field.getTagByName( JavaMojoAnnotation.REQUIRED ) != null );
 559  
 
 560  6
                 pd.setEditable( field.getTagByName( JavaMojoAnnotation.READONLY ) == null );
 561  
 
 562  6
                 String alias = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_ALIAS );
 563  
 
 564  6
                 if ( !StringUtils.isEmpty( alias ) )
 565  
                 {
 566  1
                     pd.setAlias( alias );
 567  
                 }
 568  
 
 569  6
                 String expression = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_EXPRESSION );
 570  6
                 String property = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_PROPERTY );
 571  
 
 572  6
                 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
 573  
                 {
 574  0
                     getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 575  0
                     getLogger().error( "  Cannot use both:" );
 576  0
                     getLogger().error( "    @parameter expression=\"${property}\"" );
 577  0
                     getLogger().error( "  and" );
 578  0
                     getLogger().error( "    @parameter property=\"property\"" );
 579  0
                     getLogger().error( "  Second syntax is preferred." );
 580  0
                     throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
 581  
                         + ": cannot" + " use both @parameter expression and property", null );
 582  
                 }
 583  
 
 584  6
                 if ( StringUtils.isNotEmpty( expression ) )
 585  
                 {
 586  1
                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 587  1
                     getLogger().warn( "  The syntax" );
 588  1
                     getLogger().warn( "    @parameter expression=\"${property}\"" );
 589  1
                     getLogger().warn( "  is deprecated, please use" );
 590  1
                     getLogger().warn( "    @parameter property=\"property\"" );
 591  1
                     getLogger().warn( "  instead." );
 592  
 
 593  
                 }
 594  5
                 else if ( StringUtils.isNotEmpty( property ) )
 595  
                 {
 596  1
                     expression = "${" + property + "}";
 597  
                 }
 598  
 
 599  6
                 pd.setExpression( expression );
 600  
 
 601  6
                 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
 602  
                 {
 603  0
                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
 604  0
                     getLogger().warn( "  The syntax" );
 605  0
                     getLogger().warn( "    @parameter expression=\"${component.<role>#<roleHint>}\"" );
 606  0
                     getLogger().warn( "  is deprecated, please use" );
 607  0
                     getLogger().warn( "    @component role=\"<role>\" roleHint=\"<roleHint>\"" );
 608  0
                     getLogger().warn( "  instead." );
 609  
                 }
 610  
 
 611  6
                 if ( "${reports}".equals( pd.getExpression() ) )
 612  
                 {
 613  0
                     mojoDescriptor.setRequiresReports( true );
 614  
                 }
 615  
 
 616  6
                 pd.setDefaultValue( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
 617  
 
 618  6
                 pd.setImplementation( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
 619  
             }
 620  
 
 621  8
             mojoDescriptor.addParameter( pd );
 622  8
         }
 623  5
     }
 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  15
         JavaClass superClass = javaClass.getSuperJavaClass();
 638  
 
 639  15
         if ( superClass != null )
 640  
         {
 641  10
             rawParams = extractFieldParameterTags( superClass );
 642  
         }
 643  
         else
 644  
         {
 645  5
             rawParams = new TreeMap<String, JavaField>();
 646  
         }
 647  
 
 648  15
         JavaField[] classFields = javaClass.getFields();
 649  
 
 650  15
         if ( classFields != null )
 651  
         {
 652  35
             for ( JavaField field : classFields )
 653  
             {
 654  20
                 if ( field.getTagByName( JavaMojoAnnotation.PARAMETER ) != null
 655  
                     || field.getTagByName( JavaMojoAnnotation.COMPONENT ) != null )
 656  
                 {
 657  8
                     rawParams.put( field.getName(), field );
 658  
                 }
 659  
             }
 660  
         }
 661  15
         return rawParams;
 662  
     }
 663  
 
 664  
     /** {@inheritDoc} */
 665  
     public List<MojoDescriptor> execute( MavenProject project, PluginDescriptor pluginDescriptor )
 666  
         throws ExtractionException, InvalidPluginDescriptorException
 667  
     {
 668  0
         return execute( new DefaultPluginToolsRequest( project, pluginDescriptor ) );
 669  
     }
 670  
     
 671  
     /** {@inheritDoc} */
 672  
     public List<MojoDescriptor> execute( PluginToolsRequest request )
 673  
         throws ExtractionException, InvalidPluginDescriptorException
 674  
     {
 675  5
         JavaClass[] javaClasses = discoverClasses( request );
 676  
 
 677  5
         List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
 678  
 
 679  16
         for ( JavaClass javaClass : javaClasses )
 680  
         {
 681  11
             DocletTag tag = javaClass.getTagByName( GOAL );
 682  
 
 683  11
             if ( tag != null )
 684  
             {
 685  5
                 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
 686  5
                 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
 687  
 
 688  
                 // Validate the descriptor as best we can before allowing it to be processed.
 689  5
                 validate( mojoDescriptor );
 690  
 
 691  5
                 descriptors.add( mojoDescriptor );
 692  
             }
 693  
         }
 694  
 
 695  5
         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  5
         JavaDocBuilder builder = new JavaDocBuilder();
 706  5
         builder.setEncoding( request.getEncoding() );
 707  
         
 708  5
         MavenProject project = request.getProject();
 709  
 
 710  5
         for ( String source : (List<String>) project.getCompileSourceRoots() )
 711  
         {
 712  5
             builder.addSourceTree( new File( source ) );
 713  
         }
 714  
 
 715  
         // TODO be more dynamic
 716  5
         File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
 717  5
         if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
 718  
         {
 719  5
             builder.addSourceTree( generatedPlugin );
 720  
         }
 721  
 
 722  5
         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  5
         List<Parameter> parameters = mojoDescriptor.getParameters();
 734  
 
 735  5
         if ( parameters != null )
 736  
         {
 737  12
             for ( int j = 0; j < parameters.size(); j++ )
 738  
             {
 739  8
                 validateParameter( parameters.get( j ), j );
 740  
             }
 741  
         }
 742  5
     }
 743  
 }