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