Coverage Report - org.apache.maven.tools.plugin.extractor.java.JavaMojoDescriptorExtractor
 
Classes in this File Line Coverage Branch Coverage Complexity
JavaMojoDescriptorExtractor
74% 
83% 
6.625
 
 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.JavaSource;
 27  
 import com.thoughtworks.qdox.model.Type;
 28  
 
 29  
 import org.apache.maven.plugin.descriptor.InvalidParameterException;
 30  
 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
 31  
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 32  
 import org.apache.maven.plugin.descriptor.Parameter;
 33  
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 34  
 import org.apache.maven.plugin.descriptor.Requirement;
 35  
 import org.apache.maven.project.MavenProject;
 36  
 import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
 37  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 38  
 import org.codehaus.plexus.util.StringUtils;
 39  
 
 40  
 import java.io.File;
 41  
 import java.util.ArrayList;
 42  
 import java.util.Iterator;
 43  
 import java.util.List;
 44  
 import java.util.Map;
 45  
 import java.util.TreeMap;
 46  
 
 47  
 
 48  
 /**
 49  
  * @todo add example usage tag that can be shown in the doco
 50  
  * @todo need to add validation directives so that systems embedding maven2 can
 51  
  * get validation directives to help users in IDEs.
 52  
  */
 53  1
 public class JavaMojoDescriptorExtractor
 54  
     extends AbstractLogEnabled
 55  
     implements MojoDescriptorExtractor
 56  
 {
 57  
     public static final String MAVEN_PLUGIN_INSTANTIATION = "instantiationStrategy";
 58  
 
 59  
     public static final String CONFIGURATOR = "configurator";
 60  
 
 61  
     public static final String PARAMETER = "parameter";
 62  
 
 63  
     public static final String PARAMETER_EXPRESSION = "expression";
 64  
 
 65  
     public static final String PARAMETER_DEFAULT_VALUE = "default-value";
 66  
 
 67  
     public static final String PARAMETER_ALIAS = "alias";
 68  
 
 69  
     public static final String SINCE = "since";
 70  
 
 71  
     /**
 72  
      * This indicates the base name of the bean properties used to read/write this parameter's value.
 73  
      * So:
 74  
      *
 75  
      * @parameter property="project"
 76  
      * <p/>
 77  
      * Would say there is a getProject() method and a setProject(Project) method. Here the field
 78  
      * name would not be the basis for the parameter's name. This mode of operation will allow the
 79  
      * mojos to be usable as beans and will be the promoted form of use.
 80  
      */
 81  
     public static final String PARAMETER_PROPERTY = "property";
 82  
 
 83  
     public static final String REQUIRED = "required";
 84  
 
 85  
     public static final String DEPRECATED = "deprecated";
 86  
 
 87  
     public static final String READONLY = "readonly";
 88  
 
 89  
     public static final String GOAL = "goal";
 90  
 
 91  
     public static final String PHASE = "phase";
 92  
 
 93  
     public static final String EXECUTE = "execute";
 94  
 
 95  
     public static final String EXECUTE_LIFECYCLE = "lifecycle";
 96  
 
 97  
     public static final String EXECUTE_PHASE = "phase";
 98  
 
 99  
     public static final String EXECUTE_GOAL = "goal";
 100  
 
 101  
     public static final String GOAL_DESCRIPTION = "description";
 102  
 
 103  
     public static final String GOAL_REQUIRES_DEPENDENCY_RESOLUTION = "requiresDependencyResolution";
 104  
 
 105  
     public static final String GOAL_REQUIRES_PROJECT = "requiresProject";
 106  
 
 107  
     public static final String GOAL_REQUIRES_REPORTS = "requiresReports";
 108  
 
 109  
     public static final String GOAL_IS_AGGREGATOR = "aggregator";
 110  
 
 111  
     public static final String GOAL_REQUIRES_ONLINE = "requiresOnline";
 112  
 
 113  
     public static final String GOAL_INHERIT_BY_DEFAULT = "inheritByDefault";
 114  
 
 115  
     public static final String GOAL_MULTI_EXECUTION_STRATEGY = "attainAlways";
 116  
 
 117  
     public static final String GOAL_REQUIRES_DIRECT_INVOCATION = "requiresDirectInvocation";
 118  
 
 119  
     private static final String COMPONENT = "component";
 120  
 
 121  
     private static final String COMPONENT_ROLE = "role";
 122  
 
 123  
     private static final String COMPONENT_ROLEHINT = "roleHint";
 124  
 
 125  
     protected void validateParameter( Parameter parameter, int i )
 126  
         throws InvalidParameterException
 127  
     {
 128  
         // TODO: remove when backward compatibility is no longer an issue.
 129  2
         String name = parameter.getName();
 130  
 
 131  2
         if ( name == null )
 132  
         {
 133  0
             throw new InvalidParameterException( "name", i );
 134  
         }
 135  
 
 136  
         // TODO: remove when backward compatibility is no longer an issue.
 137  2
         String type = parameter.getType();
 138  
 
 139  2
         if ( type == null )
 140  
         {
 141  0
             throw new InvalidParameterException( "type", i );
 142  
         }
 143  
 
 144  
         // TODO: remove when backward compatibility is no longer an issue.
 145  2
         String description = parameter.getDescription();
 146  
 
 147  2
         if ( description == null )
 148  
         {
 149  0
             throw new InvalidParameterException( "description", i );
 150  
         }
 151  2
     }
 152  
 
 153  
     // ----------------------------------------------------------------------
 154  
     // Mojo descriptor creation from @tags
 155  
     // ----------------------------------------------------------------------
 156  
 
 157  
     private MojoDescriptor createMojoDescriptor( JavaSource javaSource, PluginDescriptor pluginDescriptor )
 158  
         throws InvalidPluginDescriptorException
 159  
     {
 160  2
         MojoDescriptor mojoDescriptor = new MojoDescriptor();
 161  
 
 162  2
         mojoDescriptor.setPluginDescriptor( pluginDescriptor );
 163  
 
 164  2
         JavaClass javaClass = getJavaClass( javaSource );
 165  
 
 166  2
         mojoDescriptor.setLanguage( "java" );
 167  
 
 168  2
         mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
 169  
 
 170  2
         mojoDescriptor.setDescription( javaClass.getComment() );
 171  
 
 172  2
         DocletTag tag = findInClassHierarchy( javaClass, MAVEN_PLUGIN_INSTANTIATION );
 173  
 
 174  2
         if ( tag != null )
 175  
         {
 176  0
             mojoDescriptor.setInstantiationStrategy( tag.getValue() );
 177  
         }
 178  
 
 179  2
         tag = findInClassHierarchy( javaClass, GOAL_MULTI_EXECUTION_STRATEGY );
 180  
 
 181  2
         if ( tag != null )
 182  
         {
 183  0
             mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
 184  0
         }
 185  
         else
 186  
         {
 187  2
             mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
 188  
         }
 189  
 
 190  
         // ----------------------------------------------------------------------
 191  
         // Configurator hint
 192  
         // ----------------------------------------------------------------------
 193  
 
 194  2
         DocletTag configurator = findInClassHierarchy( javaClass, CONFIGURATOR );
 195  
 
 196  2
         if ( configurator != null )
 197  
         {
 198  0
             mojoDescriptor.setComponentConfigurator( configurator.getValue() );
 199  
         }
 200  
 
 201  
         // ----------------------------------------------------------------------
 202  
         // Goal name
 203  
         // ----------------------------------------------------------------------
 204  
 
 205  2
         DocletTag goal = findInClassHierarchy( javaClass, GOAL );
 206  
 
 207  2
         if ( goal != null )
 208  
         {
 209  2
             mojoDescriptor.setGoal( goal.getValue() );
 210  
         }
 211  
 
 212  
         // ----------------------------------------------------------------------
 213  
         // Phase name
 214  
         // ----------------------------------------------------------------------
 215  
 
 216  2
         DocletTag phase = findInClassHierarchy( javaClass, PHASE );
 217  
 
 218  2
         if ( phase != null )
 219  
         {
 220  0
             mojoDescriptor.setPhase( phase.getValue() );
 221  
         }
 222  
 
 223  
         // ----------------------------------------------------------------------
 224  
         // Additional phase to execute first
 225  
         // ----------------------------------------------------------------------
 226  
 
 227  2
         DocletTag execute = findInClassHierarchy( javaClass, EXECUTE );
 228  
 
 229  2
         if ( execute != null )
 230  
         {
 231  0
             String executePhase = execute.getNamedParameter( EXECUTE_PHASE );
 232  0
             String executeGoal = execute.getNamedParameter( EXECUTE_GOAL );
 233  
 
 234  0
             if ( executePhase == null && executeGoal == null )
 235  
             {
 236  0
                 throw new InvalidPluginDescriptorException( "@execute tag requires a 'phase' or 'goal' parameter" );
 237  
             }
 238  0
             else if ( executePhase != null && executeGoal != null )
 239  
             {
 240  0
                 throw new InvalidPluginDescriptorException(
 241  
                     "@execute tag can have only one of a 'phase' or 'goal' parameter" );
 242  
             }
 243  0
             mojoDescriptor.setExecutePhase( executePhase );
 244  0
             mojoDescriptor.setExecuteGoal( executeGoal );
 245  
 
 246  0
             String lifecycle = execute.getNamedParameter( EXECUTE_LIFECYCLE );
 247  
 
 248  0
             if ( lifecycle != null )
 249  
             {
 250  0
                 mojoDescriptor.setExecuteLifecycle( lifecycle );
 251  0
                 if ( mojoDescriptor.getExecuteGoal() != null )
 252  
                 {
 253  0
                     throw new InvalidPluginDescriptorException(
 254  
                         "@execute lifecycle requires a phase instead of a goal" );
 255  
                 }
 256  
             }
 257  
         }
 258  
 
 259  
         // ----------------------------------------------------------------------
 260  
         // Dependency resolution flag
 261  
         // ----------------------------------------------------------------------
 262  
 
 263  2
         DocletTag requiresDependencyResolution = findInClassHierarchy( javaClass, GOAL_REQUIRES_DEPENDENCY_RESOLUTION );
 264  
 
 265  2
         if ( requiresDependencyResolution != null )
 266  
         {
 267  2
             String value = requiresDependencyResolution.getValue();
 268  
 
 269  2
             if ( value == null || value.length() == 0 )
 270  
             {
 271  1
                 value = "runtime";
 272  
             }
 273  
 
 274  2
             mojoDescriptor.setDependencyResolutionRequired( value );
 275  
         }
 276  
 
 277  
         // ----------------------------------------------------------------------
 278  
         // Project flag
 279  
         // ----------------------------------------------------------------------
 280  
 
 281  2
         boolean value = getBooleanTagValue( javaClass, GOAL_REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
 282  2
         mojoDescriptor.setProjectRequired( value );
 283  
 
 284  
         // ----------------------------------------------------------------------
 285  
         // Aggregator flag
 286  
         // ----------------------------------------------------------------------
 287  
 
 288  2
         DocletTag aggregator = findInClassHierarchy( javaClass, GOAL_IS_AGGREGATOR );
 289  
 
 290  2
         if ( aggregator != null )
 291  
         {
 292  0
             mojoDescriptor.setAggregator( true );
 293  
         }
 294  
 
 295  
         // ----------------------------------------------------------------------
 296  
         // requiresDirectInvocation flag
 297  
         // ----------------------------------------------------------------------
 298  
 
 299  2
         value =
 300  
             getBooleanTagValue( javaClass, GOAL_REQUIRES_DIRECT_INVOCATION, mojoDescriptor.isDirectInvocationOnly() );
 301  2
         mojoDescriptor.setDirectInvocationOnly( value );
 302  
 
 303  
         // ----------------------------------------------------------------------
 304  
         // Online flag
 305  
         // ----------------------------------------------------------------------
 306  
 
 307  2
         value = getBooleanTagValue( javaClass, GOAL_REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
 308  2
         mojoDescriptor.setOnlineRequired( value );
 309  
 
 310  
         // ----------------------------------------------------------------------
 311  
         // inheritByDefault flag
 312  
         // ----------------------------------------------------------------------
 313  
 
 314  2
         value = getBooleanTagValue( javaClass, GOAL_INHERIT_BY_DEFAULT, mojoDescriptor.isInheritedByDefault() );
 315  2
         mojoDescriptor.setInheritedByDefault( value );
 316  
 
 317  2
         extractParameters( mojoDescriptor, javaClass );
 318  
 
 319  2
         return mojoDescriptor;
 320  
     }
 321  
 
 322  
     private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultValue )
 323  
     {
 324  8
         DocletTag requiresProject = findInClassHierarchy( javaClass, tagName );
 325  
 
 326  8
         if ( requiresProject != null )
 327  
         {
 328  0
             String requiresProjectValue = requiresProject.getValue();
 329  
 
 330  0
             if ( requiresProjectValue != null && requiresProjectValue.length() > 0 )
 331  
             {
 332  0
                 defaultValue = Boolean.valueOf( requiresProjectValue ).booleanValue();
 333  
             }
 334  
         }
 335  8
         return defaultValue;
 336  
     }
 337  
 
 338  
     private static DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
 339  
     {
 340  64
         DocletTag tag = javaClass.getTagByName( tagName );
 341  
 
 342  64
         if ( tag == null )
 343  
         {
 344  60
             JavaClass superClass = javaClass.getSuperJavaClass();
 345  
 
 346  60
             if ( superClass != null )
 347  
             {
 348  40
                 tag = findInClassHierarchy( superClass, tagName );
 349  
             }
 350  
         }
 351  
 
 352  64
         return tag;
 353  
     }
 354  
 
 355  
     private void extractParameters( MojoDescriptor mojoDescriptor, JavaClass javaClass )
 356  
         throws InvalidPluginDescriptorException
 357  
     {
 358  
         // ---------------------------------------------------------------------------------
 359  
         // We're resolving class-level, ancestor-class-field, local-class-field order here.
 360  
         // ---------------------------------------------------------------------------------
 361  
 
 362  2
         Map rawParams = extractFieldParameterTags( javaClass );
 363  
 
 364  2
         for ( Iterator it = rawParams.entrySet().iterator(); it.hasNext(); )
 365  
         {
 366  2
             Map.Entry entry = (Map.Entry) it.next();
 367  
 
 368  2
             JavaField field = (JavaField) entry.getValue();
 369  
 
 370  2
             Type type = field.getType();
 371  
 
 372  2
             Parameter pd = new Parameter();
 373  
 
 374  2
             if ( !type.isArray() )
 375  
             {
 376  0
                 pd.setType( type.getValue() );
 377  0
             }
 378  
             else
 379  
             {
 380  2
                 StringBuffer value = new StringBuffer( type.getValue() );
 381  
 
 382  2
                 int remaining = type.getDimensions();
 383  
 
 384  4
                 while ( remaining-- > 0 )
 385  
                 {
 386  2
                     value.append( "[]" );
 387  2
                 }
 388  
 
 389  2
                 pd.setType( value.toString() );
 390  
             }
 391  
 
 392  2
             pd.setDescription( field.getComment() );
 393  
 
 394  2
             DocletTag componentTag = field.getTagByName( COMPONENT );
 395  
 
 396  2
             if ( componentTag != null )
 397  
             {
 398  0
                 String role = componentTag.getNamedParameter( COMPONENT_ROLE );
 399  
 
 400  0
                 if ( role == null )
 401  
                 {
 402  0
                     role = field.getType().toString();
 403  
                 }
 404  
 
 405  0
                 String roleHint = componentTag.getNamedParameter( COMPONENT_ROLEHINT );
 406  
 
 407  0
                 if ( roleHint == null )
 408  
                 {
 409  
                     // support alternate syntax for better compatibility with the Plexus CDC.
 410  0
                     roleHint = componentTag.getNamedParameter( "role-hint" );
 411  
                 }
 412  
 
 413  0
                 pd.setRequirement( new Requirement( role, roleHint ) );
 414  
 
 415  0
                 pd.setName( (String) entry.getKey() );
 416  0
             }
 417  
             else
 418  
             {
 419  2
                 DocletTag parameter = field.getTagByName( PARAMETER );
 420  
 
 421  
                 // ----------------------------------------------------------------------
 422  
                 // We will look for a property name here first and use that if present
 423  
                 // i.e:
 424  
                 //
 425  
                 // @parameter property="project"
 426  
                 //
 427  
                 // Which will become the name used for the configuration element which
 428  
                 // will in turn will allow plexus to use the corresponding setter.
 429  
                 // ----------------------------------------------------------------------
 430  
 
 431  2
                 String property = parameter.getNamedParameter( PARAMETER_PROPERTY );
 432  
 
 433  2
                 if ( !StringUtils.isEmpty( property ) )
 434  
                 {
 435  0
                     pd.setName( property );
 436  0
                 }
 437  
                 else
 438  
                 {
 439  2
                     pd.setName( (String) entry.getKey() );
 440  
                 }
 441  
 
 442  2
                 pd.setRequired( field.getTagByName( REQUIRED ) != null );
 443  
 
 444  2
                 pd.setEditable( field.getTagByName( READONLY ) == null );
 445  
 
 446  2
                 DocletTag deprecationTag = field.getTagByName( DEPRECATED );
 447  
 
 448  2
                 if ( deprecationTag != null )
 449  
                 {
 450  0
                     pd.setDeprecated( deprecationTag.getValue() );
 451  
                 }
 452  
 
 453  2
                 String alias = parameter.getNamedParameter( PARAMETER_ALIAS );
 454  
 
 455  2
                 if ( !StringUtils.isEmpty( alias ) )
 456  
                 {
 457  0
                     pd.setAlias( alias );
 458  
                 }
 459  
 
 460  2
                 pd.setExpression( parameter.getNamedParameter( PARAMETER_EXPRESSION ) );
 461  
 
 462  2
                 if ( "${reports}".equals( pd.getExpression() ) )
 463  
                 {
 464  0
                     mojoDescriptor.setRequiresReports( true );
 465  
                 }
 466  
 
 467  2
                 pd.setDefaultValue( parameter.getNamedParameter( PARAMETER_DEFAULT_VALUE ) );
 468  
             }
 469  
 
 470  2
             mojoDescriptor.addParameter( pd );
 471  2
         }
 472  2
     }
 473  
 
 474  
     private Map extractFieldParameterTags( JavaClass javaClass )
 475  
     {
 476  
         Map rawParams;
 477  
 
 478  
         // we have to add the parent fields first, so that they will be overwritten by the local fields if
 479  
         // that actually happens...
 480  6
         JavaClass superClass = javaClass.getSuperJavaClass();
 481  
 
 482  6
         if ( superClass != null )
 483  
         {
 484  4
             rawParams = extractFieldParameterTags( superClass );
 485  4
         }
 486  
         else
 487  
         {
 488  2
             rawParams = new TreeMap();
 489  
         }
 490  
 
 491  6
         JavaField[] classFields = javaClass.getFields();
 492  
 
 493  6
         if ( classFields != null )
 494  
         {
 495  8
             for ( int i = 0; i < classFields.length; i++ )
 496  
             {
 497  2
                 JavaField field = classFields[i];
 498  
 
 499  2
                 if ( field.getTagByName( PARAMETER ) != null || field.getTagByName( COMPONENT ) != null )
 500  
                 {
 501  2
                     rawParams.put( field.getName(), field );
 502  
                 }
 503  
             }
 504  
         }
 505  6
         return rawParams;
 506  
     }
 507  
 
 508  
     private JavaClass getJavaClass( JavaSource javaSource )
 509  
     {
 510  4
         return javaSource.getClasses()[0];
 511  
     }
 512  
 
 513  
     public List execute( MavenProject project, PluginDescriptor pluginDescriptor )
 514  
         throws InvalidPluginDescriptorException
 515  
     {
 516  1
         JavaDocBuilder builder = new JavaDocBuilder();
 517  
 
 518  1
         for ( Iterator i = project.getCompileSourceRoots().iterator(); i.hasNext(); )
 519  
         {
 520  1
             builder.addSourceTree( new File( (String) i.next() ) );
 521  1
         }
 522  
 
 523  1
         JavaSource[] javaSources = builder.getSources();
 524  
 
 525  1
         List descriptors = new ArrayList();
 526  
 
 527  3
         for ( int i = 0; i < javaSources.length; i++ )
 528  
         {
 529  2
             JavaClass javaClass = getJavaClass( javaSources[i] );
 530  
 
 531  2
             DocletTag tag = javaClass.getTagByName( GOAL );
 532  
 
 533  2
             if ( tag != null )
 534  
             {
 535  2
                 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaSources[i], pluginDescriptor );
 536  
 
 537  
                 // ----------------------------------------------------------------------
 538  
                 // Validate the descriptor as best we can before allowing it
 539  
                 // to be processed.
 540  
                 // ----------------------------------------------------------------------
 541  
 
 542  2
                 List parameters = mojoDescriptor.getParameters();
 543  
 
 544  2
                 if ( parameters != null )
 545  
                 {
 546  4
                     for ( int j = 0; j < parameters.size(); j++ )
 547  
                     {
 548  2
                         validateParameter( (Parameter) parameters.get( j ), j );
 549  
                     }
 550  
                 }
 551  
 
 552  
                 //                Commented because it causes a VerifyError:
 553  
                 //                java.lang.VerifyError:
 554  
                 //                (class:
 555  
                 // org/apache/maven/tools/plugin/extractor/java/JavaMojoDescriptorExtractor,
 556  
                 //                method: execute signature:
 557  
                 // (Ljava/lang/String;Lorg/apache/maven/project/MavenProject;)Ljava/util/Set;)
 558  
                 //                Incompatible object argument for function call
 559  
                 //
 560  
                 //                Refactored to allow MavenMojoDescriptor.getComponentFactory()
 561  
                 //                return MavenMojoDescriptor.getMojoDescriptor().getLanguage(),
 562  
                 //                and removed all usage of MavenMojoDescriptor from extractors.
 563  
                 //
 564  
                 //
 565  
                 //                MavenMojoDescriptor mmDescriptor = new
 566  
                 // MavenMojoDescriptor(mojoDescriptor);
 567  
                 //
 568  
                 //                JavaClass javaClass = getJavaClass(javaSources[i]);
 569  
                 //
 570  
                 //                mmDescriptor.setImplementation(javaClass.getFullyQualifiedName());
 571  
                 //
 572  
                 //                descriptors.add( mmDescriptor );
 573  
 
 574  2
                 descriptors.add( mojoDescriptor );
 575  
             }
 576  
         }
 577  
 
 578  1
         return descriptors;
 579  
     }
 580  
 
 581  
 }