1 package org.apache.maven.tools.plugin.generator;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.Writer;
26 import java.net.URI;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.maven.plugin.descriptor.MojoDescriptor;
34 import org.apache.maven.plugin.descriptor.Parameter;
35 import org.apache.maven.plugin.descriptor.PluginDescriptor;
36 import org.apache.maven.plugin.descriptor.Requirement;
37 import org.apache.maven.project.MavenProject;
38 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
39 import org.apache.maven.tools.plugin.PluginToolsRequest;
40 import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
41 import org.apache.maven.tools.plugin.util.PluginUtils;
42 import org.codehaus.plexus.util.StringUtils;
43 import org.codehaus.plexus.util.io.CachingOutputStream;
44 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
45 import org.codehaus.plexus.util.xml.XMLWriter;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import static java.nio.charset.StandardCharsets.UTF_8;
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class PluginDescriptorFilesGenerator
64 implements Generator
65 {
66 private static final Logger LOG = LoggerFactory.getLogger( PluginDescriptorFilesGenerator.class );
67
68
69
70
71 enum DescriptorType
72 {
73 STANDARD,
74 LIMITED_FOR_HELP_MOJO,
75 XHTML
76 }
77
78 @Override
79 public void execute( File destinationDirectory, PluginToolsRequest request )
80 throws GeneratorException
81 {
82 try
83 {
84
85 File f = new File( destinationDirectory, "plugin.xml" );
86 writeDescriptor( f, request, DescriptorType.STANDARD );
87
88
89 MavenProject mavenProject = request.getProject();
90 f = new File( destinationDirectory,
91 PluginHelpGenerator.getPluginHelpPath( mavenProject ) );
92 writeDescriptor( f, request, DescriptorType.LIMITED_FOR_HELP_MOJO );
93
94
95 f = getEnhancedDescriptorFilePath( mavenProject );
96 writeDescriptor( f, request, DescriptorType.XHTML );
97 }
98 catch ( IOException e )
99 {
100 throw new GeneratorException( e.getMessage(), e );
101 }
102 }
103
104 public static File getEnhancedDescriptorFilePath( MavenProject project )
105 {
106 return new File( project.getBuild().getDirectory(), "plugin-enhanced.xml" );
107 }
108
109 private String getVersion()
110 {
111 Package p = this.getClass().getPackage();
112 String version = ( p == null ) ? null : p.getSpecificationVersion();
113 return ( version == null ) ? "SNAPSHOT" : version;
114 }
115
116 public void writeDescriptor( File destinationFile, PluginToolsRequest request, DescriptorType type )
117 throws IOException
118 {
119 PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
120
121 if ( !destinationFile.getParentFile().exists() )
122 {
123 destinationFile.getParentFile().mkdirs();
124 }
125
126 try ( Writer writer = new OutputStreamWriter( new CachingOutputStream( destinationFile ), UTF_8 ) )
127 {
128 XMLWriter w = new PrettyPrintXMLWriter( writer, UTF_8.name(), null );
129
130 final String additionalInfo;
131 switch ( type )
132 {
133 case LIMITED_FOR_HELP_MOJO:
134 additionalInfo = " (for help'mojo with additional elements)";
135 break;
136 case XHTML:
137 additionalInfo = " (enhanced XHTML version with additional elements (used for plugin:report))";
138 break;
139 default:
140 additionalInfo = "";
141 break;
142 }
143 w.writeMarkup( "\n<!-- Generated by maven-plugin-tools " + getVersion()
144 + additionalInfo + "-->\n\n" );
145
146 w.startElement( "plugin" );
147
148 GeneratorUtils.element( w, "name", pluginDescriptor.getName() );
149
150 GeneratorUtils.element( w, "description", pluginDescriptor.getDescription() );
151
152 GeneratorUtils.element( w, "groupId", pluginDescriptor.getGroupId() );
153
154 GeneratorUtils.element( w, "artifactId", pluginDescriptor.getArtifactId() );
155
156 GeneratorUtils.element( w, "version", pluginDescriptor.getVersion() );
157
158 GeneratorUtils.element( w, "goalPrefix", pluginDescriptor.getGoalPrefix() );
159
160 if ( type != DescriptorType.LIMITED_FOR_HELP_MOJO )
161 {
162 GeneratorUtils.element( w, "isolatedRealm", String.valueOf( pluginDescriptor.isIsolatedRealm() ) );
163
164 GeneratorUtils.element( w, "inheritedByDefault",
165 String.valueOf( pluginDescriptor.isInheritedByDefault() ) );
166 }
167
168 w.startElement( "mojos" );
169
170 final JavadocLinkGenerator javadocLinkGenerator;
171 if ( request.getInternalJavadocBaseUrl() != null
172 || ( request.getExternalJavadocBaseUrls() != null
173 && !request.getExternalJavadocBaseUrls().isEmpty() ) )
174 {
175 javadocLinkGenerator = new JavadocLinkGenerator( request.getInternalJavadocBaseUrl(),
176 request.getInternalJavadocVersion(),
177 request.getExternalJavadocBaseUrls(),
178 request.getSettings() );
179 }
180 else
181 {
182 javadocLinkGenerator = null;
183 }
184 if ( pluginDescriptor.getMojos() != null )
185 {
186 List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
187
188 PluginUtils.sortMojos( descriptors );
189
190 for ( MojoDescriptor descriptor : descriptors )
191 {
192 processMojoDescriptor( descriptor, w, type, javadocLinkGenerator );
193 }
194 }
195
196 w.endElement();
197
198 if ( type != DescriptorType.LIMITED_FOR_HELP_MOJO )
199 {
200 GeneratorUtils.writeDependencies( w, pluginDescriptor );
201 }
202
203 w.endElement();
204
205 writer.flush();
206
207 }
208 }
209
210
211
212
213
214
215
216
217 private static String getTextValue( DescriptorType type, boolean containsXhtmlValue, String text )
218 {
219 final String xhtmlText;
220 if ( !containsXhtmlValue )
221 {
222 xhtmlText = GeneratorUtils.makeHtmlValid( text );
223 }
224 else
225 {
226 xhtmlText = text;
227 }
228 if ( type != DescriptorType.XHTML )
229 {
230 return new HtmlToPlainTextConverter().convert( text );
231 }
232 else
233 {
234 return xhtmlText;
235 }
236 }
237
238 @SuppressWarnings( "deprecation" )
239 protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w, DescriptorType type,
240 JavadocLinkGenerator javadocLinkGenerator )
241 {
242 boolean containsXhtmlTextValues = mojoDescriptor instanceof ExtendedMojoDescriptor
243 && ( (ExtendedMojoDescriptor) mojoDescriptor ).containsXhtmlTextValues();
244
245 w.startElement( "mojo" );
246
247
248
249
250
251 w.startElement( "goal" );
252 w.writeText( mojoDescriptor.getGoal() );
253 w.endElement();
254
255
256
257
258
259 String description = mojoDescriptor.getDescription();
260
261 if ( StringUtils.isNotEmpty( description ) )
262 {
263 w.startElement( "description" );
264 w.writeText( getTextValue( type, containsXhtmlTextValues, mojoDescriptor.getDescription() ) );
265 w.endElement();
266 }
267
268
269
270
271
272 if ( StringUtils.isNotEmpty( mojoDescriptor.isDependencyResolutionRequired() ) )
273 {
274 GeneratorUtils.element( w, "requiresDependencyResolution",
275 mojoDescriptor.isDependencyResolutionRequired() );
276 }
277
278
279
280
281
282 GeneratorUtils.element( w, "requiresDirectInvocation",
283 String.valueOf( mojoDescriptor.isDirectInvocationOnly() ) );
284
285
286
287
288
289 GeneratorUtils.element( w, "requiresProject", String.valueOf( mojoDescriptor.isProjectRequired() ) );
290
291
292
293
294
295 GeneratorUtils.element( w, "requiresReports", String.valueOf( mojoDescriptor.isRequiresReports() ) );
296
297
298
299
300
301 GeneratorUtils.element( w, "aggregator", String.valueOf( mojoDescriptor.isAggregator() ) );
302
303
304
305
306
307 GeneratorUtils.element( w, "requiresOnline", String.valueOf( mojoDescriptor.isOnlineRequired() ) );
308
309
310
311
312
313 GeneratorUtils.element( w, "inheritedByDefault", String.valueOf( mojoDescriptor.isInheritedByDefault() ) );
314
315
316
317
318
319 if ( StringUtils.isNotEmpty( mojoDescriptor.getPhase() ) )
320 {
321 GeneratorUtils.element( w, "phase", mojoDescriptor.getPhase() );
322 }
323
324
325
326
327
328 if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
329 {
330 GeneratorUtils.element( w, "executePhase", mojoDescriptor.getExecutePhase() );
331 }
332
333 if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteGoal() ) )
334 {
335 GeneratorUtils.element( w, "executeGoal", mojoDescriptor.getExecuteGoal() );
336 }
337
338 if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteLifecycle() ) )
339 {
340 GeneratorUtils.element( w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle() );
341 }
342
343
344
345
346
347 w.startElement( "implementation" );
348 w.writeText( mojoDescriptor.getImplementation() );
349 w.endElement();
350
351
352
353
354
355 w.startElement( "language" );
356 w.writeText( mojoDescriptor.getLanguage() );
357 w.endElement();
358
359
360
361
362
363 if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentConfigurator() ) )
364 {
365 w.startElement( "configurator" );
366 w.writeText( mojoDescriptor.getComponentConfigurator() );
367 w.endElement();
368 }
369
370
371
372
373
374 if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentComposer() ) )
375 {
376 w.startElement( "composer" );
377 w.writeText( mojoDescriptor.getComponentComposer() );
378 w.endElement();
379 }
380
381
382
383
384
385 w.startElement( "instantiationStrategy" );
386 w.writeText( mojoDescriptor.getInstantiationStrategy() );
387 w.endElement();
388
389
390
391
392
393 w.startElement( "executionStrategy" );
394 w.writeText( mojoDescriptor.getExecutionStrategy() );
395 w.endElement();
396
397
398
399
400
401 if ( mojoDescriptor.getSince() != null )
402 {
403 w.startElement( "since" );
404
405 if ( StringUtils.isEmpty( mojoDescriptor.getSince() ) )
406 {
407 w.writeText( "No version given" );
408 }
409 else
410 {
411 w.writeText( mojoDescriptor.getSince() );
412 }
413
414 w.endElement();
415 }
416
417
418
419
420
421 if ( mojoDescriptor.getDeprecated() != null )
422 {
423 w.startElement( "deprecated" );
424
425 if ( StringUtils.isEmpty( mojoDescriptor.getDeprecated() ) )
426 {
427 w.writeText( "No reason given" );
428 }
429 else
430 {
431 w.writeText( getTextValue( type, containsXhtmlTextValues, mojoDescriptor.getDeprecated() ) );
432 }
433
434 w.endElement();
435 }
436
437
438
439
440
441 if ( mojoDescriptor instanceof ExtendedMojoDescriptor )
442 {
443 ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
444 if ( extendedMojoDescriptor.getDependencyCollectionRequired() != null )
445 {
446 GeneratorUtils.element( w, "requiresDependencyCollection",
447 extendedMojoDescriptor.getDependencyCollectionRequired() );
448 }
449
450 GeneratorUtils.element( w, "threadSafe", String.valueOf( extendedMojoDescriptor.isThreadSafe() ) );
451 }
452
453
454
455
456
457 List<Parameter> parameters = mojoDescriptor.getParameters();
458
459 w.startElement( "parameters" );
460
461 Map<String, Requirement> requirements = new LinkedHashMap<>();
462
463 Set<Parameter> configuration = new LinkedHashSet<>();
464
465 if ( parameters != null )
466 {
467 if ( type == DescriptorType.LIMITED_FOR_HELP_MOJO )
468 {
469 PluginUtils.sortMojoParameters( parameters );
470 }
471
472 for ( Parameter parameter : parameters )
473 {
474 String expression = getExpression( parameter );
475
476 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
477 {
478
479
480
481 String role = expression.substring( "${component.".length(), expression.length() - 1 );
482
483 String roleHint = null;
484
485 int posRoleHintSeparator = role.indexOf( '#' );
486 if ( posRoleHintSeparator > 0 )
487 {
488 roleHint = role.substring( posRoleHintSeparator + 1 );
489
490 role = role.substring( 0, posRoleHintSeparator );
491 }
492
493
494 requirements.put( parameter.getName(), new Requirement( role, roleHint ) );
495 }
496 else if ( parameter.getRequirement() != null )
497 {
498 requirements.put( parameter.getName(), parameter.getRequirement() );
499 }
500
501 else if ( type != DescriptorType.LIMITED_FOR_HELP_MOJO || parameter.isEditable() )
502 {
503
504
505 w.startElement( "parameter" );
506
507 GeneratorUtils.element( w, "name", parameter.getName() );
508
509 if ( parameter.getAlias() != null )
510 {
511 GeneratorUtils.element( w, "alias", parameter.getAlias() );
512 }
513
514 writeParameterType( w, type, javadocLinkGenerator, parameter, mojoDescriptor.getGoal() );
515
516 if ( parameter.getSince() != null )
517 {
518 w.startElement( "since" );
519
520 if ( StringUtils.isEmpty( parameter.getSince() ) )
521 {
522 w.writeText( "No version given" );
523 }
524 else
525 {
526 w.writeText( parameter.getSince() );
527 }
528
529 w.endElement();
530 }
531
532 if ( parameter.getDeprecated() != null )
533 {
534 if ( StringUtils.isEmpty( parameter.getDeprecated() ) )
535 {
536 GeneratorUtils.element( w, "deprecated", "No reason given" );
537 }
538 else
539 {
540 GeneratorUtils.element( w, "deprecated",
541 getTextValue( type, containsXhtmlTextValues,
542 parameter.getDeprecated() ) );
543 }
544 }
545
546 if ( parameter.getImplementation() != null )
547 {
548 GeneratorUtils.element( w, "implementation", parameter.getImplementation() );
549 }
550
551 GeneratorUtils.element( w, "required", Boolean.toString( parameter.isRequired() ) );
552
553 GeneratorUtils.element( w, "editable", Boolean.toString( parameter.isEditable() ) );
554
555 GeneratorUtils.element( w, "description", getTextValue( type, containsXhtmlTextValues,
556 parameter.getDescription() ) );
557
558 if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) || StringUtils.isNotEmpty(
559 parameter.getExpression() ) )
560 {
561 configuration.add( parameter );
562 }
563
564 w.endElement();
565 }
566
567 }
568 }
569
570 w.endElement();
571
572
573
574
575
576 if ( !configuration.isEmpty() )
577 {
578 w.startElement( "configuration" );
579
580 for ( Parameter parameter : configuration )
581 {
582 if ( type == DescriptorType.LIMITED_FOR_HELP_MOJO && !parameter.isEditable() )
583 {
584
585 continue;
586 }
587
588 w.startElement( parameter.getName() );
589
590
591 String parameterType = StringUtils.chomp( parameter.getType(), "<" );
592 if ( StringUtils.isNotEmpty( parameterType ) )
593 {
594 w.addAttribute( "implementation", parameterType );
595 }
596
597 if ( parameter.getDefaultValue() != null )
598 {
599 w.addAttribute( "default-value", parameter.getDefaultValue() );
600 }
601
602 if ( StringUtils.isNotEmpty( parameter.getExpression() ) )
603 {
604 w.writeText( parameter.getExpression() );
605 }
606
607 w.endElement();
608 }
609
610 w.endElement();
611 }
612
613
614
615
616
617 if ( !requirements.isEmpty() && type != DescriptorType.LIMITED_FOR_HELP_MOJO )
618 {
619 w.startElement( "requirements" );
620
621 for ( Map.Entry<String, Requirement> entry : requirements.entrySet() )
622 {
623 String key = entry.getKey();
624 Requirement requirement = entry.getValue();
625
626 w.startElement( "requirement" );
627
628 GeneratorUtils.element( w, "role", requirement.getRole() );
629
630 if ( StringUtils.isNotEmpty( requirement.getRoleHint() ) )
631 {
632 GeneratorUtils.element( w, "role-hint", requirement.getRoleHint() );
633 }
634
635 GeneratorUtils.element( w, "field-name", key );
636
637 w.endElement();
638 }
639
640 w.endElement();
641 }
642
643 w.endElement();
644 }
645
646
647
648
649
650
651
652
653
654 protected void writeParameterType( XMLWriter w, DescriptorType type, JavadocLinkGenerator javadocLinkGenerator,
655 Parameter parameter, String goal )
656 {
657 String parameterType = parameter.getType();
658
659 if ( type == DescriptorType.STANDARD )
660 {
661
662 parameterType = StringUtils.chomp( parameterType, "<" );
663 }
664 GeneratorUtils.element( w, "type", parameterType );
665
666 if ( type == DescriptorType.XHTML && javadocLinkGenerator != null )
667 {
668
669 if ( parameter.getType().indexOf( '.' ) == -1 )
670 {
671 LOG.debug( "Javadoc URLs are not available for primitive types like {}",
672 parameter.getType() );
673 }
674 else
675 {
676 try
677 {
678 URI javadocUrl = getJavadocUrlForType( javadocLinkGenerator, parameterType );
679 GeneratorUtils.element( w, "typeJavadocUrl", javadocUrl.toString() );
680 }
681 catch ( IllegalArgumentException e )
682 {
683 LOG.warn( "Could not get javadoc URL for type {} of parameter {} from goal {}: {}",
684 parameter.getType(), parameter.getName(), goal,
685 e.getMessage() );
686 }
687 }
688 }
689 }
690
691 static URI getJavadocUrlForType( JavadocLinkGenerator javadocLinkGenerator, String type )
692 {
693 final String binaryName;
694 int startOfParameterType = type.indexOf( "<" );
695 if ( startOfParameterType != -1 )
696 {
697
698 String mainType = type.substring( 0, startOfParameterType );
699
700
701 String[] parameterTypes = type.substring( startOfParameterType + 1, type.lastIndexOf( ">" ) )
702 .split( ",\\s*" );
703 switch ( parameterTypes.length )
704 {
705 case 1:
706 binaryName = parameterTypes[0];
707 break;
708 case 2:
709 binaryName = parameterTypes[1];
710 break;
711 default:
712
713 binaryName = mainType;
714 }
715 }
716 else
717 {
718 binaryName = type;
719 }
720 return javadocLinkGenerator.createLink( binaryName );
721 }
722
723
724
725
726
727
728
729 private String getExpression( Parameter parameter )
730 {
731 String expression = parameter.getExpression();
732 if ( StringUtils.isNotBlank( expression ) && !expression.contains( "${" ) )
733 {
734 expression = "${" + expression.trim() + "}";
735 parameter.setExpression( expression );
736 }
737 return expression;
738 }
739 }