1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.pdf;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.io.StringReader;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32
33 import org.apache.commons.io.input.XmlStreamReader;
34 import org.apache.maven.artifact.Artifact;
35 import org.apache.maven.artifact.repository.ArtifactRepository;
36 import org.apache.maven.doxia.Doxia;
37 import org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer;
38 import org.apache.maven.doxia.docrenderer.DocumentRenderer;
39 import org.apache.maven.doxia.docrenderer.DocumentRendererContext;
40 import org.apache.maven.doxia.docrenderer.DocumentRendererException;
41 import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer;
42 import org.apache.maven.doxia.document.DocumentMeta;
43 import org.apache.maven.doxia.document.DocumentModel;
44 import org.apache.maven.doxia.document.DocumentTOC;
45 import org.apache.maven.doxia.document.DocumentTOCItem;
46 import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Writer;
47 import org.apache.maven.doxia.index.IndexEntry;
48 import org.apache.maven.doxia.index.IndexingSink;
49 import org.apache.maven.doxia.module.xdoc.XdocSink;
50 import org.apache.maven.doxia.parser.ParseException;
51 import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
52 import org.apache.maven.doxia.sink.impl.SinkAdapter;
53 import org.apache.maven.doxia.site.decoration.DecorationModel;
54 import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader;
55 import org.apache.maven.doxia.siterenderer.Renderer;
56 import org.apache.maven.doxia.siterenderer.RendererException;
57 import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
58 import org.apache.maven.doxia.tools.SiteTool;
59 import org.apache.maven.doxia.tools.SiteToolException;
60 import org.apache.maven.execution.MavenSession;
61 import org.apache.maven.model.ReportPlugin;
62 import org.apache.maven.model.Reporting;
63 import org.apache.maven.plugin.MojoExecutionException;
64 import org.apache.maven.plugins.annotations.Component;
65 import org.apache.maven.plugins.annotations.Mojo;
66 import org.apache.maven.plugins.annotations.Parameter;
67 import org.apache.maven.plugins.annotations.ResolutionScope;
68 import org.apache.maven.project.MavenProject;
69 import org.apache.maven.reporting.MavenReport;
70 import org.apache.maven.reporting.MavenReportException;
71 import org.apache.maven.reporting.exec.MavenReportExecution;
72 import org.apache.maven.reporting.exec.MavenReportExecutor;
73 import org.apache.maven.reporting.exec.MavenReportExecutorRequest;
74 import org.apache.maven.settings.Settings;
75 import org.codehaus.plexus.PlexusConstants;
76 import org.codehaus.plexus.PlexusContainer;
77 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
78 import org.codehaus.plexus.context.Context;
79 import org.codehaus.plexus.context.ContextException;
80 import org.codehaus.plexus.i18n.I18N;
81 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
82 import org.codehaus.plexus.util.FileUtils;
83 import org.codehaus.plexus.util.IOUtil;
84 import org.codehaus.plexus.util.PathTool;
85 import org.codehaus.plexus.util.ReaderFactory;
86 import org.codehaus.plexus.util.StringUtils;
87 import org.codehaus.plexus.util.WriterFactory;
88 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
89
90
91
92
93
94
95 @Mojo(name = "pdf", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
96 public class PdfMojo extends AbstractPdfMojo implements Contextualizable {
97
98
99
100
101 private static final String EOL = System.getProperty("line.separator");
102
103
104
105
106 @Component(hint = "fo")
107 private PdfRenderer foRenderer;
108
109
110
111
112 @Component
113 private I18N i18n;
114
115
116
117
118 @Component(hint = "itext")
119 private PdfRenderer itextRenderer;
120
121
122
123
124
125 @Parameter(property = "locales")
126 private String locales;
127
128
129
130
131 @Component
132 private Renderer siteRenderer;
133
134
135
136
137 @Component
138 private SiteTool siteTool;
139
140
141
142
143
144
145 @Component
146 private Doxia doxia;
147
148
149
150
151 @Parameter(defaultValue = "${project}", readonly = true, required = true)
152 protected MavenProject project;
153
154
155
156
157
158
159 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
160 private Settings settings;
161
162
163
164
165
166
167 @Parameter(defaultValue = "${session}", readonly = true, required = true)
168 private MavenSession session;
169
170
171
172
173 @Parameter(defaultValue = "${basedir}/src/site", required = true)
174 private File siteDirectory;
175
176
177
178
179
180
181 @Parameter(defaultValue = "${project.build.directory}/generated-site", required = true)
182 private File generatedSiteDirectory;
183
184
185
186
187 @Parameter(defaultValue = "${project.build.directory}/pdf", required = true)
188 private File outputDirectory;
189
190
191
192
193 @Parameter(defaultValue = "${project.build.directory}/pdf", required = true)
194 private File workingDirectory;
195
196
197
198
199 @Parameter(defaultValue = "src/site/pdf.xml")
200 private File docDescriptor;
201
202
203
204
205 @Parameter(property = "implementation", defaultValue = "fo", required = true)
206 private String implementation;
207
208
209
210
211 @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
212 private ArtifactRepository localRepository;
213
214
215
216
217
218
219 @Parameter(defaultValue = "${project.remoteArtifactRepositories}")
220 private List<ArtifactRepository> remoteRepositories;
221
222
223
224
225
226 @Parameter(property = "aggregate", defaultValue = "true")
227 private boolean aggregate;
228
229
230
231
232 @Parameter(defaultValue = "${plugin.version}", readonly = true)
233 private String pluginVersion;
234
235
236
237
238
239
240
241
242 @Parameter(property = "includeReports", defaultValue = "true")
243 private boolean includeReports;
244
245
246
247
248
249
250
251
252 @Parameter(property = "generateTOC", defaultValue = "start")
253 private String generateTOC;
254
255
256
257
258
259
260
261
262
263 @Parameter(property = "validate", defaultValue = "false")
264 private boolean validate;
265
266
267
268
269
270
271 @Parameter(defaultValue = "${reports}", required = true, readonly = true)
272 private MavenReport[] reports;
273
274
275
276
277
278
279 @Parameter(defaultValue = "${project.reporting}", readonly = true)
280 private Reporting reporting;
281
282
283
284
285
286 private DocumentRenderer docRenderer;
287
288
289
290
291 private Locale defaultLocale;
292
293
294
295
296 private List<Locale> localesList;
297
298
299
300
301 private DecorationModel defaultDecorationModel;
302
303
304
305
306
307
308 private File generatedSiteDirectoryTmp;
309
310
311
312
313
314
315 private Map<Locale, List<MavenReport>> generatedMavenReports;
316
317
318
319
320 private PlexusContainer container;
321
322
323 public void execute() throws MojoExecutionException {
324 init();
325
326 try {
327 generatePdf();
328 } catch (IOException e) {
329 debugLogGeneratedModel(getDocumentModel(Locale.ENGLISH));
330
331 throw new MojoExecutionException("Error during document generation: " + e.getMessage(), e);
332 }
333
334 try {
335 copyGeneratedPdf();
336 } catch (IOException e) {
337 throw new MojoExecutionException("Error copying generated PDF: " + e.getMessage(), e);
338 }
339 }
340
341
342 public void contextualize(Context context) throws ContextException {
343 container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
344 }
345
346 protected File getOutputDirectory() {
347 return outputDirectory;
348 }
349
350 protected File getWorkingDirectory() {
351 return workingDirectory;
352 }
353
354 protected boolean isIncludeReports() {
355 return includeReports;
356 }
357
358
359
360
361 private void init() {
362 if ("fo".equalsIgnoreCase(implementation)) {
363 this.docRenderer = foRenderer;
364 } else if ("itext".equalsIgnoreCase(implementation)) {
365 this.docRenderer = itextRenderer;
366 } else {
367 getLog().warn("Invalid 'implementation' parameter: '" + implementation + "', using 'fo' as default.");
368
369 this.docRenderer = foRenderer;
370 }
371
372 if (!("none".equalsIgnoreCase(generateTOC)
373 || "start".equalsIgnoreCase(generateTOC)
374 || "end".equalsIgnoreCase(generateTOC))) {
375 getLog().warn("Invalid 'generateTOC' parameter: '" + generateTOC + "', using 'start' as default.");
376
377 this.generateTOC = "start";
378 }
379 }
380
381
382
383
384
385
386
387
388 private void copyGeneratedPdf() throws MojoExecutionException, IOException {
389 boolean requireCopy = !getOutputDirectory()
390 .getCanonicalPath()
391 .equals(getWorkingDirectory().getCanonicalPath());
392
393 String outputName = getDocumentModel(getDefaultLocale()).getOutputName().trim();
394 if (!outputName.endsWith(".pdf")) {
395 outputName = outputName.concat(".pdf");
396 }
397
398 for (final Locale locale : getAvailableLocales()) {
399 File generatedPdfSource = new File(getLocaleDirectory(getWorkingDirectory(), locale), outputName);
400
401 if (!generatedPdfSource.exists()) {
402 getLog().warn("Unable to find the generated pdf: " + generatedPdfSource.getAbsolutePath());
403 continue;
404 }
405
406 File generatedPdfDest = new File(getLocaleDirectory(getOutputDirectory(), locale), outputName);
407
408 if (requireCopy) {
409 FileUtils.copyFile(generatedPdfSource, generatedPdfDest);
410 generatedPdfSource.delete();
411 }
412
413 getLog().info("pdf generated: " + generatedPdfDest);
414 }
415 }
416
417
418
419
420
421
422
423
424 private void generatePdf() throws MojoExecutionException, IOException {
425 Locale.setDefault(getDefaultLocale());
426
427 for (final Locale locale : getAvailableLocales()) {
428 final File workingDir = getLocaleDirectory(getWorkingDirectory(), locale);
429
430 File siteDirectoryFile = getLocaleDirectory(getSiteDirectoryTmp(), locale);
431
432 copyResources(locale);
433
434
435 generateMavenReports(locale);
436
437
438 DocumentRendererContext context = new DocumentRendererContext();
439 context.put("project", project);
440 context.put("settings", settings);
441 context.put("PathTool", new PathTool());
442 context.put("FileUtils", new FileUtils());
443 context.put("StringUtils", new StringUtils());
444 context.put("i18n", i18n);
445 context.put("generateTOC", generateTOC);
446 context.put("validate", validate);
447
448
449 for (Map.Entry<Object, Object> entry : project.getProperties().entrySet()) {
450 context.put((String) entry.getKey(), entry.getValue());
451 }
452
453 final DocumentModel model = aggregate ? getDocumentModel(locale) : null;
454
455 try {
456
457 ((AbstractDocumentRenderer) docRenderer).render(siteDirectoryFile, workingDir, model, context);
458 } catch (DocumentRendererException e) {
459 throw new MojoExecutionException("Error during document generation: " + e.getMessage(), e);
460 }
461 }
462 }
463
464
465
466
467
468 private File getGeneratedSiteDirectoryTmp() {
469 if (this.generatedSiteDirectoryTmp == null) {
470 this.generatedSiteDirectoryTmp = new File(getWorkingDirectory(), "generated-site.tmp");
471 }
472
473 return this.generatedSiteDirectoryTmp;
474 }
475
476
477
478
479
480
481
482
483
484
485
486 protected void prepareTempSiteDirectory(final File tmpSiteDir) throws IOException {
487
488 tmpSiteDir.mkdirs();
489
490
491 if (siteDirectory.exists()) {
492 FileUtils.copyDirectoryStructure(siteDirectory, tmpSiteDir);
493 }
494
495
496 List<String> files = FileUtils.getFileAndDirectoryNames(
497 tmpSiteDir, FileUtils.getDefaultExcludesAsString(), null, true, true, true, true);
498 for (final String fileName : files) {
499 final File file = new File(fileName);
500
501 if (file.isDirectory()) {
502 FileUtils.deleteDirectory(file);
503 } else {
504 file.delete();
505 }
506 }
507
508 copySiteDir(generatedSiteDirectory, tmpSiteDir);
509 }
510
511
512
513
514
515
516
517
518
519 private void copySiteDir(final File from, final File to) throws IOException {
520 if (from == null || !from.exists()) {
521 return;
522 }
523
524
525 for (final Locale locale : getAvailableLocales()) {
526 String excludes = getDefaultExcludesWithLocales(getAvailableLocales(), getDefaultLocale());
527 List<String> siteFiles = siteDirectory.exists()
528 ? FileUtils.getFileNames(siteDirectory, "**/*", excludes, false)
529 : new ArrayList<>();
530 File siteDirectoryLocale = new File(siteDirectory, locale.getLanguage());
531 if (!locale.getLanguage().equals(getDefaultLocale().getLanguage()) && siteDirectoryLocale.exists()) {
532 siteFiles = FileUtils.getFileNames(siteDirectoryLocale, "**/*", excludes, false);
533 }
534
535 List<String> generatedSiteFiles = FileUtils.getFileNames(from, "**/*", excludes, false);
536 File fromLocale = new File(from, locale.getLanguage());
537 if (!locale.getLanguage().equals(getDefaultLocale().getLanguage()) && fromLocale.exists()) {
538 generatedSiteFiles = FileUtils.getFileNames(fromLocale, "**/*", excludes, false);
539 }
540
541 for (final String generatedSiteFile : generatedSiteFiles) {
542 if (siteFiles.contains(generatedSiteFile)) {
543 getLog().warn("Generated-site already contains a file in site: " + generatedSiteFile
544 + ". Ignoring copying it!");
545 continue;
546 }
547
548 if (!locale.getLanguage().equals(getDefaultLocale().getLanguage())) {
549 if (fromLocale.exists()) {
550 File in = new File(fromLocale, generatedSiteFile);
551 File out = new File(new File(to, locale.getLanguage()), generatedSiteFile);
552 out.getParentFile().mkdirs();
553 FileUtils.copyFile(in, out);
554 }
555 } else {
556 File in = new File(from, generatedSiteFile);
557 File out = new File(to, generatedSiteFile);
558 out.getParentFile().mkdirs();
559 FileUtils.copyFile(in, out);
560 }
561 }
562 }
563 }
564
565
566
567
568
569
570
571
572
573
574 private DocumentModel getDocumentModel(Locale locale) throws MojoExecutionException {
575 if (docDescriptor.exists()) {
576 DocumentModel doc = getDocumentModelFromDescriptor(locale);
577
578
579 appendGeneratedReports(doc, locale);
580
581 saveTOC(doc.getToc(), locale);
582
583 return doc;
584 }
585
586 DocumentModel model = new DocumentModelBuilder(project, getDefaultDecorationModel()).getDocumentModel();
587
588 model.getMeta().setGenerator(getDefaultGenerator());
589 model.getMeta().setLanguage(locale.getLanguage());
590 model.getCover().setCoverType(i18n.getString("pdf-plugin", getDefaultLocale(), "toc.type"));
591 model.getToc().setName(i18n.getString("pdf-plugin", getDefaultLocale(), "toc.title"));
592
593 appendGeneratedReports(model, locale);
594
595 saveTOC(model.getToc(), locale);
596
597 debugLogGeneratedModel(model);
598
599 return model;
600 }
601
602
603
604
605
606
607
608
609 private DocumentModel getDocumentModelFromDescriptor(Locale locale) throws MojoExecutionException {
610 DocumentModel model;
611
612 try {
613 model = new DocumentDescriptorReader(project, getLog(), locale)
614 .readAndFilterDocumentDescriptor(docDescriptor);
615 } catch (XmlPullParserException ex) {
616 throw new MojoExecutionException("Error reading DocumentDescriptor!", ex);
617 } catch (IOException io) {
618 throw new MojoExecutionException("Error opening DocumentDescriptor!", io);
619 }
620
621 if (model.getMeta() == null) {
622 model.setMeta(new DocumentMeta());
623 }
624
625 if (StringUtils.isEmpty(model.getMeta().getLanguage())) {
626 model.getMeta().setLanguage(locale.getLanguage());
627 }
628
629 if (StringUtils.isEmpty(model.getMeta().getGenerator())) {
630 model.getMeta().setGenerator(getDefaultGenerator());
631 }
632
633 return model;
634 }
635
636
637
638
639
640
641
642
643 private File getLocaleDirectory(File basedir, Locale locale) {
644 if (locale.getLanguage().equals(getDefaultLocale().getLanguage())) {
645 return basedir;
646 }
647
648 return new File(basedir, locale.getLanguage());
649 }
650
651
652
653
654
655 private Locale getDefaultLocale() {
656 if (this.defaultLocale == null) {
657 this.defaultLocale = getAvailableLocales().get(0);
658 }
659
660 return this.defaultLocale;
661 }
662
663
664
665
666 private List<Locale> getAvailableLocales() {
667 if (this.localesList == null) {
668 this.localesList = siteTool.getSiteLocales(locales);
669 }
670
671 return this.localesList;
672 }
673
674
675
676
677
678 private DecorationModel getDefaultDecorationModel() throws MojoExecutionException {
679 if (this.defaultDecorationModel == null) {
680 final Locale locale = getDefaultLocale();
681
682 final File descriptorFile = siteTool.getSiteDescriptor(siteDirectory, locale);
683 DecorationModel decoration = null;
684
685 if (descriptorFile.exists()) {
686 try (XmlStreamReader reader = new XmlStreamReader(descriptorFile)) {
687 String siteDescriptorContent = IOUtil.toString(reader);
688
689 siteDescriptorContent = siteTool.getInterpolatedSiteDescriptorContent(
690 new HashMap<>(2), project, siteDescriptorContent);
691
692 decoration = new DecorationXpp3Reader().read(new StringReader(siteDescriptorContent));
693 } catch (XmlPullParserException e) {
694 throw new MojoExecutionException("Error parsing site descriptor", e);
695 } catch (IOException e) {
696 throw new MojoExecutionException("Error reading site descriptor", e);
697 } catch (SiteToolException e) {
698 throw new MojoExecutionException("Error when interpoling site descriptor", e);
699 }
700 }
701
702 this.defaultDecorationModel = decoration;
703 }
704
705 return this.defaultDecorationModel;
706 }
707
708
709
710
711
712
713
714
715 private void copyResources(Locale locale) throws MojoExecutionException {
716 final DecorationModel decorationModel = getDefaultDecorationModel();
717 if (decorationModel == null) {
718 return;
719 }
720
721 Artifact skinArtifact;
722 try {
723 skinArtifact = siteTool.getSkinArtifactFromRepository(
724 localRepository, project.getRemoteArtifactRepositories(), decorationModel);
725 } catch (SiteToolException e) {
726 throw new MojoExecutionException("SiteToolException: " + e.getMessage(), e);
727 }
728
729 if (skinArtifact == null) {
730 return;
731 }
732
733 if (getLog().isDebugEnabled()) {
734 getLog().debug("Copy resources from skin artifact: '" + skinArtifact.getId() + "'...");
735 }
736
737 try {
738 final SiteRenderingContext context = siteRenderer.createContextForSkin(
739 skinArtifact, new HashMap<>(2), decorationModel, project.getName(), locale);
740 context.addSiteDirectory(new File(siteDirectory, locale.getLanguage()));
741
742 siteRenderer.copyResources(context, getWorkingDirectory());
743 } catch (IOException e) {
744 throw new MojoExecutionException("IOException: " + e.getMessage(), e);
745 } catch (RendererException e) {
746 throw new MojoExecutionException("RendererException: " + e.getMessage(), e);
747 }
748 }
749
750
751
752
753
754
755 private String getDefaultGenerator() {
756 return "Maven PDF Plugin v. " + pluginVersion + ", '" + implementation + "' implementation.";
757 }
758
759
760
761
762
763
764 private void debugLogGeneratedModel(final DocumentModel docModel) {
765 if (getLog().isDebugEnabled() && project != null) {
766 final File outputDir = new File(project.getBuild().getDirectory(), "pdf");
767
768 if (!outputDir.exists()) {
769 outputDir.mkdirs();
770 }
771
772 final File doc = FileUtils.createTempFile("pdf", ".xml", outputDir);
773 final DocumentXpp3Writer xpp3 = new DocumentXpp3Writer();
774
775 try (Writer writer = WriterFactory.newXmlWriter(doc)) {
776 xpp3.write(writer, docModel);
777 getLog().debug("Generated a default document model: " + doc.getAbsolutePath());
778 } catch (IOException e) {
779 getLog().error("Failed to write document model: " + e.getMessage());
780 getLog().debug(e);
781 }
782 }
783 }
784
785
786
787
788
789
790
791
792
793
794 private void generateMavenReports(Locale locale) throws MojoExecutionException, IOException {
795 if (!isIncludeReports()) {
796 getLog().info("Skipped report generation.");
797 return;
798 }
799
800 if (project.getReporting() == null) {
801 getLog().info("No report was specified.");
802 return;
803 }
804
805 List<MavenReportExecution> reportExecutions = getReports();
806 for (MavenReportExecution reportExecution : reportExecutions) {
807 generateMavenReport(reportExecution, locale);
808 }
809
810
811 copySiteDir(getGeneratedSiteDirectoryTmp(), getSiteDirectoryTmp());
812 copySiteDir(generatedSiteDirectory, getSiteDirectoryTmp());
813 }
814
815
816
817
818
819
820
821
822
823
824
825 private void generateMavenReport(MavenReportExecution reportExecution, Locale locale)
826 throws IOException, MojoExecutionException {
827 MavenReport report = reportExecution.getMavenReport();
828
829 String localReportName = report.getName(locale);
830
831 if (!reportExecution.canGenerateReport()) {
832 getLog().info("Skipped \"" + localReportName + "\" report.");
833 getLog().debug("canGenerateReport() was false.");
834
835 return;
836 }
837
838 if (report.isExternalReport()) {
839 getLog().info("Skipped external \"" + localReportName + "\" report (not supported by pdf plugin).");
840 getLog().debug("isExternalReport() was false.");
841
842 return;
843 }
844
845 for (final MavenReport generatedReport : getGeneratedMavenReports(locale)) {
846 if (report.getName(locale).equals(generatedReport.getName(locale))) {
847 if (getLog().isDebugEnabled()) {
848 getLog().debug(report.getName(locale) + " was already generated.");
849 }
850 return;
851 }
852 }
853
854 File outDir = new File(getGeneratedSiteDirectoryTmp(), "xdoc");
855 if (!locale.getLanguage().equals(defaultLocale.getLanguage())) {
856 outDir = new File(new File(getGeneratedSiteDirectoryTmp(), locale.getLanguage()), "xdoc");
857 }
858 outDir.mkdirs();
859
860 File generatedReport = new File(outDir, report.getOutputName() + ".xml");
861
862 if (siteDirectory.exists()) {
863 String excludes = getDefaultExcludesWithLocales(getAvailableLocales(), getDefaultLocale());
864 List<String> files =
865 FileUtils.getFileNames(siteDirectory, "*/" + report.getOutputName() + ".*", excludes, false);
866 if (!locale.getLanguage().equals(defaultLocale.getLanguage())) {
867 files = FileUtils.getFileNames(
868 new File(siteDirectory, locale.getLanguage()),
869 "*/" + report.getOutputName() + ".*",
870 excludes,
871 false);
872 }
873
874 if (files.size() != 0) {
875 String displayLanguage = locale.getDisplayLanguage(Locale.ENGLISH);
876
877 if (getLog().isInfoEnabled()) {
878 getLog().info("Skipped \"" + report.getName(locale) + "\" report, file \""
879 + report.getOutputName() + "\" already exists for the " + displayLanguage
880 + " version.");
881 }
882
883 return;
884 }
885 }
886
887 if (getLog().isInfoEnabled()) {
888 getLog().info("Generating \"" + localReportName + "\" report.");
889 }
890
891
892 report.setReportOutputDirectory(outDir);
893
894 StringWriter sw = new StringWriter();
895
896 PdfXdocSink pdfXdocSink = null;
897 try {
898 pdfXdocSink = new PdfXdocSink(sw);
899 renderReportToSink(reportExecution, locale, pdfXdocSink);
900 } catch (MavenReportException e) {
901 String goal = reportExecution.getPlugin().getArtifactId()
902 + ':'
903 + reportExecution.getPlugin().getVersion()
904 + ':'
905 + reportExecution.getGoal();
906 throw new MojoExecutionException("Error generating " + goal + " report", e);
907 } finally {
908 if (pdfXdocSink != null) {
909 pdfXdocSink.close();
910 }
911 }
912
913 if (getLog().isDebugEnabled()) {
914 getLog().debug("Writing generated xdoc to " + generatedReport);
915 }
916 writeGeneratedReport(sw.toString(), generatedReport);
917
918
919 if (isValidGeneratedReportXdoc(
920 reportExecution.getPlugin().getId() + ':' + reportExecution.getGoal(),
921 generatedReport,
922 localReportName)) {
923 getGeneratedMavenReports(locale).add(report);
924 }
925 }
926
927
928
929
930
931
932
933
934
935 private void renderReportToSink(MavenReportExecution reportExec, Locale locale, PdfXdocSink sink)
936 throws MavenReportException {
937 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
938 try {
939 if (reportExec.getClassLoader() != null) {
940 Thread.currentThread().setContextClassLoader(reportExec.getClassLoader());
941 }
942
943 MavenReport report = reportExec.getMavenReport();
944
945
946
947
948
949
950
951
952
953
954
955
956
957 report.generate(sink, locale);
958
959 } finally {
960 if (reportExec.getClassLoader() != null) {
961 Thread.currentThread().setContextClassLoader(originalClassLoader);
962 }
963 }
964 }
965
966
967
968
969
970
971 private List<MavenReport> getGeneratedMavenReports(Locale locale) {
972 if (this.generatedMavenReports == null) {
973 this.generatedMavenReports = new HashMap<>(2);
974 }
975
976 this.generatedMavenReports.computeIfAbsent(locale, k -> new ArrayList<>(2));
977
978 return this.generatedMavenReports.get(locale);
979 }
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997 protected void appendGeneratedReports(DocumentModel model, Locale locale) {
998 if (!isIncludeReports()) {
999 return;
1000 }
1001 if (getGeneratedMavenReports(locale).isEmpty()) {
1002 return;
1003 }
1004
1005 final DocumentTOCItem documentTOCItem = new DocumentTOCItem();
1006 documentTOCItem.setName(i18n.getString("pdf-plugin", locale, "toc.project-info.item"));
1007 documentTOCItem.setRef("project-info");
1008
1009 List<String> addedRef = new ArrayList<>(4);
1010
1011 List<DocumentTOCItem> items = new ArrayList<>(4);
1012
1013
1014 for (final MavenReport report : getGeneratedMavenReports(locale)) {
1015 final DocumentTOCItem reportItem = new DocumentTOCItem();
1016 reportItem.setName(report.getName(locale));
1017 reportItem.setRef(report.getOutputName());
1018
1019 items.add(reportItem);
1020
1021 addedRef.add(report.getOutputName());
1022 }
1023
1024
1025 try {
1026 if (generatedSiteDirectory.exists()) {
1027 String excludes = getDefaultExcludesWithLocales(getAvailableLocales(), getDefaultLocale());
1028 List<String> generatedDirs = FileUtils.getDirectoryNames(generatedSiteDirectory, "*", excludes, true);
1029 if (!locale.getLanguage().equals(getDefaultLocale().getLanguage())) {
1030 generatedDirs = FileUtils.getFileNames(
1031 new File(generatedSiteDirectory, locale.getLanguage()), "*", excludes, true);
1032 }
1033
1034 for (final String generatedDir : generatedDirs) {
1035 List<String> generatedFiles =
1036 FileUtils.getFileNames(new File(generatedDir), "**.*", excludes, false);
1037
1038 for (final String generatedFile : generatedFiles) {
1039 final String ref = generatedFile.substring(0, generatedFile.lastIndexOf('.'));
1040
1041 if (!addedRef.contains(ref)) {
1042 final String title = getGeneratedDocumentTitle(new File(generatedDir, generatedFile));
1043
1044 if (title != null) {
1045 final DocumentTOCItem reportItem = new DocumentTOCItem();
1046 reportItem.setName(title);
1047 reportItem.setRef(ref);
1048
1049 items.add(reportItem);
1050 }
1051 }
1052 }
1053 }
1054 }
1055 } catch (IOException e) {
1056 getLog().error("IOException: " + e.getMessage());
1057 getLog().debug(e);
1058 }
1059
1060
1061 documentTOCItem.setItems(items);
1062 model.getToc().addItem(documentTOCItem);
1063 }
1064
1065 private void saveTOC(DocumentTOC toc, Locale locale) {
1066 try {
1067 TocFileHelper.saveTOC(getWorkingDirectory(), toc, locale);
1068 } catch (IOException e) {
1069 getLog().error("Error while writing table of contents", e);
1070 }
1071 }
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081 private String getGeneratedDocumentTitle(final File f) throws IOException {
1082 final IndexEntry entry = new IndexEntry("index");
1083 final IndexingSink titleSink = new IndexingSink(entry);
1084
1085 try (Reader reader = ReaderFactory.newXmlReader(f)) {
1086 doxia.parse(reader, f.getParentFile().getName(), titleSink);
1087 } catch (ParseException e) {
1088 getLog().error("ParseException: " + e.getMessage());
1089 getLog().debug(e);
1090 return null;
1091 } catch (ParserNotFoundException e) {
1092 getLog().error("ParserNotFoundException: " + e.getMessage());
1093 getLog().debug(e);
1094 return null;
1095 }
1096
1097 return titleSink.getTitle();
1098 }
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109 private boolean isValidGeneratedReportXdoc(String fullGoal, File generatedReport, String localReportName) {
1110 SinkAdapter sinkAdapter = new SinkAdapter();
1111 try (Reader reader = ReaderFactory.newXmlReader(generatedReport)) {
1112 doxia.parse(reader, "xdoc", sinkAdapter);
1113 } catch (ParseException e) {
1114 String sb = EOL
1115 + "Error when parsing the generated report xdoc file: "
1116 + generatedReport.getAbsolutePath() + EOL
1117 + e.getMessage() + EOL
1118 + "You could:" + EOL
1119 + " * exclude all reports using -DincludeReports=false" + EOL
1120 + " * remove the "
1121 + fullGoal
1122 + " from the <reporting/> part. To not affect the site generation, "
1123 + "you could create a PDF profile." + EOL
1124 + "Ignoring the \"" + localReportName + "\" report in the PDF." + EOL;
1125 getLog().error(sb);
1126 getLog().debug(e);
1127
1128 return false;
1129 } catch (ParserNotFoundException e) {
1130 getLog().error("ParserNotFoundException: " + e.getMessage());
1131 getLog().debug(e);
1132
1133 return false;
1134 } catch (IOException e) {
1135 getLog().error("IOException: " + e.getMessage());
1136 getLog().debug(e);
1137
1138 return false;
1139 }
1140
1141 return true;
1142 }
1143
1144 protected List<MavenReportExecution> getReports() throws MojoExecutionException {
1145 MavenReportExecutorRequest mavenReportExecutorRequest = new MavenReportExecutorRequest();
1146 mavenReportExecutorRequest.setLocalRepository(localRepository);
1147 mavenReportExecutorRequest.setMavenSession(session);
1148 mavenReportExecutorRequest.setProject(project);
1149 mavenReportExecutorRequest.setReportPlugins(getReportingPlugins());
1150
1151 MavenReportExecutor mavenReportExecutor;
1152 try {
1153 mavenReportExecutor = (MavenReportExecutor) container.lookup(MavenReportExecutor.class.getName());
1154 } catch (ComponentLookupException e) {
1155 throw new MojoExecutionException("could not get MavenReportExecutor component", e);
1156 }
1157 return mavenReportExecutor.buildMavenReports(mavenReportExecutorRequest);
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167 private ReportPlugin[] getReportingPlugins() {
1168 List<ReportPlugin> reportingPlugins = reporting.getPlugins();
1169
1170
1171 boolean hasMavenProjectInfoReportsPlugin = false;
1172 for (ReportPlugin plugin : reportingPlugins) {
1173 if ("org.apache.maven.plugins".equals(plugin.getGroupId())
1174 && "maven-project-info-reports-plugin".equals(plugin.getArtifactId())) {
1175 hasMavenProjectInfoReportsPlugin = true;
1176 break;
1177 }
1178 }
1179
1180 if (!reporting.isExcludeDefaults() && !hasMavenProjectInfoReportsPlugin) {
1181 ReportPlugin mpir = new ReportPlugin();
1182 mpir.setArtifactId("maven-project-info-reports-plugin");
1183 reportingPlugins.add(mpir);
1184 }
1185 return reportingPlugins.toArray(new ReportPlugin[0]);
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 private static void writeGeneratedReport(String content, File toFile) throws IOException {
1200 if (content == null || content.isEmpty()) {
1201 return;
1202 }
1203
1204 try (Writer writer = WriterFactory.newXmlWriter(toFile)) {
1205
1206 writer.write(StringUtils.replace(content, "<table><table", "<table"));
1207 }
1208 }
1209
1210
1211
1212
1213
1214
1215
1216
1217 private static String getDefaultExcludesWithLocales(List<Locale> locales, Locale defaultLocale) {
1218 StringBuilder excludesLocales = new StringBuilder(FileUtils.getDefaultExcludesAsString());
1219 for (final Locale locale : locales) {
1220 if (!locale.getLanguage().equals(defaultLocale.getLanguage())) {
1221 excludesLocales.append(",**/").append(locale.getLanguage()).append("/*");
1222 }
1223 }
1224
1225 return excludesLocales.toString();
1226 }
1227
1228
1229
1230
1231
1232
1233 private static class PdfXdocSink extends XdocSink implements org.codehaus.doxia.sink.Sink {
1234 protected PdfXdocSink(Writer writer) {
1235 super(writer);
1236 }
1237
1238
1239 public void text(String text) {
1240
1241 super.text(StringUtils.replace(text, "\u0092", "'"));
1242 }
1243
1244 public void tableRow() {
1245
1246 if (!this.tableRows) {
1247 tableRows(null, false);
1248 }
1249 super.tableRow(null);
1250 }
1251 }
1252 }