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