View Javadoc
1   package org.apache.maven.tools.plugin.extractor.javadoc;
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 java.io.File;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.TreeMap;
31  
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.plugin.descriptor.InvalidParameterException;
34  import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
35  import org.apache.maven.plugin.descriptor.MojoDescriptor;
36  import org.apache.maven.plugin.descriptor.Parameter;
37  import org.apache.maven.plugin.descriptor.Requirement;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
40  import org.apache.maven.tools.plugin.PluginToolsRequest;
41  import org.apache.maven.tools.plugin.extractor.ExtractionException;
42  import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
43  import org.apache.maven.tools.plugin.util.PluginUtils;
44  import org.codehaus.plexus.component.annotations.Component;
45  import org.codehaus.plexus.logging.AbstractLogEnabled;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  import com.thoughtworks.qdox.JavaProjectBuilder;
49  import com.thoughtworks.qdox.library.SortedClassLibraryBuilder;
50  import com.thoughtworks.qdox.model.DocletTag;
51  import com.thoughtworks.qdox.model.JavaClass;
52  import com.thoughtworks.qdox.model.JavaField;
53  import com.thoughtworks.qdox.model.JavaType;
54  
55  /**
56   * Extracts Mojo descriptors from <a href="http://java.sun.com/">Java</a> sources.
57   * <br/>
58   * For more information about the usage tag, have a look to:
59   * <a href="http://maven.apache.org/developers/mojo-api-specification.html">
60   * http://maven.apache.org/developers/mojo-api-specification.html</a>;
61   *
62   * @version $Id: JavaJavadocMojoDescriptorExtractor.html 1024032 2018-01-19 18:16:30Z hboutemy $
63   * @see org.apache.maven.plugin.descriptor.MojoDescriptor
64   */
65  @Component( role = MojoDescriptorExtractor.class, hint = "java-javadoc" )
66  public class JavaJavadocMojoDescriptorExtractor
67      extends AbstractLogEnabled
68      implements MojoDescriptorExtractor, JavadocMojoAnnotation
69  {
70      /**
71       * @param parameter not null
72       * @param i positive number
73       * @throws InvalidParameterException if any
74       */
75      protected void validateParameter( Parameter parameter, int i )
76          throws InvalidParameterException
77      {
78          // TODO: remove when backward compatibility is no longer an issue.
79          String name = parameter.getName();
80  
81          if ( name == null )
82          {
83              throw new InvalidParameterException( "name", i );
84          }
85  
86          // TODO: remove when backward compatibility is no longer an issue.
87          String type = parameter.getType();
88  
89          if ( type == null )
90          {
91              throw new InvalidParameterException( "type", i );
92          }
93  
94          // TODO: remove when backward compatibility is no longer an issue.
95          String description = parameter.getDescription();
96  
97          if ( description == null )
98          {
99              throw new InvalidParameterException( "description", i );
100         }
101     }
102 
103     // ----------------------------------------------------------------------
104     // Mojo descriptor creation from @tags
105     // ----------------------------------------------------------------------
106 
107     /**
108      * @param javaClass not null
109      * @return a mojo descriptor
110      * @throws InvalidPluginDescriptorException if any
111      */
112     protected MojoDescriptor createMojoDescriptor( JavaClass javaClass )
113         throws InvalidPluginDescriptorException
114     {
115         ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor();
116         mojoDescriptor.setLanguage( "java" );
117         mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
118         mojoDescriptor.setDescription( javaClass.getComment() );
119 
120         // ----------------------------------------------------------------------
121         // Mojo annotations in alphabetical order
122         // ----------------------------------------------------------------------
123 
124         // Aggregator flag
125         DocletTag aggregator = findInClassHierarchy( javaClass, JavadocMojoAnnotation.AGGREGATOR );
126         if ( aggregator != null )
127         {
128             mojoDescriptor.setAggregator( true );
129         }
130 
131         // Configurator hint
132         DocletTag configurator = findInClassHierarchy( javaClass, JavadocMojoAnnotation.CONFIGURATOR );
133         if ( configurator != null )
134         {
135             mojoDescriptor.setComponentConfigurator( configurator.getValue() );
136         }
137 
138         // Additional phase to execute first
139         DocletTag execute = findInClassHierarchy( javaClass, JavadocMojoAnnotation.EXECUTE );
140         if ( execute != null )
141         {
142             String executePhase = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_PHASE );
143             String executeGoal = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_GOAL );
144 
145             if ( executePhase == null && executeGoal == null )
146             {
147                 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
148                     + ": @execute tag requires either a 'phase' or 'goal' parameter" );
149             }
150             else if ( executePhase != null && executeGoal != null )
151             {
152                 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
153                     + ": @execute tag can have only one of a 'phase' or 'goal' parameter" );
154             }
155             mojoDescriptor.setExecutePhase( executePhase );
156             mojoDescriptor.setExecuteGoal( executeGoal );
157 
158             String lifecycle = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_LIFECYCLE );
159             if ( lifecycle != null )
160             {
161                 mojoDescriptor.setExecuteLifecycle( lifecycle );
162                 if ( mojoDescriptor.getExecuteGoal() != null )
163                 {
164                     throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
165                         + ": @execute lifecycle requires a phase instead of a goal" );
166                 }
167             }
168         }
169 
170         // Goal name
171         DocletTag goal = findInClassHierarchy( javaClass, JavadocMojoAnnotation.GOAL );
172         if ( goal != null )
173         {
174             mojoDescriptor.setGoal( goal.getValue() );
175         }
176 
177         // inheritByDefault flag
178         boolean value =
179             getBooleanTagValue( javaClass, JavadocMojoAnnotation.INHERIT_BY_DEFAULT,
180                                 mojoDescriptor.isInheritedByDefault() );
181         mojoDescriptor.setInheritedByDefault( value );
182 
183         // instantiationStrategy
184         DocletTag tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.INSTANTIATION_STRATEGY );
185         if ( tag != null )
186         {
187             mojoDescriptor.setInstantiationStrategy( tag.getValue() );
188         }
189 
190         // executionStrategy (and deprecated @attainAlways)
191         tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.MULTI_EXECUTION_STRATEGY );
192         if ( tag != null )
193         {
194             getLogger().warn( "@" + JavadocMojoAnnotation.MULTI_EXECUTION_STRATEGY + " in "
195                                   + javaClass.getFullyQualifiedName() + " is deprecated: please use '@"
196                                   + JavadocMojoAnnotation.EXECUTION_STATEGY + " always' instead." );
197             mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
198         }
199         else
200         {
201             mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
202         }
203         tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.EXECUTION_STATEGY );
204         if ( tag != null )
205         {
206             mojoDescriptor.setExecutionStrategy( tag.getValue() );
207         }
208 
209         // Phase name
210         DocletTag phase = findInClassHierarchy( javaClass, JavadocMojoAnnotation.PHASE );
211         if ( phase != null )
212         {
213             mojoDescriptor.setPhase( phase.getValue() );
214         }
215 
216         // Dependency resolution flag
217         DocletTag requiresDependencyResolution =
218             findInClassHierarchy( javaClass, JavadocMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION );
219         if ( requiresDependencyResolution != null )
220         {
221             String v = requiresDependencyResolution.getValue();
222 
223             if ( StringUtils.isEmpty( v ) )
224             {
225                 v = "runtime";
226             }
227 
228             mojoDescriptor.setDependencyResolutionRequired( v );
229         }
230 
231         // Dependency collection flag
232         DocletTag requiresDependencyCollection =
233             findInClassHierarchy( javaClass, JavadocMojoAnnotation.REQUIRES_DEPENDENCY_COLLECTION );
234         if ( requiresDependencyCollection != null )
235         {
236             String v = requiresDependencyCollection.getValue();
237 
238             if ( StringUtils.isEmpty( v ) )
239             {
240                 v = "runtime";
241             }
242 
243             mojoDescriptor.setDependencyCollectionRequired( v );
244         }
245 
246         // requiresDirectInvocation flag
247         value =
248             getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_DIRECT_INVOCATION,
249                                 mojoDescriptor.isDirectInvocationOnly() );
250         mojoDescriptor.setDirectInvocationOnly( value );
251 
252         // Online flag
253         value =
254             getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
255         mojoDescriptor.setOnlineRequired( value );
256 
257         // Project flag
258         value =
259             getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
260         mojoDescriptor.setProjectRequired( value );
261 
262         // requiresReports flag
263         value =
264             getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_REPORTS, mojoDescriptor.isRequiresReports() );
265         mojoDescriptor.setRequiresReports( value );
266 
267         // ----------------------------------------------------------------------
268         // Javadoc annotations in alphabetical order
269         // ----------------------------------------------------------------------
270 
271         // Deprecation hint
272         DocletTag deprecated = javaClass.getTagByName( JavadocMojoAnnotation.DEPRECATED );
273         if ( deprecated != null )
274         {
275             mojoDescriptor.setDeprecated( deprecated.getValue() );
276         }
277 
278         // What version it was introduced in
279         DocletTag since = findInClassHierarchy( javaClass, JavadocMojoAnnotation.SINCE );
280         if ( since != null )
281         {
282             mojoDescriptor.setSince( since.getValue() );
283         }
284 
285         // Thread-safe mojo 
286 
287         value = getBooleanTagValue( javaClass, JavadocMojoAnnotation.THREAD_SAFE, true, mojoDescriptor.isThreadSafe() );
288         mojoDescriptor.setThreadSafe( value );
289 
290         extractParameters( mojoDescriptor, javaClass );
291 
292         return mojoDescriptor;
293     }
294 
295     /**
296      * @param javaClass not null
297      * @param tagName not null
298      * @param defaultValue the wanted default value
299      * @return the boolean value of the given tagName
300      * @see #findInClassHierarchy(JavaClass, String)
301      */
302     private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultValue )
303     {
304         DocletTag tag = findInClassHierarchy( javaClass, tagName );
305 
306         if ( tag != null )
307         {
308             String value = tag.getValue();
309 
310             if ( StringUtils.isNotEmpty( value ) )
311             {
312                 defaultValue = Boolean.valueOf( value ).booleanValue();
313             }
314         }
315         return defaultValue;
316     }
317 
318     /**
319      * @param javaClass     not null
320      * @param tagName       not null
321      * @param defaultForTag The wanted default value when only the tagname is present
322      * @param defaultValue  the wanted default value when the tag is not specified
323      * @return the boolean value of the given tagName
324      * @see #findInClassHierarchy(JavaClass, String)
325      */
326     private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultForTag,
327                                                boolean defaultValue )
328     {
329         DocletTag tag = findInClassHierarchy( javaClass, tagName );
330 
331         if ( tag != null )
332         {
333             String value = tag.getValue();
334 
335             if ( StringUtils.isNotEmpty( value ) )
336             {
337                 return Boolean.valueOf( value ).booleanValue();
338             }
339             else
340             {
341                 return defaultForTag;
342             }
343         }
344         return defaultValue;
345     }
346 
347     /**
348      * @param javaClass not null
349      * @param tagName not null
350      * @return docletTag instance
351      */
352     private static DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
353     {
354         DocletTag tag = javaClass.getTagByName( tagName );
355 
356         if ( tag == null )
357         {
358             JavaClass superClass = javaClass.getSuperJavaClass();
359 
360             if ( superClass != null )
361             {
362                 tag = findInClassHierarchy( superClass, tagName );
363             }
364         }
365 
366         return tag;
367     }
368 
369     /**
370      * @param mojoDescriptor not null
371      * @param javaClass not null
372      * @throws InvalidPluginDescriptorException if any
373      */
374     private void extractParameters( MojoDescriptor mojoDescriptor, JavaClass javaClass )
375         throws InvalidPluginDescriptorException
376     {
377         // ---------------------------------------------------------------------------------
378         // We're resolving class-level, ancestor-class-field, local-class-field order here.
379         // ---------------------------------------------------------------------------------
380 
381         Map<String, JavaField> rawParams = extractFieldParameterTags( javaClass );
382 
383         for ( Map.Entry<String, JavaField> entry : rawParams.entrySet() )
384         {
385             JavaField field = entry.getValue();
386 
387             JavaType type = field.getType();
388 
389             Parameter pd = new Parameter();
390 
391             pd.setName( entry.getKey() );
392 
393             pd.setType( type.getFullyQualifiedName() );
394 
395             pd.setDescription( field.getComment() );
396 
397             DocletTag deprecationTag = field.getTagByName( JavadocMojoAnnotation.DEPRECATED );
398 
399             if ( deprecationTag != null )
400             {
401                 pd.setDeprecated( deprecationTag.getValue() );
402             }
403 
404             DocletTag sinceTag = field.getTagByName( JavadocMojoAnnotation.SINCE );
405             if ( sinceTag != null )
406             {
407                 pd.setSince( sinceTag.getValue() );
408             }
409 
410             DocletTag componentTag = field.getTagByName( JavadocMojoAnnotation.COMPONENT );
411 
412             if ( componentTag != null )
413             {
414                 // Component tag
415                 String role = componentTag.getNamedParameter( JavadocMojoAnnotation.COMPONENT_ROLE );
416 
417                 if ( role == null )
418                 {
419                     role = field.getType().toString();
420                 }
421 
422                 String roleHint = componentTag.getNamedParameter( JavadocMojoAnnotation.COMPONENT_ROLEHINT );
423 
424                 if ( roleHint == null )
425                 {
426                     // support alternate syntax for better compatibility with the Plexus CDC.
427                     roleHint = componentTag.getNamedParameter( "role-hint" );
428                 }
429 
430                 // recognize Maven-injected objects as components annotations instead of parameters
431                 // Note: the expressions we are looking for, i.e. "${project}", are in the values of the Map,
432                 // so the lookup mechanism is different here than in maven-plugin-tools-annotations
433                 boolean isDeprecated = PluginUtils.MAVEN_COMPONENTS.containsValue( role );
434 
435                 if ( !isDeprecated )
436                 {
437                     // normal component
438                     pd.setRequirement( new Requirement( role, roleHint ) );
439                 }
440                 else
441                 {
442                     // not a component but a Maven object to be transformed into an expression/property
443                     getLogger().warn( "Deprecated @component Javadoc tag for '" + pd.getName() + "' field in "
444                                           + javaClass.getFullyQualifiedName()
445                                           + ": replace with @Parameter( defaultValue = \"" + role
446                                           + "\", readonly = true )" );
447                     pd.setDefaultValue( role );
448                     pd.setRequired( true );
449                 }
450 
451                 pd.setEditable( false );
452                 /* TODO: or better like this? Need @component fields be editable for the user?
453                 pd.setEditable( field.getTagByName( READONLY ) == null );
454                 */
455             }
456             else
457             {
458                 // Parameter tag
459                 DocletTag parameter = field.getTagByName( JavadocMojoAnnotation.PARAMETER );
460 
461                 pd.setRequired( field.getTagByName( JavadocMojoAnnotation.REQUIRED ) != null );
462 
463                 pd.setEditable( field.getTagByName( JavadocMojoAnnotation.READONLY ) == null );
464 
465                 String name = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_NAME );
466 
467                 if ( !StringUtils.isEmpty( name ) )
468                 {
469                     pd.setName( name );
470                 }
471 
472                 String alias = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_ALIAS );
473 
474                 if ( !StringUtils.isEmpty( alias ) )
475                 {
476                     pd.setAlias( alias );
477                 }
478 
479                 String expression = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_EXPRESSION );
480                 String property = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_PROPERTY );
481 
482                 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
483                 {
484                     getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
485                     getLogger().error( "  Cannot use both:" );
486                     getLogger().error( "    @parameter expression=\"${property}\"" );
487                     getLogger().error( "  and" );
488                     getLogger().error( "    @parameter property=\"property\"" );
489                     getLogger().error( "  Second syntax is preferred." );
490                     throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
491                         + ": cannot" + " use both @parameter expression and property", null );
492                 }
493 
494                 if ( StringUtils.isNotEmpty( expression ) )
495                 {
496                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
497                     getLogger().warn( "  The syntax" );
498                     getLogger().warn( "    @parameter expression=\"${property}\"" );
499                     getLogger().warn( "  is deprecated, please use" );
500                     getLogger().warn( "    @parameter property=\"property\"" );
501                     getLogger().warn( "  instead." );
502 
503                 }
504                 else if ( StringUtils.isNotEmpty( property ) )
505                 {
506                     expression = "${" + property + "}";
507                 }
508 
509                 pd.setExpression( expression );
510 
511                 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
512                 {
513                     getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
514                     getLogger().warn( "  The syntax" );
515                     getLogger().warn( "    @parameter expression=\"${component.<role>#<roleHint>}\"" );
516                     getLogger().warn( "  is deprecated, please use" );
517                     getLogger().warn( "    @component role=\"<role>\" roleHint=\"<roleHint>\"" );
518                     getLogger().warn( "  instead." );
519                 }
520 
521                 if ( "${reports}".equals( pd.getExpression() ) )
522                 {
523                     mojoDescriptor.setRequiresReports( true );
524                 }
525 
526                 pd.setDefaultValue( parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
527 
528                 pd.setImplementation( parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
529             }
530 
531             mojoDescriptor.addParameter( pd );
532         }
533     }
534 
535     /**
536      * extract fields that are either parameters or components.
537      * 
538      * @param javaClass not null
539      * @return map with Mojo parameters names as keys
540      */
541     private Map<String, JavaField> extractFieldParameterTags( JavaClass javaClass )
542     {
543         Map<String, JavaField> rawParams;
544 
545         // we have to add the parent fields first, so that they will be overwritten by the local fields if
546         // that actually happens...
547         JavaClass superClass = javaClass.getSuperJavaClass();
548 
549         if ( superClass != null )
550         {
551             rawParams = extractFieldParameterTags( superClass );
552         }
553         else
554         {
555             rawParams = new TreeMap<String, JavaField>();
556         }
557 
558         for ( JavaField field : javaClass.getFields() )
559         {
560             if ( field.getTagByName( JavadocMojoAnnotation.PARAMETER ) != null
561                 || field.getTagByName( JavadocMojoAnnotation.COMPONENT ) != null )
562             {
563                 rawParams.put( field.getName(), field );
564             }
565         }
566         return rawParams;
567     }
568 
569     /** {@inheritDoc} */
570     public List<MojoDescriptor> execute( PluginToolsRequest request )
571         throws ExtractionException, InvalidPluginDescriptorException
572     {
573         Collection<JavaClass> javaClasses = discoverClasses( request );
574 
575         List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
576 
577         for ( JavaClass javaClass : javaClasses )
578         {
579             DocletTag tag = javaClass.getTagByName( GOAL );
580 
581             if ( tag != null )
582             {
583                 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
584                 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
585 
586                 // Validate the descriptor as best we can before allowing it to be processed.
587                 validate( mojoDescriptor );
588 
589                 descriptors.add( mojoDescriptor );
590             }
591         }
592 
593         return descriptors;
594     }
595 
596     /**
597      * @param request The plugin request.
598      * @return an array of java class
599      */
600     @SuppressWarnings( "unchecked" )
601     protected Collection<JavaClass> discoverClasses( final PluginToolsRequest request )
602     {
603         JavaProjectBuilder builder = new JavaProjectBuilder( new SortedClassLibraryBuilder() );
604         builder.setEncoding( request.getEncoding() );
605         
606          // Build isolated Classloader with only the artifacts of the project (none of this plugin) 
607         List<URL> urls = new ArrayList<URL>( request.getDependencies().size() );
608         for ( Artifact artifact : request.getDependencies() )
609         {
610             try
611             {
612                 urls.add( artifact.getFile().toURI().toURL() );
613             }
614             catch ( MalformedURLException e )
615             {
616                 // noop
617             }
618         }
619         builder.addClassLoader( new URLClassLoader( urls.toArray( new URL[0] ), ClassLoader.getSystemClassLoader() ) );
620         
621         MavenProject project = request.getProject();
622 
623         for ( String source : (List<String>) project.getCompileSourceRoots() )
624         {
625             builder.addSourceTree( new File( source ) );
626         }
627 
628         // TODO be more dynamic
629         File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
630         if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
631         {
632             builder.addSourceTree( generatedPlugin );
633         }
634 
635         return builder.getClasses();
636     }
637 
638     /**
639      * @param mojoDescriptor not null
640      * @throws InvalidParameterException if any
641      */
642     protected void validate( MojoDescriptor mojoDescriptor )
643         throws InvalidParameterException
644     {
645         @SuppressWarnings( "unchecked" )
646         List<Parameter> parameters = mojoDescriptor.getParameters();
647 
648         if ( parameters != null )
649         {
650             for ( int j = 0; j < parameters.size(); j++ )
651             {
652                 validateParameter( parameters.get( j ), j );
653             }
654         }
655     }
656 }