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