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