001 package org.apache.maven.tools.plugin.extractor.java;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import com.thoughtworks.qdox.JavaDocBuilder;
023 import com.thoughtworks.qdox.model.DocletTag;
024 import com.thoughtworks.qdox.model.JavaClass;
025 import com.thoughtworks.qdox.model.JavaField;
026 import com.thoughtworks.qdox.model.Type;
027
028 import org.apache.maven.plugin.descriptor.InvalidParameterException;
029 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
030 import org.apache.maven.plugin.descriptor.MojoDescriptor;
031 import org.apache.maven.plugin.descriptor.Parameter;
032 import org.apache.maven.plugin.descriptor.PluginDescriptor;
033 import org.apache.maven.plugin.descriptor.Requirement;
034 import org.apache.maven.project.MavenProject;
035 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
036 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
037 import org.apache.maven.tools.plugin.PluginToolsRequest;
038 import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
039 import org.apache.maven.tools.plugin.extractor.ExtractionException;
040 import org.apache.maven.tools.plugin.util.PluginUtils;
041
042 import org.codehaus.plexus.component.annotations.Component;
043 import org.codehaus.plexus.logging.AbstractLogEnabled;
044 import org.codehaus.plexus.util.StringUtils;
045
046 import java.io.File;
047 import java.util.ArrayList;
048 import java.util.List;
049 import java.util.Map;
050 import java.util.TreeMap;
051
052 /**
053 * Extracts Mojo descriptors from <a href="http://java.sun.com/">Java</a> sources.
054 * <br/>
055 * For more information about the usage tag, have a look to:
056 * <a href="http://maven.apache.org/developers/mojo-api-specification.html">
057 * http://maven.apache.org/developers/mojo-api-specification.html</a>
058 *
059 * @todo need to add validation directives so that systems embedding maven2 can
060 * get validation directives to help users in IDEs.
061 * @version $Id: JavaMojoDescriptorExtractor.java 1353231 2012-06-24 08:36:58Z hboutemy $
062 * @see org.apache.maven.plugin.descriptor.MojoDescriptor
063 */
064 @Component( role = MojoDescriptorExtractor.class, hint = "java" )
065 public class JavaMojoDescriptorExtractor
066 extends AbstractLogEnabled
067 implements MojoDescriptorExtractor, JavaMojoAnnotation
068 {
069 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#INSTANTIATION_STRATEGY} instead of. */
070 public static final String MAVEN_PLUGIN_INSTANTIATION = JavaMojoAnnotation.INSTANTIATION_STRATEGY;
071
072 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#CONFIGURATOR} instead of. */
073 public static final String CONFIGURATOR = JavaMojoAnnotation.CONFIGURATOR;
074
075 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER} instead of. */
076 public static final String PARAMETER = JavaMojoAnnotation.PARAMETER;
077
078 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_EXPRESSION} instead of. */
079 public static final String PARAMETER_EXPRESSION = JavaMojoAnnotation.PARAMETER_EXPRESSION;
080
081 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_DEFAULT_VALUE} instead of. */
082 public static final String PARAMETER_DEFAULT_VALUE = JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE;
083
084 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_ALIAS} instead of. */
085 public static final String PARAMETER_ALIAS = JavaMojoAnnotation.PARAMETER_ALIAS;
086
087 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#SINCE} instead of. */
088 public static final String SINCE = JavaMojoAnnotation.SINCE;
089
090 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#PARAMETER_IMPLEMENTATION} instead of. */
091 public static final String PARAMETER_IMPLEMENTATION = JavaMojoAnnotation.PARAMETER_IMPLEMENTATION;
092
093 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#REQUIRED} instead of. */
094 public static final String REQUIRED = JavaMojoAnnotation.REQUIRED;
095
096 /** @deprecated since 2.4, use {@link JavaMojoAnnotation#DEPRECATED} instead of. */
097 public static final String DEPRECATED = JavaMojoAnnotation.DEPRECATED;
098
099 /** @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 String name = parameter.getName();
166
167 if ( name == null )
168 {
169 throw new InvalidParameterException( "name", i );
170 }
171
172 // TODO: remove when backward compatibility is no longer an issue.
173 String type = parameter.getType();
174
175 if ( type == null )
176 {
177 throw new InvalidParameterException( "type", i );
178 }
179
180 // TODO: remove when backward compatibility is no longer an issue.
181 String description = parameter.getDescription();
182
183 if ( description == null )
184 {
185 throw new InvalidParameterException( "description", i );
186 }
187 }
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 ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor();
202 mojoDescriptor.setLanguage( "java" );
203 mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
204 mojoDescriptor.setDescription( javaClass.getComment() );
205
206 // ----------------------------------------------------------------------
207 // Mojo annotations in alphabetical order
208 // ----------------------------------------------------------------------
209
210 // Aggregator flag
211 DocletTag aggregator = findInClassHierarchy( javaClass, JavaMojoAnnotation.AGGREGATOR );
212 if ( aggregator != null )
213 {
214 mojoDescriptor.setAggregator( true );
215 }
216
217 // Configurator hint
218 DocletTag configurator = findInClassHierarchy( javaClass, JavaMojoAnnotation.CONFIGURATOR );
219 if ( configurator != null )
220 {
221 mojoDescriptor.setComponentConfigurator( configurator.getValue() );
222 }
223
224 // Additional phase to execute first
225 DocletTag execute = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTE );
226 if ( execute != null )
227 {
228 String executePhase = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_PHASE );
229 String executeGoal = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_GOAL );
230
231 if ( executePhase == null && executeGoal == null )
232 {
233 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
234 + ": @execute tag requires either a 'phase' or 'goal' parameter" );
235 }
236 else if ( executePhase != null && executeGoal != null )
237 {
238 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
239 + ": @execute tag can have only one of a 'phase' or 'goal' parameter" );
240 }
241 mojoDescriptor.setExecutePhase( executePhase );
242 mojoDescriptor.setExecuteGoal( executeGoal );
243
244 String lifecycle = execute.getNamedParameter( JavaMojoAnnotation.EXECUTE_LIFECYCLE );
245 if ( lifecycle != null )
246 {
247 mojoDescriptor.setExecuteLifecycle( lifecycle );
248 if ( mojoDescriptor.getExecuteGoal() != null )
249 {
250 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName()
251 + ": @execute lifecycle requires a phase instead of a goal" );
252 }
253 }
254 }
255
256 // Goal name
257 DocletTag goal = findInClassHierarchy( javaClass, JavaMojoAnnotation.GOAL );
258 if ( goal != null )
259 {
260 mojoDescriptor.setGoal( goal.getValue() );
261 }
262
263 // inheritByDefault flag
264 boolean value =
265 getBooleanTagValue( javaClass, JavaMojoAnnotation.INHERIT_BY_DEFAULT,
266 mojoDescriptor.isInheritedByDefault() );
267 mojoDescriptor.setInheritedByDefault( value );
268
269 // instantiationStrategy
270 DocletTag tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.INSTANTIATION_STRATEGY );
271 if ( tag != null )
272 {
273 mojoDescriptor.setInstantiationStrategy( tag.getValue() );
274 }
275
276 // executionStrategy (and deprecated @attainAlways)
277 tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY );
278 if ( tag != null )
279 {
280 getLogger().warn( "@" + JavaMojoAnnotation.MULTI_EXECUTION_STRATEGY + " in "
281 + javaClass.getFullyQualifiedName() + " is deprecated: please use '@"
282 + JavaMojoAnnotation.EXECUTION_STATEGY + " always' instead." );
283 mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
284 }
285 else
286 {
287 mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
288 }
289 tag = findInClassHierarchy( javaClass, JavaMojoAnnotation.EXECUTION_STATEGY );
290 if ( tag != null )
291 {
292 mojoDescriptor.setExecutionStrategy( tag.getValue() );
293 }
294
295 // Phase name
296 DocletTag phase = findInClassHierarchy( javaClass, JavaMojoAnnotation.PHASE );
297 if ( phase != null )
298 {
299 mojoDescriptor.setPhase( phase.getValue() );
300 }
301
302 // Dependency resolution flag
303 DocletTag requiresDependencyResolution =
304 findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION );
305 if ( requiresDependencyResolution != null )
306 {
307 String v = requiresDependencyResolution.getValue();
308
309 if ( StringUtils.isEmpty( v ) )
310 {
311 v = "runtime";
312 }
313
314 mojoDescriptor.setDependencyResolutionRequired( v );
315 }
316
317 // Dependency collection flag
318 DocletTag requiresDependencyCollection =
319 findInClassHierarchy( javaClass, JavaMojoAnnotation.REQUIRES_DEPENDENCY_COLLECTION );
320 if ( requiresDependencyCollection != null )
321 {
322 String v = requiresDependencyCollection.getValue();
323
324 if ( StringUtils.isEmpty( v ) )
325 {
326 v = "runtime";
327 }
328
329 mojoDescriptor.setDependencyCollectionRequired( v );
330 }
331
332 // requiresDirectInvocation flag
333 value =
334 getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_DIRECT_INVOCATION,
335 mojoDescriptor.isDirectInvocationOnly() );
336 mojoDescriptor.setDirectInvocationOnly( value );
337
338 // Online flag
339 value =
340 getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
341 mojoDescriptor.setOnlineRequired( value );
342
343 // Project flag
344 value =
345 getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
346 mojoDescriptor.setProjectRequired( value );
347
348 // requiresReports flag
349 value =
350 getBooleanTagValue( javaClass, JavaMojoAnnotation.REQUIRES_REPORTS, mojoDescriptor.isRequiresReports() );
351 mojoDescriptor.setRequiresReports( value );
352
353 // ----------------------------------------------------------------------
354 // Javadoc annotations in alphabetical order
355 // ----------------------------------------------------------------------
356
357 // Deprecation hint
358 DocletTag deprecated = javaClass.getTagByName( JavaMojoAnnotation.DEPRECATED );
359 if ( deprecated != null )
360 {
361 mojoDescriptor.setDeprecated( deprecated.getValue() );
362 }
363
364 // What version it was introduced in
365 DocletTag since = findInClassHierarchy( javaClass, JavaMojoAnnotation.SINCE );
366 if ( since != null )
367 {
368 mojoDescriptor.setSince( since.getValue() );
369 }
370
371 // Thread-safe mojo
372
373 value = getBooleanTagValue( javaClass, JavaMojoAnnotation.THREAD_SAFE, true, mojoDescriptor.isThreadSafe() );
374 mojoDescriptor.setThreadSafe( value );
375
376 extractParameters( mojoDescriptor, javaClass );
377
378 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 DocletTag tag = findInClassHierarchy( javaClass, tagName );
391
392 if ( tag != null )
393 {
394 String value = tag.getValue();
395
396 if ( StringUtils.isNotEmpty( value ) )
397 {
398 defaultValue = Boolean.valueOf( value ).booleanValue();
399 }
400 }
401 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 DocletTag tag = findInClassHierarchy( javaClass, tagName );
416
417 if ( tag != null )
418 {
419 String value = tag.getValue();
420
421 if ( StringUtils.isNotEmpty( value ) )
422 {
423 return Boolean.valueOf( value ).booleanValue();
424 }
425 else
426 {
427 return defaultForTag;
428 }
429 }
430 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 DocletTag tag = javaClass.getTagByName( tagName );
441
442 if ( tag == null )
443 {
444 JavaClass superClass = javaClass.getSuperJavaClass();
445
446 if ( superClass != null )
447 {
448 tag = findInClassHierarchy( superClass, tagName );
449 }
450 }
451
452 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 Map<String, JavaField> rawParams = extractFieldParameterTags( javaClass );
468
469 for ( Map.Entry<String, JavaField> entry : rawParams.entrySet() )
470 {
471 JavaField field = entry.getValue();
472
473 Type type = field.getType();
474
475 Parameter pd = new Parameter();
476
477 pd.setName( entry.getKey() );
478
479 if ( !type.isArray() )
480 {
481 pd.setType( type.getValue() );
482 }
483 else
484 {
485 StringBuilder value = new StringBuilder( type.getValue() );
486
487 int remaining = type.getDimensions();
488
489 while ( remaining-- > 0 )
490 {
491 value.append( "[]" );
492 }
493
494 pd.setType( value.toString() );
495 }
496
497 pd.setDescription( field.getComment() );
498
499 DocletTag deprecationTag = field.getTagByName( JavaMojoAnnotation.DEPRECATED );
500
501 if ( deprecationTag != null )
502 {
503 pd.setDeprecated( deprecationTag.getValue() );
504 }
505
506 DocletTag sinceTag = field.getTagByName( JavaMojoAnnotation.SINCE );
507 if ( sinceTag != null )
508 {
509 pd.setSince( sinceTag.getValue() );
510 }
511
512 DocletTag componentTag = field.getTagByName( JavaMojoAnnotation.COMPONENT );
513
514 if ( componentTag != null )
515 {
516 // Component tag
517 String role = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLE );
518
519 if ( role == null )
520 {
521 role = field.getType().toString();
522 }
523
524 String roleHint = componentTag.getNamedParameter( JavaMojoAnnotation.COMPONENT_ROLEHINT );
525
526 if ( roleHint == null )
527 {
528 // support alternate syntax for better compatibility with the Plexus CDC.
529 roleHint = componentTag.getNamedParameter( "role-hint" );
530 }
531
532 // recognize Maven-injected objects as components annotations instead of parameters
533 String expression = PluginUtils.MAVEN_COMPONENTS.get( role );
534
535 if ( expression == null )
536 {
537 // normal component
538 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 pd.setDefaultValue( expression );
544 pd.setType( role );
545 pd.setRequired( true );
546 }
547
548 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 }
553 else
554 {
555 // Parameter tag
556 DocletTag parameter = field.getTagByName( JavaMojoAnnotation.PARAMETER );
557
558 pd.setRequired( field.getTagByName( JavaMojoAnnotation.REQUIRED ) != null );
559
560 pd.setEditable( field.getTagByName( JavaMojoAnnotation.READONLY ) == null );
561
562 String alias = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_ALIAS );
563
564 if ( !StringUtils.isEmpty( alias ) )
565 {
566 pd.setAlias( alias );
567 }
568
569 String expression = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_EXPRESSION );
570 String property = parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_PROPERTY );
571
572 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) )
573 {
574 getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
575 getLogger().error( " Cannot use both:" );
576 getLogger().error( " @parameter expression=\"${property}\"" );
577 getLogger().error( " and" );
578 getLogger().error( " @parameter property=\"property\"" );
579 getLogger().error( " Second syntax is preferred." );
580 throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName()
581 + ": cannot" + " use both @parameter expression and property", null );
582 }
583
584 if ( StringUtils.isNotEmpty( expression ) )
585 {
586 getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
587 getLogger().warn( " The syntax" );
588 getLogger().warn( " @parameter expression=\"${property}\"" );
589 getLogger().warn( " is deprecated, please use" );
590 getLogger().warn( " @parameter property=\"property\"" );
591 getLogger().warn( " instead." );
592
593 }
594 else if ( StringUtils.isNotEmpty( property ) )
595 {
596 expression = "${" + property + "}";
597 }
598
599 pd.setExpression( expression );
600
601 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
602 {
603 getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" );
604 getLogger().warn( " The syntax" );
605 getLogger().warn( " @parameter expression=\"${component.<role>#<roleHint>}\"" );
606 getLogger().warn( " is deprecated, please use" );
607 getLogger().warn( " @component role=\"<role>\" roleHint=\"<roleHint>\"" );
608 getLogger().warn( " instead." );
609 }
610
611 if ( "${reports}".equals( pd.getExpression() ) )
612 {
613 mojoDescriptor.setRequiresReports( true );
614 }
615
616 pd.setDefaultValue( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_DEFAULT_VALUE ) );
617
618 pd.setImplementation( parameter.getNamedParameter( JavaMojoAnnotation.PARAMETER_IMPLEMENTATION ) );
619 }
620
621 mojoDescriptor.addParameter( pd );
622 }
623 }
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 JavaClass superClass = javaClass.getSuperJavaClass();
638
639 if ( superClass != null )
640 {
641 rawParams = extractFieldParameterTags( superClass );
642 }
643 else
644 {
645 rawParams = new TreeMap<String, JavaField>();
646 }
647
648 JavaField[] classFields = javaClass.getFields();
649
650 if ( classFields != null )
651 {
652 for ( JavaField field : classFields )
653 {
654 if ( field.getTagByName( JavaMojoAnnotation.PARAMETER ) != null
655 || field.getTagByName( JavaMojoAnnotation.COMPONENT ) != null )
656 {
657 rawParams.put( field.getName(), field );
658 }
659 }
660 }
661 return rawParams;
662 }
663
664 /** {@inheritDoc} */
665 public List<MojoDescriptor> execute( MavenProject project, PluginDescriptor pluginDescriptor )
666 throws ExtractionException, InvalidPluginDescriptorException
667 {
668 return execute( new DefaultPluginToolsRequest( project, pluginDescriptor ) );
669 }
670
671 /** {@inheritDoc} */
672 public List<MojoDescriptor> execute( PluginToolsRequest request )
673 throws ExtractionException, InvalidPluginDescriptorException
674 {
675 JavaClass[] javaClasses = discoverClasses( request );
676
677 List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>();
678
679 for ( JavaClass javaClass : javaClasses )
680 {
681 DocletTag tag = javaClass.getTagByName( GOAL );
682
683 if ( tag != null )
684 {
685 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass );
686 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() );
687
688 // Validate the descriptor as best we can before allowing it to be processed.
689 validate( mojoDescriptor );
690
691 descriptors.add( mojoDescriptor );
692 }
693 }
694
695 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 JavaDocBuilder builder = new JavaDocBuilder();
706 builder.setEncoding( request.getEncoding() );
707
708 MavenProject project = request.getProject();
709
710 for ( String source : (List<String>) project.getCompileSourceRoots() )
711 {
712 builder.addSourceTree( new File( source ) );
713 }
714
715 // TODO be more dynamic
716 File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
717 if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) )
718 {
719 builder.addSourceTree( generatedPlugin );
720 }
721
722 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 List<Parameter> parameters = mojoDescriptor.getParameters();
734
735 if ( parameters != null )
736 {
737 for ( int j = 0; j < parameters.size(); j++ )
738 {
739 validateParameter( parameters.get( j ), j );
740 }
741 }
742 }
743 }