1 package org.apache.maven.tools.plugin.extractor.annotations;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import javax.inject.Inject;
23 import javax.inject.Named;
24 import javax.inject.Singleton;
25
26 import java.io.File;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.net.URLClassLoader;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 import java.util.stream.Collectors;
43
44 import com.thoughtworks.qdox.JavaProjectBuilder;
45 import com.thoughtworks.qdox.library.SortedClassLibraryBuilder;
46 import com.thoughtworks.qdox.model.DocletTag;
47 import com.thoughtworks.qdox.model.JavaAnnotatedElement;
48 import com.thoughtworks.qdox.model.JavaClass;
49 import com.thoughtworks.qdox.model.JavaField;
50 import com.thoughtworks.qdox.model.JavaMember;
51 import com.thoughtworks.qdox.model.JavaMethod;
52 import org.apache.maven.artifact.Artifact;
53 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
54 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
55 import org.apache.maven.plugin.descriptor.DuplicateParameterException;
56 import org.apache.maven.plugin.descriptor.InvalidParameterException;
57 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
58 import org.apache.maven.plugin.descriptor.MojoDescriptor;
59 import org.apache.maven.plugin.descriptor.PluginDescriptor;
60 import org.apache.maven.plugin.descriptor.Requirement;
61 import org.apache.maven.project.MavenProject;
62 import org.apache.maven.repository.RepositorySystem;
63 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
64 import org.apache.maven.tools.plugin.PluginToolsRequest;
65 import org.apache.maven.tools.plugin.extractor.ExtractionException;
66 import org.apache.maven.tools.plugin.extractor.GroupKey;
67 import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
68 import org.apache.maven.tools.plugin.extractor.annotations.converter.ConverterContext;
69 import org.apache.maven.tools.plugin.extractor.annotations.converter.JavaClassConverterContext;
70 import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocBlockTagsToXhtmlConverter;
71 import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocInlineTagsToXhtmlConverter;
72 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
73 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
74 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
75 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ParameterAnnotationContent;
76 import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotatedClass;
77 import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner;
78 import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScannerRequest;
79 import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
80 import org.apache.maven.tools.plugin.util.PluginUtils;
81 import org.codehaus.plexus.archiver.ArchiverException;
82 import org.codehaus.plexus.archiver.UnArchiver;
83 import org.codehaus.plexus.archiver.manager.ArchiverManager;
84 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
85 import org.codehaus.plexus.logging.AbstractLogEnabled;
86 import org.codehaus.plexus.util.StringUtils;
87
88
89
90
91
92
93
94
95 @Named( JavaAnnotationsMojoDescriptorExtractor.NAME )
96 @Singleton
97 public class JavaAnnotationsMojoDescriptorExtractor
98 extends AbstractLogEnabled
99 implements MojoDescriptorExtractor
100 {
101 public static final String NAME = "java-annotations";
102
103 private static final GroupKey GROUP_KEY = new GroupKey( GroupKey.JAVA_GROUP, 100 );
104
105 @Inject
106 private MojoAnnotationsScanner mojoAnnotationsScanner;
107
108 @Inject
109 private RepositorySystem repositorySystem;
110
111 @Inject
112 private ArchiverManager archiverManager;
113
114 @Inject
115 private JavadocInlineTagsToXhtmlConverter javadocInlineTagsToHtmlConverter;
116
117 @Inject
118 private JavadocBlockTagsToXhtmlConverter javadocBlockTagsToHtmlConverter;
119
120 @Override
121 public String getName()
122 {
123 return NAME;
124 }
125
126 @Override
127 public boolean isDeprecated()
128 {
129 return false;
130 }
131
132 @Override
133 public GroupKey getGroupKey()
134 {
135 return GROUP_KEY;
136 }
137
138 @Override
139 public List<MojoDescriptor> execute( PluginToolsRequest request )
140 throws ExtractionException, InvalidPluginDescriptorException
141 {
142 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = scanAnnotations( request );
143
144 JavaProjectBuilder builder = scanJavadoc( request, mojoAnnotatedClasses.values() );
145 Map<String, JavaClass> javaClassesMap = discoverClasses( builder );
146
147 final JavadocLinkGenerator linkGenerator;
148 if ( request.getInternalJavadocBaseUrl() != null || ( request.getExternalJavadocBaseUrls() != null
149 && !request.getExternalJavadocBaseUrls().isEmpty() ) )
150 {
151 linkGenerator = new JavadocLinkGenerator( request.getInternalJavadocBaseUrl(),
152 request.getInternalJavadocVersion(),
153 request.getExternalJavadocBaseUrls(),
154 request.getSettings() );
155 }
156 else
157 {
158 linkGenerator = null;
159 }
160
161 populateDataFromJavadoc( builder, mojoAnnotatedClasses, javaClassesMap, linkGenerator );
162
163 return toMojoDescriptors( mojoAnnotatedClasses, request.getPluginDescriptor() );
164 }
165
166 private Map<String, MojoAnnotatedClass> scanAnnotations( PluginToolsRequest request )
167 throws ExtractionException
168 {
169 MojoAnnotationsScannerRequest mojoAnnotationsScannerRequest = new MojoAnnotationsScannerRequest();
170
171 File output = new File( request.getProject().getBuild().getOutputDirectory() );
172 mojoAnnotationsScannerRequest.setClassesDirectories( Arrays.asList( output ) );
173
174 mojoAnnotationsScannerRequest.setDependencies( request.getDependencies() );
175
176 mojoAnnotationsScannerRequest.setProject( request.getProject() );
177
178 return mojoAnnotationsScanner.scan( mojoAnnotationsScannerRequest );
179 }
180
181 private JavaProjectBuilder scanJavadoc( PluginToolsRequest request,
182 Collection<MojoAnnotatedClass> mojoAnnotatedClasses )
183 throws ExtractionException
184 {
185
186
187 List<MavenProject> mavenProjects = new ArrayList<>();
188
189
190 Set<Artifact> externalArtifacts = new HashSet<>();
191
192 JavaProjectBuilder builder = new JavaProjectBuilder( new SortedClassLibraryBuilder( ) );
193 builder.setEncoding( request.getEncoding() );
194 extendJavaProjectBuilder( builder, request.getProject() );
195
196 for ( MojoAnnotatedClass mojoAnnotatedClass : mojoAnnotatedClasses )
197 {
198 if ( Objects.equals( mojoAnnotatedClass.getArtifact().getArtifactId(),
199 request.getProject().getArtifact().getArtifactId() ) )
200 {
201 continue;
202 }
203
204 if ( !isMojoAnnnotatedClassCandidate( mojoAnnotatedClass ) )
205 {
206
207 continue;
208 }
209
210 MavenProject mavenProject =
211 getFromProjectReferences( mojoAnnotatedClass.getArtifact(), request.getProject() );
212
213 if ( mavenProject != null )
214 {
215 mavenProjects.add( mavenProject );
216 }
217 else
218 {
219 externalArtifacts.add( mojoAnnotatedClass.getArtifact() );
220 }
221 }
222
223
224 for ( Artifact artifact : externalArtifacts )
225 {
226
227 if ( StringUtils.equalsIgnoreCase( "tests", artifact.getClassifier() ) )
228 {
229 extendJavaProjectBuilderWithSourcesJar( builder, artifact, request, "test-sources" );
230 }
231 else
232 {
233 extendJavaProjectBuilderWithSourcesJar( builder, artifact, request, "sources" );
234 }
235
236 }
237
238 for ( MavenProject mavenProject : mavenProjects )
239 {
240 extendJavaProjectBuilder( builder, mavenProject );
241 }
242
243 return builder;
244 }
245
246 private boolean isMojoAnnnotatedClassCandidate( MojoAnnotatedClass mojoAnnotatedClass )
247 {
248 return mojoAnnotatedClass != null && mojoAnnotatedClass.hasAnnotations();
249 }
250
251
252
253
254 protected void populateDataFromJavadoc( JavaProjectBuilder javaProjectBuilder,
255 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses,
256 Map<String, JavaClass> javaClassesMap,
257 JavadocLinkGenerator linkGenerator )
258 {
259
260 for ( Map.Entry<String, MojoAnnotatedClass> entry : mojoAnnotatedClasses.entrySet() )
261 {
262 JavaClass javaClass = javaClassesMap.get( entry.getKey() );
263 if ( javaClass == null )
264 {
265 continue;
266 }
267
268 MojoAnnotationContent mojoAnnotationContent = entry.getValue().getMojo();
269 if ( mojoAnnotationContent != null )
270 {
271 JavaClassConverterContext context =
272 new JavaClassConverterContext( javaClass, javaProjectBuilder,
273 mojoAnnotatedClasses, linkGenerator,
274 javaClass.getLineNumber() );
275 mojoAnnotationContent.setDescription(
276 getDescriptionFromElement( javaClass, context ) );
277
278 DocletTag since = findInClassHierarchy( javaClass, "since" );
279 if ( since != null )
280 {
281 mojoAnnotationContent.setSince( getRawValueFromTaglet ( since, context ) );
282 }
283
284 DocletTag deprecated = findInClassHierarchy( javaClass, "deprecated" );
285 if ( deprecated != null )
286 {
287 mojoAnnotationContent.setDeprecated( getRawValueFromTaglet ( deprecated, context ) );
288 }
289 }
290
291 Map<String, JavaAnnotatedElement> fieldsMap = extractFieldsAnnotations( javaClass, javaClassesMap );
292 Map<String, JavaAnnotatedElement> methodsMap = extractMethodsAnnotations( javaClass, javaClassesMap );
293
294
295 Map<String, ParameterAnnotationContent> parameters =
296 getParametersParentHierarchy( entry.getValue(), mojoAnnotatedClasses );
297 parameters = new TreeMap<>( parameters );
298 for ( Map.Entry<String, ParameterAnnotationContent> parameter : parameters.entrySet() )
299 {
300 JavaAnnotatedElement element;
301 if ( parameter.getValue().isAnnotationOnMethod() )
302 {
303 element = methodsMap.get( parameter.getKey() );
304 }
305 else
306 {
307 element = fieldsMap.get( parameter.getKey() );
308 }
309
310 if ( element == null )
311 {
312 continue;
313 }
314
315 JavaClassConverterContext context =
316 new JavaClassConverterContext( javaClass, ( (JavaMember) element ).getDeclaringClass(),
317 javaProjectBuilder, mojoAnnotatedClasses,
318 linkGenerator, element.getLineNumber() );
319 ParameterAnnotationContent parameterAnnotationContent = parameter.getValue();
320 parameterAnnotationContent.setDescription(
321 getDescriptionFromElement( element, context ) );
322
323 DocletTag deprecated = element.getTagByName( "deprecated" );
324 if ( deprecated != null )
325 {
326 parameterAnnotationContent.setDeprecated( getRawValueFromTaglet ( deprecated, context ) );
327 }
328
329 DocletTag since = element.getTagByName( "since" );
330 if ( since != null )
331 {
332 parameterAnnotationContent.setSince( getRawValueFromTaglet ( since, context ) );
333 }
334 }
335
336
337 Map<String, ComponentAnnotationContent> components = entry.getValue().getComponents();
338 for ( Map.Entry<String, ComponentAnnotationContent> component : components.entrySet() )
339 {
340 JavaAnnotatedElement element = fieldsMap.get( component.getKey() );
341 if ( element == null )
342 {
343 continue;
344 }
345
346 JavaClassConverterContext context =
347 new JavaClassConverterContext( javaClass, ( (JavaMember) element ).getDeclaringClass(),
348 javaProjectBuilder, mojoAnnotatedClasses,
349 linkGenerator, javaClass.getLineNumber() );
350 ComponentAnnotationContent componentAnnotationContent = component.getValue();
351 componentAnnotationContent.setDescription(
352 getDescriptionFromElement( element, context ) );
353
354 DocletTag deprecated = element.getTagByName( "deprecated" );
355 if ( deprecated != null )
356 {
357 componentAnnotationContent.setDeprecated( getRawValueFromTaglet ( deprecated, context ) );
358 }
359
360 DocletTag since = element.getTagByName( "since" );
361 if ( since != null )
362 {
363 componentAnnotationContent.setSince( getRawValueFromTaglet ( since, context ) );
364 }
365 }
366
367 }
368
369 }
370
371
372
373
374
375
376
377
378 String getDescriptionFromElement( JavaAnnotatedElement element, JavaClassConverterContext context )
379 {
380
381 String comment = element.getComment();
382 if ( comment == null )
383 {
384 return null;
385 }
386 StringBuilder description = new StringBuilder( javadocInlineTagsToHtmlConverter.convert( comment, context ) );
387 for ( DocletTag docletTag : element.getTags() )
388 {
389
390 if ( "see".equals( docletTag.getName() ) )
391 {
392 description.append( javadocBlockTagsToHtmlConverter.convert( docletTag, context ) );
393 }
394 }
395 return description.toString();
396 }
397
398 String getRawValueFromTaglet( DocletTag docletTag, ConverterContext context )
399 {
400
401 return javadocInlineTagsToHtmlConverter.convert( docletTag.getValue(), context );
402 }
403
404
405
406
407
408
409 private DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
410 {
411 try
412 {
413 DocletTag tag = javaClass.getTagByName( tagName );
414
415 if ( tag == null )
416 {
417 JavaClass superClass = javaClass.getSuperJavaClass();
418
419 if ( superClass != null )
420 {
421 tag = findInClassHierarchy( superClass, tagName );
422 }
423 }
424
425 return tag;
426 }
427 catch ( NoClassDefFoundError e )
428 {
429 getLogger().warn( "Failed extracting tag '" + tagName + "' from class " + javaClass );
430 throw e;
431 }
432 }
433
434
435
436
437
438
439
440 private Map<String, JavaAnnotatedElement> extractFieldsAnnotations( JavaClass javaClass,
441 Map<String, JavaClass> javaClassesMap )
442 {
443 try
444 {
445 Map<String, JavaAnnotatedElement> rawParams = new TreeMap<>();
446
447
448
449 JavaClass superClass = javaClass.getSuperJavaClass();
450
451 if ( superClass != null )
452 {
453 if ( !superClass.getFields().isEmpty() )
454 {
455 rawParams = extractFieldsAnnotations( superClass, javaClassesMap );
456 }
457
458 superClass = javaClassesMap.get( superClass.getFullyQualifiedName() );
459 if ( superClass != null && !superClass.getFields().isEmpty() )
460 {
461 rawParams = extractFieldsAnnotations( superClass, javaClassesMap );
462 }
463 }
464 else
465 {
466
467 rawParams = new TreeMap<>();
468 }
469
470 for ( JavaField field : javaClass.getFields() )
471 {
472 rawParams.put( field.getName(), field );
473 }
474
475 return rawParams;
476 }
477 catch ( NoClassDefFoundError e )
478 {
479 getLogger().warn( "Failed extracting parameters from " + javaClass );
480 throw e;
481 }
482 }
483
484
485
486
487
488
489
490 private Map<String, JavaAnnotatedElement> extractMethodsAnnotations( JavaClass javaClass,
491 Map<String, JavaClass> javaClassesMap )
492 {
493 try
494 {
495 Map<String, JavaAnnotatedElement> rawParams = new TreeMap<>();
496
497
498
499 JavaClass superClass = javaClass.getSuperJavaClass();
500
501 if ( superClass != null )
502 {
503 if ( !superClass.getMethods().isEmpty() )
504 {
505 rawParams = extractMethodsAnnotations( superClass, javaClassesMap );
506 }
507
508 superClass = javaClassesMap.get( superClass.getFullyQualifiedName() );
509 if ( superClass != null && !superClass.getMethods().isEmpty() )
510 {
511 rawParams = extractMethodsAnnotations( superClass, javaClassesMap );
512 }
513 }
514 else
515 {
516
517 rawParams = new TreeMap<>();
518 }
519
520 for ( JavaMethod method : javaClass.getMethods() )
521 {
522 if ( isPublicSetterMethod( method ) )
523 {
524 rawParams.put(
525 StringUtils.lowercaseFirstLetter( method.getName().substring( 3 ) ), method );
526 }
527 }
528
529 return rawParams;
530 }
531 catch ( NoClassDefFoundError e )
532 {
533 getLogger().warn( "Failed extracting methods from " + javaClass );
534 throw e;
535 }
536 }
537
538 private boolean isPublicSetterMethod( JavaMethod method )
539 {
540 return method.isPublic()
541 && !method.isStatic()
542 && method.getName().length() > 3
543 && ( method.getName().startsWith( "add" ) || method.getName().startsWith( "set" ) )
544 && "void".equals( method.getReturnType().getValue() )
545 && method.getParameters().size() == 1;
546 }
547
548 protected Map<String, JavaClass> discoverClasses( JavaProjectBuilder builder )
549 {
550 Collection<JavaClass> javaClasses = builder.getClasses();
551
552 if ( javaClasses == null || javaClasses.size() < 1 )
553 {
554 return Collections.emptyMap();
555 }
556
557 Map<String, JavaClass> javaClassMap = new HashMap<>( javaClasses.size() );
558
559 for ( JavaClass javaClass : javaClasses )
560 {
561 javaClassMap.put( javaClass.getFullyQualifiedName(), javaClass );
562 }
563
564 return javaClassMap;
565 }
566
567 protected void extendJavaProjectBuilderWithSourcesJar( JavaProjectBuilder builder,
568 Artifact artifact, PluginToolsRequest request,
569 String classifier )
570 throws ExtractionException
571 {
572 try
573 {
574 Artifact sourcesArtifact =
575 repositorySystem.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(),
576 artifact.getVersion(), artifact.getType(), classifier );
577
578 ArtifactResolutionRequest req = new ArtifactResolutionRequest();
579 req.setArtifact( sourcesArtifact );
580 req.setLocalRepository( request.getLocal() );
581 req.setRemoteRepositories( request.getRemoteRepos() );
582 ArtifactResolutionResult res = repositorySystem.resolve( req );
583 if ( res.hasMissingArtifacts() || res.hasExceptions() )
584 {
585 getLogger().warn(
586 "Unable to get sources artifact for " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
587 + artifact.getVersion() + ". Some javadoc tags (@since, @deprecated and comments) won't be used" );
588 return;
589 }
590
591 if ( sourcesArtifact.getFile() == null || !sourcesArtifact.getFile().exists() )
592 {
593
594 return;
595 }
596
597 if ( sourcesArtifact.getFile().isFile() )
598 {
599
600 File extractDirectory = new File( request.getProject().getBuild().getDirectory(),
601 "maven-plugin-plugin-sources/" + sourcesArtifact.getGroupId() + "/"
602 + sourcesArtifact.getArtifactId() + "/" + sourcesArtifact.getVersion()
603 + "/" + sourcesArtifact.getClassifier() );
604 extractDirectory.mkdirs();
605
606 UnArchiver unArchiver = archiverManager.getUnArchiver( "jar" );
607 unArchiver.setSourceFile( sourcesArtifact.getFile() );
608 unArchiver.setDestDirectory( extractDirectory );
609 unArchiver.extract();
610
611 extendJavaProjectBuilder( builder,
612 Arrays.asList( extractDirectory ),
613 request.getDependencies() );
614 }
615 else if ( sourcesArtifact.getFile().isDirectory() )
616 {
617 extendJavaProjectBuilder( builder,
618 Arrays.asList( sourcesArtifact.getFile() ),
619 request.getDependencies() );
620 }
621 }
622 catch ( ArchiverException | NoSuchArchiverException e )
623 {
624 throw new ExtractionException( e.getMessage(), e );
625 }
626 }
627
628 private void extendJavaProjectBuilder( JavaProjectBuilder builder,
629 final MavenProject project )
630 {
631 List<File> sources = new ArrayList<>();
632
633 for ( String source : project.getCompileSourceRoots() )
634 {
635 sources.add( new File( source ) );
636 }
637
638
639 File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" );
640 if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() )
641 && generatedPlugin.exists() )
642 {
643 sources.add( generatedPlugin );
644 }
645 extendJavaProjectBuilder( builder, sources, project.getArtifacts() );
646 }
647
648 private void extendJavaProjectBuilder( JavaProjectBuilder builder,
649 List<File> sourceDirectories,
650 Set<Artifact> artifacts )
651 {
652
653
654 List<URL> urls = new ArrayList<>( artifacts.size() );
655 for ( Artifact artifact : artifacts )
656 {
657 try
658 {
659 urls.add( artifact.getFile().toURI().toURL() );
660 }
661 catch ( MalformedURLException e )
662 {
663
664 }
665 }
666 builder.addClassLoader( new URLClassLoader( urls.toArray( new URL[0] ), ClassLoader.getSystemClassLoader() ) );
667
668 for ( File source : sourceDirectories )
669 {
670 builder.addSourceTree( source );
671 }
672 }
673
674 private List<MojoDescriptor> toMojoDescriptors( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses,
675 PluginDescriptor pluginDescriptor )
676 throws DuplicateParameterException, InvalidParameterException
677 {
678 List<MojoDescriptor> mojoDescriptors = new ArrayList<>( mojoAnnotatedClasses.size() );
679 for ( MojoAnnotatedClass mojoAnnotatedClass : mojoAnnotatedClasses.values() )
680 {
681
682 if ( mojoAnnotatedClass.getMojo() == null )
683 {
684 continue;
685 }
686
687 ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor( true );
688
689
690
691 mojoDescriptor.setImplementation( mojoAnnotatedClass.getClassName() );
692 mojoDescriptor.setLanguage( "java" );
693
694 MojoAnnotationContent mojo = mojoAnnotatedClass.getMojo();
695
696 mojoDescriptor.setDescription( mojo.getDescription() );
697 mojoDescriptor.setSince( mojo.getSince() );
698 mojo.setDeprecated( mojo.getDeprecated() );
699
700 mojoDescriptor.setProjectRequired( mojo.requiresProject() );
701
702 mojoDescriptor.setRequiresReports( mojo.requiresReports() );
703
704 mojoDescriptor.setComponentConfigurator( mojo.configurator() );
705
706 mojoDescriptor.setInheritedByDefault( mojo.inheritByDefault() );
707
708 mojoDescriptor.setInstantiationStrategy( mojo.instantiationStrategy().id() );
709
710 mojoDescriptor.setAggregator( mojo.aggregator() );
711 mojoDescriptor.setDependencyResolutionRequired( mojo.requiresDependencyResolution().id() );
712 mojoDescriptor.setDependencyCollectionRequired( mojo.requiresDependencyCollection().id() );
713
714 mojoDescriptor.setDirectInvocationOnly( mojo.requiresDirectInvocation() );
715 mojoDescriptor.setDeprecated( mojo.getDeprecated() );
716 mojoDescriptor.setThreadSafe( mojo.threadSafe() );
717
718 ExecuteAnnotationContent execute = findExecuteInParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
719 if ( execute != null )
720 {
721 mojoDescriptor.setExecuteGoal( execute.goal() );
722 mojoDescriptor.setExecuteLifecycle( execute.lifecycle() );
723 if ( execute.phase() != null )
724 {
725 mojoDescriptor.setExecutePhase( execute.phase().id() );
726 }
727 }
728
729 mojoDescriptor.setExecutionStrategy( mojo.executionStrategy() );
730
731
732
733 mojoDescriptor.setGoal( mojo.name() );
734 mojoDescriptor.setOnlineRequired( mojo.requiresOnline() );
735
736 mojoDescriptor.setPhase( mojo.defaultPhase().id() );
737
738
739 Map<String, ParameterAnnotationContent> parameters =
740 getParametersParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
741
742 for ( ParameterAnnotationContent parameterAnnotationContent : new TreeSet<>( parameters.values() ) )
743 {
744 org.apache.maven.plugin.descriptor.Parameter parameter =
745 new org.apache.maven.plugin.descriptor.Parameter();
746 String name =
747 StringUtils.isEmpty( parameterAnnotationContent.name() ) ? parameterAnnotationContent.getFieldName()
748 : parameterAnnotationContent.name();
749 parameter.setName( name );
750 parameter.setAlias( parameterAnnotationContent.alias() );
751 parameter.setDefaultValue( parameterAnnotationContent.defaultValue() );
752 parameter.setDeprecated( parameterAnnotationContent.getDeprecated() );
753 parameter.setDescription( parameterAnnotationContent.getDescription() );
754 parameter.setEditable( !parameterAnnotationContent.readonly() );
755 String property = parameterAnnotationContent.property();
756 if ( StringUtils.contains( property, '$' ) || StringUtils.contains( property, '{' )
757 || StringUtils.contains( property, '}' ) )
758 {
759 throw new InvalidParameterException(
760 "Invalid property for parameter '" + parameter.getName() + "', " + "forbidden characters ${}: "
761 + property, null );
762 }
763 parameter.setExpression( StringUtils.isEmpty( property ) ? "" : "${" + property + "}" );
764 StringBuilder type = new StringBuilder( parameterAnnotationContent.getClassName() );
765 if ( !parameterAnnotationContent.getTypeParameters().isEmpty() )
766 {
767 type.append( parameterAnnotationContent.getTypeParameters().stream()
768 .collect( Collectors.joining( ", ", "<", ">" ) ) );
769 }
770 parameter.setType( type.toString() );
771 parameter.setSince( parameterAnnotationContent.getSince() );
772 parameter.setRequired( parameterAnnotationContent.required() );
773
774 mojoDescriptor.addParameter( parameter );
775 }
776
777
778 Map<String, ComponentAnnotationContent> components =
779 getComponentsParentHierarchy( mojoAnnotatedClass, mojoAnnotatedClasses );
780
781 for ( ComponentAnnotationContent componentAnnotationContent : new TreeSet<>( components.values() ) )
782 {
783 org.apache.maven.plugin.descriptor.Parameter parameter =
784 new org.apache.maven.plugin.descriptor.Parameter();
785 parameter.setName( componentAnnotationContent.getFieldName() );
786
787
788 String expression = PluginUtils.MAVEN_COMPONENTS.get( componentAnnotationContent.getRoleClassName() );
789 if ( expression == null )
790 {
791
792 parameter.setRequirement( new Requirement( componentAnnotationContent.getRoleClassName(),
793 componentAnnotationContent.hint() ) );
794 }
795 else
796 {
797
798 getLogger().warn( "Deprecated @Component annotation for '" + parameter.getName() + "' field in "
799 + mojoAnnotatedClass.getClassName()
800 + ": replace with @Parameter( defaultValue = \"" + expression
801 + "\", readonly = true )" );
802 parameter.setDefaultValue( expression );
803 parameter.setType( componentAnnotationContent.getRoleClassName() );
804 parameter.setRequired( true );
805 }
806 parameter.setDeprecated( componentAnnotationContent.getDeprecated() );
807 parameter.setSince( componentAnnotationContent.getSince() );
808
809
810
811 parameter.setEditable( false );
812
813 mojoDescriptor.addParameter( parameter );
814 }
815
816 mojoDescriptor.setPluginDescriptor( pluginDescriptor );
817
818 mojoDescriptors.add( mojoDescriptor );
819 }
820 return mojoDescriptors;
821 }
822
823 protected ExecuteAnnotationContent findExecuteInParentHierarchy( MojoAnnotatedClass mojoAnnotatedClass,
824 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
825 {
826 if ( mojoAnnotatedClass.getExecute() != null )
827 {
828 return mojoAnnotatedClass.getExecute();
829 }
830 String parentClassName = mojoAnnotatedClass.getParentClassName();
831 if ( StringUtils.isEmpty( parentClassName ) )
832 {
833 return null;
834 }
835 MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
836 if ( parent == null )
837 {
838 return null;
839 }
840 return findExecuteInParentHierarchy( parent, mojoAnnotatedClasses );
841 }
842
843
844 protected Map<String, ParameterAnnotationContent> getParametersParentHierarchy(
845 MojoAnnotatedClass mojoAnnotatedClass,
846 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
847 {
848 List<ParameterAnnotationContent> parameterAnnotationContents = new ArrayList<>();
849
850 parameterAnnotationContents =
851 getParametersParent( mojoAnnotatedClass, parameterAnnotationContents, mojoAnnotatedClasses );
852
853
854 Collections.reverse( parameterAnnotationContents );
855
856 Map<String, ParameterAnnotationContent> map = new HashMap<>( parameterAnnotationContents.size() );
857
858 for ( ParameterAnnotationContent parameterAnnotationContent : parameterAnnotationContents )
859 {
860 map.put( parameterAnnotationContent.getFieldName(), parameterAnnotationContent );
861 }
862 return map;
863 }
864
865 protected List<ParameterAnnotationContent> getParametersParent( MojoAnnotatedClass mojoAnnotatedClass,
866 List<ParameterAnnotationContent> parameterAnnotationContents,
867 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
868 {
869 parameterAnnotationContents.addAll( mojoAnnotatedClass.getParameters().values() );
870 String parentClassName = mojoAnnotatedClass.getParentClassName();
871 if ( parentClassName != null )
872 {
873 MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
874 if ( parent != null )
875 {
876 return getParametersParent( parent, parameterAnnotationContents, mojoAnnotatedClasses );
877 }
878 }
879 return parameterAnnotationContents;
880 }
881
882 protected Map<String, ComponentAnnotationContent> getComponentsParentHierarchy(
883 MojoAnnotatedClass mojoAnnotatedClass,
884 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
885 {
886 List<ComponentAnnotationContent> componentAnnotationContents = new ArrayList<>();
887
888 componentAnnotationContents =
889 getComponentParent( mojoAnnotatedClass, componentAnnotationContents, mojoAnnotatedClasses );
890
891
892 Collections.reverse( componentAnnotationContents );
893
894 Map<String, ComponentAnnotationContent> map = new HashMap<>( componentAnnotationContents.size() );
895
896 for ( ComponentAnnotationContent componentAnnotationContent : componentAnnotationContents )
897 {
898 map.put( componentAnnotationContent.getFieldName(), componentAnnotationContent );
899 }
900 return map;
901 }
902
903 protected List<ComponentAnnotationContent> getComponentParent( MojoAnnotatedClass mojoAnnotatedClass,
904 List<ComponentAnnotationContent> componentAnnotationContents,
905 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses )
906 {
907 componentAnnotationContents.addAll( mojoAnnotatedClass.getComponents().values() );
908 String parentClassName = mojoAnnotatedClass.getParentClassName();
909 if ( parentClassName != null )
910 {
911 MojoAnnotatedClass parent = mojoAnnotatedClasses.get( parentClassName );
912 if ( parent != null )
913 {
914 return getComponentParent( parent, componentAnnotationContents, mojoAnnotatedClasses );
915 }
916 }
917 return componentAnnotationContents;
918 }
919
920 protected MavenProject getFromProjectReferences( Artifact artifact, MavenProject project )
921 {
922 if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
923 {
924 return null;
925 }
926 Collection<MavenProject> mavenProjects = project.getProjectReferences().values();
927 for ( MavenProject mavenProject : mavenProjects )
928 {
929 if ( Objects.equals( mavenProject.getId(), artifact.getId() ) )
930 {
931 return mavenProject;
932 }
933 }
934 return null;
935 }
936
937 }