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.PrintWriter;
25 import java.io.Writer;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.text.MessageFormat;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.ResourceBundle;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 import org.apache.maven.plugin.descriptor.MojoDescriptor;
38 import org.apache.maven.plugin.descriptor.Parameter;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.tools.plugin.EnhancedParameterWrapper;
41 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
42 import org.apache.maven.tools.plugin.PluginToolsRequest;
43 import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
44 import org.codehaus.plexus.util.StringUtils;
45 import org.codehaus.plexus.util.io.CachingOutputStream;
46 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
47 import org.codehaus.plexus.util.xml.XMLWriter;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import static java.nio.charset.StandardCharsets.UTF_8;
52
53
54
55
56 public class PluginXdocGenerator implements Generator {
57
58
59
60
61 private static final Pattern HTML_LINK_PATTERN = Pattern.compile("<a href=\\\"([^\\\"]*)\\\">(.*?)</a>");
62
63 private static final Logger LOG = LoggerFactory.getLogger(PluginXdocGenerator.class);
64
65
66
67
68 private final Locale locale;
69
70
71
72
73 private final MavenProject project;
74
75
76
77
78
79 private final File reportOutputDirectory;
80
81 private final boolean disableInternalJavadocLinkValidation;
82
83
84
85
86
87 public PluginXdocGenerator() {
88 this(null);
89 }
90
91
92
93
94
95
96 public PluginXdocGenerator(MavenProject project) {
97 this(project, Locale.ENGLISH, new File("").getAbsoluteFile(), false);
98 }
99
100
101
102
103
104 public PluginXdocGenerator(
105 MavenProject project,
106 Locale locale,
107 File reportOutputDirectory,
108 boolean disableInternalJavadocLinkValidation) {
109 this.project = project;
110 this.locale = locale;
111 this.reportOutputDirectory = reportOutputDirectory;
112 this.disableInternalJavadocLinkValidation = disableInternalJavadocLinkValidation;
113 }
114
115
116
117
118 @Override
119 public void execute(File destinationDirectory, PluginToolsRequest request) throws GeneratorException {
120 try {
121 if (request.getPluginDescriptor().getMojos() != null) {
122 List<MojoDescriptor> mojos = request.getPluginDescriptor().getMojos();
123 for (MojoDescriptor descriptor : mojos) {
124 processMojoDescriptor(descriptor, destinationDirectory);
125 }
126 }
127 } catch (IOException e) {
128 throw new GeneratorException(e.getMessage(), e);
129 }
130 }
131
132
133
134
135
136
137 protected void processMojoDescriptor(MojoDescriptor mojoDescriptor, File destinationDirectory) throws IOException {
138 File outputFile = new File(destinationDirectory, getMojoFilename(mojoDescriptor, "xml"));
139 try (Writer writer = new OutputStreamWriter(new CachingOutputStream(outputFile), UTF_8)) {
140 XMLWriter w = new PrettyPrintXMLWriter(new PrintWriter(writer), UTF_8.name(), null);
141 writeBody(mojoDescriptor, w);
142
143 writer.flush();
144 }
145 }
146
147
148
149
150
151
152 private String getMojoFilename(MojoDescriptor mojo, String ext) {
153 return mojo.getGoal() + "-mojo." + ext;
154 }
155
156
157
158
159
160 private void writeBody(MojoDescriptor mojoDescriptor, XMLWriter w) {
161 w.startElement("document");
162 w.addAttribute("xmlns", "http://maven.apache.org/XDOC/2.0");
163 w.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
164 w.addAttribute(
165 "xsi:schemaLocation", "http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd");
166
167
168
169
170
171 w.startElement("properties");
172
173 w.startElement("title");
174 w.writeText(mojoDescriptor.getFullGoalName());
175 w.endElement();
176
177 w.endElement();
178
179
180
181
182
183 w.startElement("body");
184
185 w.startElement("section");
186
187 w.addAttribute("name", mojoDescriptor.getFullGoalName());
188
189 writeReportNotice(mojoDescriptor, w);
190
191 w.startElement("p");
192 w.writeMarkup(getString("pluginxdoc.mojodescriptor.fullname"));
193 w.endElement();
194 w.startElement("p");
195 w.writeMarkup(mojoDescriptor.getPluginDescriptor().getId() + ":" + mojoDescriptor.getGoal());
196 w.endElement();
197
198 String context = "goal " + mojoDescriptor.getGoal();
199 if (StringUtils.isNotEmpty(mojoDescriptor.getDeprecated())) {
200 w.startElement("p");
201 w.writeMarkup(getString("pluginxdoc.mojodescriptor.deprecated"));
202 w.endElement();
203 w.startElement("div");
204 w.writeMarkup(getXhtmlWithValidatedLinks(mojoDescriptor.getDeprecated(), context));
205 w.endElement();
206 }
207
208 w.startElement("p");
209 w.writeMarkup(getString("pluginxdoc.description"));
210 w.endElement();
211 w.startElement("div");
212 if (StringUtils.isNotEmpty(mojoDescriptor.getDescription())) {
213 w.writeMarkup(getXhtmlWithValidatedLinks(mojoDescriptor.getDescription(), context));
214 } else {
215 w.writeText(getString("pluginxdoc.nodescription"));
216 }
217 w.endElement();
218
219 writeGoalAttributes(mojoDescriptor, w);
220
221 writeGoalParameterTable(mojoDescriptor, w);
222
223 w.endElement();
224
225 w.endElement();
226
227 w.endElement();
228 }
229
230
231
232
233
234 private void writeReportNotice(MojoDescriptor mojoDescriptor, XMLWriter w) {
235 if (GeneratorUtils.isMavenReport(mojoDescriptor.getImplementation(), project)) {
236 w.startElement("p");
237 w.writeMarkup(getString("pluginxdoc.mojodescriptor.notice.note"));
238 w.writeText(getString("pluginxdoc.mojodescriptor.notice.isMavenReport"));
239 w.endElement();
240 }
241 }
242
243
244
245
246
247 private void writeGoalAttributes(MojoDescriptor mojoDescriptor, XMLWriter w) {
248 w.startElement("p");
249 w.writeMarkup(getString("pluginxdoc.mojodescriptor.attributes"));
250 w.endElement();
251
252 boolean addedUl = false;
253 String value;
254 if (mojoDescriptor.isProjectRequired()) {
255 addedUl = addUl(w, addedUl);
256 w.startElement("li");
257 w.writeMarkup(getString("pluginxdoc.mojodescriptor.projectRequired"));
258 w.endElement();
259 }
260
261 if (mojoDescriptor.isRequiresReports()) {
262 addedUl = addUl(w, addedUl);
263 w.startElement("li");
264 w.writeMarkup(getString("pluginxdoc.mojodescriptor.reportingMojo"));
265 w.endElement();
266 }
267
268 if (mojoDescriptor.isAggregator()) {
269 addedUl = addUl(w, addedUl);
270 w.startElement("li");
271 w.writeMarkup(getString("pluginxdoc.mojodescriptor.aggregator"));
272 w.endElement();
273 }
274
275 if (mojoDescriptor.isDirectInvocationOnly()) {
276 addedUl = addUl(w, addedUl);
277 w.startElement("li");
278 w.writeMarkup(getString("pluginxdoc.mojodescriptor.directInvocationOnly"));
279 w.endElement();
280 }
281
282 value = mojoDescriptor.isDependencyResolutionRequired();
283 if (value != null && !value.isEmpty()) {
284 addedUl = addUl(w, addedUl);
285 w.startElement("li");
286 w.writeMarkup(format("pluginxdoc.mojodescriptor.dependencyResolutionRequired", value));
287 w.endElement();
288 }
289
290 if (mojoDescriptor instanceof ExtendedMojoDescriptor) {
291 ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
292
293 value = extendedMojoDescriptor.getDependencyCollectionRequired();
294 if (value != null && !value.isEmpty()) {
295 addedUl = addUl(w, addedUl);
296 w.startElement("li");
297 w.writeMarkup(format("pluginxdoc.mojodescriptor.dependencyCollectionRequired", value));
298 w.endElement();
299 }
300 }
301
302 addedUl = addUl(w, addedUl);
303 w.startElement("li");
304 w.writeMarkup(getString(
305 mojoDescriptor.isThreadSafe()
306 ? "pluginxdoc.mojodescriptor.threadSafe"
307 : "pluginxdoc.mojodescriptor.notThreadSafe"));
308 w.endElement();
309
310 value = mojoDescriptor.getSince();
311 if (value != null && !value.isEmpty()) {
312 addedUl = addUl(w, addedUl);
313 w.startElement("li");
314 w.writeMarkup(format("pluginxdoc.mojodescriptor.since", value));
315 w.endElement();
316 }
317
318 value = mojoDescriptor.getPhase();
319 if (value != null && !value.isEmpty()) {
320 addedUl = addUl(w, addedUl);
321 w.startElement("li");
322 w.writeMarkup(format("pluginxdoc.mojodescriptor.phase", value));
323 w.endElement();
324 }
325
326 value = mojoDescriptor.getExecutePhase();
327 if (value != null && !value.isEmpty()) {
328 addedUl = addUl(w, addedUl);
329 w.startElement("li");
330 w.writeMarkup(format("pluginxdoc.mojodescriptor.executePhase", value));
331 w.endElement();
332 }
333
334 value = mojoDescriptor.getExecuteGoal();
335 if (value != null && !value.isEmpty()) {
336 addedUl = addUl(w, addedUl);
337 w.startElement("li");
338 w.writeMarkup(format("pluginxdoc.mojodescriptor.executeGoal", value));
339 w.endElement();
340 }
341
342 value = mojoDescriptor.getExecuteLifecycle();
343 if (value != null && !value.isEmpty()) {
344 addedUl = addUl(w, addedUl);
345 w.startElement("li");
346 w.writeMarkup(format("pluginxdoc.mojodescriptor.executeLifecycle", value));
347 w.endElement();
348 }
349
350 if (mojoDescriptor.isOnlineRequired()) {
351 addedUl = addUl(w, addedUl);
352 w.startElement("li");
353 w.writeMarkup(getString("pluginxdoc.mojodescriptor.onlineRequired"));
354 w.endElement();
355 }
356
357 if (!mojoDescriptor.isInheritedByDefault()) {
358 addedUl = addUl(w, addedUl);
359 w.startElement("li");
360 w.writeMarkup(getString("pluginxdoc.mojodescriptor.inheritedByDefault"));
361 w.endElement();
362 }
363
364 if (addedUl) {
365 w.endElement();
366 }
367 }
368
369
370
371
372
373 private void writeGoalParameterTable(MojoDescriptor mojoDescriptor, XMLWriter w) {
374 List<Parameter> parameterList = mojoDescriptor.getParameters();
375
376
377 List<Parameter> list = filterParameters(parameterList);
378
379 if (!list.isEmpty()) {
380 writeParameterSummary(list, w, mojoDescriptor.getGoal());
381 writeParameterDetails(list, w, mojoDescriptor.getGoal());
382 } else {
383 w.startElement("subsection");
384 w.addAttribute("name", getString("pluginxdoc.mojodescriptor.parameters"));
385
386 w.startElement("p");
387 w.writeMarkup(getString("pluginxdoc.mojodescriptor.noParameter"));
388 w.endElement();
389
390 w.endElement();
391 }
392 }
393
394
395
396
397
398
399
400 private List<Parameter> filterParameters(List<Parameter> parameterList) {
401 List<Parameter> filtered = new ArrayList<>();
402
403 if (parameterList != null) {
404 for (Parameter parameter : parameterList) {
405 if (parameter.isEditable()) {
406 String expression = parameter.getExpression();
407
408 if (expression == null || !expression.startsWith("${component.")) {
409 filtered.add(parameter);
410 }
411 }
412 }
413 }
414
415 return filtered;
416 }
417
418
419
420
421
422 private void writeParameterDetails(List<Parameter> parameterList, XMLWriter w, String goal) {
423 w.startElement("subsection");
424 w.addAttribute("name", getString("pluginxdoc.mojodescriptor.parameter.details"));
425
426 for (Iterator<Parameter> parameters = parameterList.iterator(); parameters.hasNext(); ) {
427 Parameter parameter = parameters.next();
428
429 w.startElement("h4");
430 w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.name_internal", parameter.getName()));
431 w.endElement();
432
433 String context = "Parameter " + parameter.getName() + " in goal " + goal;
434 if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
435 w.startElement("div");
436 String deprecated = getXhtmlWithValidatedLinks(parameter.getDeprecated(), context);
437 w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.deprecated", deprecated));
438 w.endElement();
439 }
440
441 w.startElement("div");
442 if (StringUtils.isNotEmpty(parameter.getDescription())) {
443 w.writeMarkup(getXhtmlWithValidatedLinks(parameter.getDescription(), context));
444 } else {
445 w.writeMarkup(getString("pluginxdoc.nodescription"));
446 }
447 w.endElement();
448
449 boolean addedUl = false;
450 addedUl = addUl(w, addedUl, parameter.getType());
451 String typeValue = getLinkedType(parameter, false);
452 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.type"), typeValue, w);
453
454 if (StringUtils.isNotEmpty(parameter.getSince())) {
455 addedUl = addUl(w, addedUl);
456 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.since"), parameter.getSince(), w);
457 }
458
459 if (parameter.isRequired()) {
460 addedUl = addUl(w, addedUl);
461 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.required"), getString("pluginxdoc.yes"), w);
462 } else {
463 addedUl = addUl(w, addedUl);
464 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.required"), getString("pluginxdoc.no"), w);
465 }
466
467 String expression = parameter.getExpression();
468 addedUl = addUl(w, addedUl, expression);
469 String property = getPropertyFromExpression(expression);
470 if (property == null) {
471 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.expression"), expression, w);
472 } else {
473 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.property"), property, w);
474 }
475
476 addedUl = addUl(w, addedUl, parameter.getDefaultValue());
477 writeDetail(
478 getString("pluginxdoc.mojodescriptor.parameter.default"),
479 escapeXml(parameter.getDefaultValue()),
480 w);
481
482 addedUl = addUl(w, addedUl, parameter.getAlias());
483 writeDetail(getString("pluginxdoc.mojodescriptor.parameter.alias"), escapeXml(parameter.getAlias()), w);
484
485 if (addedUl) {
486 w.endElement();
487 }
488
489 if (parameters.hasNext()) {
490 w.writeMarkup("<hr/>");
491 }
492 }
493
494 w.endElement();
495 }
496
497 static String getShortType(String type) {
498
499 int startTypeArguments = type.indexOf('<');
500 if (startTypeArguments == -1) {
501 return getShortTypeOfSimpleType(type);
502 } else {
503 StringBuilder shortType = new StringBuilder();
504 shortType.append(getShortTypeOfSimpleType(type.substring(0, startTypeArguments)));
505 shortType
506 .append("<")
507 .append(getShortTypeOfTypeArgument(type.substring(startTypeArguments + 1, type.lastIndexOf(">"))))
508 .append(">");
509 return shortType.toString();
510 }
511 }
512
513 private static String getShortTypeOfTypeArgument(String type) {
514 String[] typeArguments = type.split(",\\s*");
515 StringBuilder shortType = new StringBuilder();
516 for (int i = 0; i < typeArguments.length; i++) {
517 String typeArgument = typeArguments[i];
518 if (typeArgument.contains("<")) {
519
520 return "...";
521 } else {
522 shortType.append(getShortTypeOfSimpleType(typeArgument));
523 if (i < typeArguments.length - 1) {
524 shortType.append(",");
525 }
526 }
527 }
528 return shortType.toString();
529 }
530
531 private static String getShortTypeOfSimpleType(String type) {
532 int index = type.lastIndexOf('.');
533 return type.substring(index + 1);
534 }
535
536 private String getLinkedType(Parameter parameter, boolean isShortType) {
537 final String typeValue;
538 if (isShortType) {
539 typeValue = getShortType(parameter.getType());
540 } else {
541 typeValue = parameter.getType();
542 }
543 if (parameter instanceof EnhancedParameterWrapper) {
544 EnhancedParameterWrapper enhancedParameter = (EnhancedParameterWrapper) parameter;
545 if (enhancedParameter.getTypeJavadocUrl() != null) {
546 URI javadocUrl = enhancedParameter.getTypeJavadocUrl();
547
548 if (javadocUrl.isAbsolute()
549 || disableInternalJavadocLinkValidation
550 || JavadocLinkGenerator.isLinkValid(javadocUrl, reportOutputDirectory.toPath())) {
551 return format(
552 "pluginxdoc.mojodescriptor.parameter.type_link",
553 new Object[] {escapeXml(typeValue), enhancedParameter.getTypeJavadocUrl()});
554 }
555 }
556 }
557 return escapeXml(typeValue);
558 }
559
560 private boolean addUl(XMLWriter w, boolean addedUl, String content) {
561 if (content != null && !content.isEmpty()) {
562 return addUl(w, addedUl);
563 }
564 return addedUl;
565 }
566
567 private boolean addUl(XMLWriter w, boolean addedUl) {
568 if (!addedUl) {
569 w.startElement("ul");
570 addedUl = true;
571 }
572 return addedUl;
573 }
574
575 private String getPropertyFromExpression(String expression) {
576 if ((expression != null && !expression.isEmpty())
577 && expression.startsWith("${")
578 && expression.endsWith("}")
579 && !expression.substring(2).contains("${")) {
580
581 return expression.substring(2, expression.length() - 1);
582 }
583
584 return null;
585 }
586
587
588
589
590
591
592 private void writeDetail(String param, String value, XMLWriter w) {
593 if (value != null && !value.isEmpty()) {
594 w.startElement("li");
595 w.writeMarkup(format("pluginxdoc.detail", new String[] {param, value}));
596 w.endElement();
597 }
598 }
599
600
601
602
603
604 private void writeParameterSummary(List<Parameter> parameterList, XMLWriter w, String goal) {
605 List<Parameter> requiredParams = getParametersByRequired(true, parameterList);
606 if (!requiredParams.isEmpty()) {
607 writeParameterList(getString("pluginxdoc.mojodescriptor.requiredParameters"), requiredParams, w, goal);
608 }
609
610 List<Parameter> optionalParams = getParametersByRequired(false, parameterList);
611 if (!optionalParams.isEmpty()) {
612 writeParameterList(getString("pluginxdoc.mojodescriptor.optionalParameters"), optionalParams, w, goal);
613 }
614 }
615
616
617
618
619
620
621 private void writeParameterList(String title, List<Parameter> parameterList, XMLWriter w, String goal) {
622 w.startElement("subsection");
623 w.addAttribute("name", title);
624
625 w.startElement("table");
626 w.addAttribute("border", "0");
627 w.addAttribute("class", "bodyTable");
628
629 w.startElement("tr");
630 w.startElement("th");
631 w.writeText(getString("pluginxdoc.mojodescriptor.parameter.name"));
632 w.endElement();
633 w.startElement("th");
634 w.writeText(getString("pluginxdoc.mojodescriptor.parameter.type"));
635 w.endElement();
636 w.startElement("th");
637 w.writeText(getString("pluginxdoc.mojodescriptor.parameter.since"));
638 w.endElement();
639 w.startElement("th");
640 w.writeText(getString("pluginxdoc.mojodescriptor.parameter.description"));
641 w.endElement();
642 w.endElement();
643
644 for (Parameter parameter : parameterList) {
645 w.startElement("tr");
646
647
648 w.startElement("td");
649 w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName()));
650 w.endElement();
651
652
653 w.startElement("td");
654 w.writeMarkup("<code>" + getLinkedType(parameter, true) + "</code>");
655 w.endElement();
656
657
658 w.startElement("td");
659 if (StringUtils.isNotEmpty(parameter.getSince())) {
660 w.writeMarkup("<code>" + parameter.getSince() + "</code>");
661 } else {
662 w.writeMarkup("<code>-</code>");
663 }
664 w.endElement();
665
666
667 w.startElement("td");
668 String description;
669 String context = "Parameter " + parameter.getName() + " in goal " + goal;
670 if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
671 String deprecated = getXhtmlWithValidatedLinks(parameter.getDescription(), context);
672 description = format("pluginxdoc.mojodescriptor.parameter.deprecated", deprecated);
673 } else if (StringUtils.isNotEmpty(parameter.getDescription())) {
674 description = getXhtmlWithValidatedLinks(parameter.getDescription(), context);
675 } else {
676 description = getString("pluginxdoc.nodescription");
677 }
678 w.writeMarkup(description + "<br/>");
679
680 if (StringUtils.isNotEmpty(parameter.getDefaultValue())) {
681 w.writeMarkup(format(
682 "pluginxdoc.mojodescriptor.parameter.defaultValue", escapeXml(parameter.getDefaultValue())));
683 w.writeMarkup("<br/>");
684 }
685
686 String property = getPropertyFromExpression(parameter.getExpression());
687 if (property != null) {
688 w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.property.description", property));
689 w.writeMarkup("<br/>");
690 }
691
692 if (StringUtils.isNotEmpty(parameter.getAlias())) {
693 w.writeMarkup(format(
694 "pluginxdoc.mojodescriptor.parameter.alias.description", escapeXml(parameter.getAlias())));
695 }
696
697 w.endElement();
698 w.endElement();
699 }
700
701 w.endElement();
702 w.endElement();
703 }
704
705
706
707
708
709
710 private List<Parameter> getParametersByRequired(boolean required, List<Parameter> parameterList) {
711 List<Parameter> list = new ArrayList<>();
712
713 for (Parameter parameter : parameterList) {
714 if (parameter.isRequired() == required) {
715 list.add(parameter);
716 }
717 }
718
719 return list;
720 }
721
722
723
724
725
726
727 private ResourceBundle getBundle() {
728 return ResourceBundle.getBundle("pluginxdoc", locale, getClass().getClassLoader());
729 }
730
731
732
733
734
735
736 private String getString(String key) {
737 return getBundle().getString(key);
738 }
739
740
741
742
743
744
745
746
747
748 private String format(String key, Object arg1) {
749 return format(key, new Object[] {arg1});
750 }
751
752
753
754
755
756
757
758
759
760 private String format(String key, Object[] args) {
761 String pattern = getString(key);
762
763 pattern = StringUtils.replace(pattern, "'", "''");
764
765 MessageFormat messageFormat = new MessageFormat("");
766 messageFormat.setLocale(locale);
767 messageFormat.applyPattern(pattern);
768
769 return messageFormat.format(args);
770 }
771
772
773
774
775
776 private String escapeXml(String text) {
777 if (text != null) {
778 text = text.replace("&", "&");
779 text = text.replace("<", "<");
780 text = text.replace(">", ">");
781 text = text.replace("\"", """);
782 text = text.replace("\'", "'");
783 }
784 return text;
785 }
786
787 String getXhtmlWithValidatedLinks(String xhtmlText, String context) {
788 if (disableInternalJavadocLinkValidation) {
789 return xhtmlText;
790 }
791 StringBuffer sanitizedXhtmlText = new StringBuffer();
792
793 Matcher matcher = HTML_LINK_PATTERN.matcher(xhtmlText);
794 while (matcher.find()) {
795 URI link;
796 try {
797 link = new URI(matcher.group(1));
798 if (!link.isAbsolute() && !JavadocLinkGenerator.isLinkValid(link, reportOutputDirectory.toPath())) {
799 matcher.appendReplacement(sanitizedXhtmlText, matcher.group(2));
800 LOG.debug("Removed invalid link {} in {}", link, context);
801 } else {
802 matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
803 }
804 } catch (URISyntaxException e) {
805 LOG.warn("Invalid URI {} found in {}. Cannot validate, leave untouched", matcher.group(1), context);
806 matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
807 }
808 }
809 matcher.appendTail(sanitizedXhtmlText);
810 return sanitizedXhtmlText.toString();
811 }
812 }