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