View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2012-2015 ForgeRock AS.
15   */
16  
17  package org.forgerock.doc.maven.build;
18  
19  import org.apache.commons.io.FileUtils;
20  import org.apache.commons.io.FilenameUtils;
21  import org.apache.maven.plugin.MojoExecutionException;
22  import org.forgerock.doc.maven.AbstractDocbkxMojo;
23  import org.forgerock.doc.maven.pre.Fop;
24  import org.forgerock.doc.maven.utils.NameUtils;
25  import org.forgerock.doc.maven.utils.OLinkUtils;
26  import org.twdata.maven.mojoexecutor.MojoExecutor;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  
33  /**
34   * Build FO output formats.
35   */
36  public class Fo {
37  
38      /**
39       * The Mojo that holds configuration and related methods.
40       */
41      private AbstractDocbkxMojo m;
42  
43      /**
44       * The Executor to run docbkx-tools.
45       */
46      private final Executor executor;
47  
48      /**
49       * Constructor setting the Mojo that holds the configuration.
50       *
51       * @param mojo The Mojo that holds the configuration.
52       */
53      public Fo(final AbstractDocbkxMojo mojo) {
54          m = mojo;
55          this.executor = new Executor();
56      }
57  
58      /**
59       * Supported FO formats include "pdf" and "rtf".
60       */
61      private String format = "pdf";
62  
63      /**
64       * Get the format.
65       * Defaults to PDF unless the format has been set to RTF.
66       *
67       * @return The format, either "pdf" or "rtf".
68       */
69      public String getFormat() {
70          return format;
71      }
72  
73      /**
74       * Set the format to PDF or RTF.
75       * Defaults to PDF unless RTF is specified (case does not matter).
76       *
77       * @param format Either {@code pdf} or {@code rtf}.
78       */
79      public void setFormat(final String format) {
80          if (format.equalsIgnoreCase("rtf")) {
81              this.format = "rtf";
82          } else {
83              this.format = "pdf";
84          }
85      }
86  
87      /**
88       * Build documents from DocBook XML sources.
89       *
90       * @throws MojoExecutionException Failed to build output.
91       */
92      public void execute() throws MojoExecutionException {
93          executor.prepareOlinkDB();
94          executor.build();
95      }
96  
97      /**
98       * Get absolute path to an Olink target database XML document
99       * that points to the individual generated Olink DB files, for FO (PDF, RTF).
100      *
101      * @return Absolute path to the file.
102      * @throws MojoExecutionException Could not write target DB file.
103      */
104     final String getTargetDB() throws MojoExecutionException {
105         File targetDB = new File(m.getBuildDirectory(), "olinkdb-" + getFormat() + ".xml");
106 
107         try {
108             OLinkUtils.createTargetDatabase(targetDB, getFormat(), m);
109         } catch (Exception e) {
110             throw new MojoExecutionException(
111                     "Failed to write link target database: " + targetDB.getPath(), e);
112         }
113 
114         return targetDB.getPath();
115     }
116 
117     /**
118      * Enclose methods to run plugins.
119      */
120     class Executor extends MojoExecutor {
121 
122         // Absolute path to Olink target database XML document.
123         private String targetDatabaseDocument;
124 
125         /**
126          * Get the olink target database XML document path.
127          *
128          * @return Absolute path to the file.
129          * @throws MojoExecutionException Could not write target DB file.
130          */
131         String getTargetDatabaseDocument() throws MojoExecutionException {
132             // If it has not been set yet, then set it now.
133             if (targetDatabaseDocument == null || targetDatabaseDocument.isEmpty()) {
134                 targetDatabaseDocument = getTargetDB();
135             }
136 
137             return targetDatabaseDocument;
138         }
139 
140         /**
141          * Prepare olink target database from DocBook XML sources.
142          *
143          * @throws MojoExecutionException Failed to build target database.
144          */
145         void prepareOlinkDB() throws MojoExecutionException {
146 
147             // Due to https://code.google.com/p/docbkx-tools/issues/detail?id=112
148             // RTF generation does not work with docbkx-tools 2.0.15 or 2.0.16.
149             // Rather than try also to fix olinks in RTF,
150             // skip this until that issue is resolved.
151             if (getFormat().equalsIgnoreCase("rtf")) {
152                 return;
153             }
154 
155             for (String docName : m.getDocNames()) {
156                 ArrayList<MojoExecutor.Element> cfg = new ArrayList<MojoExecutor.Element>();
157                 cfg.addAll(m.getBaseConfiguration());
158                 cfg.add(element(name("xincludeSupported"), m.isXincludeSupported()));
159                 cfg.add(element(name("sourceDirectory"), m.path(m.getDocbkxModifiableSourcesDirectory())));
160                 cfg.add(element(name("fop1Extensions"), "1"));
161                 cfg.add(element(name("fopLogLevel"), m.getFopLogLevel()));
162                 cfg.add(element(name("collectXrefTargets"), "yes"));
163                 if (getFormat().equalsIgnoreCase("pdf")) {
164                     cfg.add(element(name("insertOlinkPdfFrag"), "1"));
165                 }
166                 cfg.add(element(name("includes"), docName + "/" + m.getDocumentSrcName()));
167                 cfg.add(element(name("currentDocid"), docName));
168                 cfg.add(element(name("targetDatabaseDocument"), getTargetDatabaseDocument()));
169                 cfg.add(element(name("targetDirectory"), m.path(m.getDocbkxOutputDirectory()) + "/" + getFormat()));
170                 cfg.add(element(name("targetsFilename"), m.getDocumentSrcName() + ".fo.target.db"));
171 
172                 executeMojo(
173                         plugin(
174                                 groupId("com.agilejava.docbkx"),
175                                 artifactId("docbkx-maven-plugin"),
176                                 version(m.getDocbkxVersion())),
177                         goal("generate-" + getFormat()),
178                         configuration(cfg.toArray(new Element[cfg.size()])),
179                         executionEnvironment(m.getProject(), m.getSession(), m.getPluginManager())
180                 );
181 
182                 File outputDir = FileUtils.getFile(
183                         m.getBaseDir(), "target", "docbkx", getFormat(), docName);
184 
185                 // The following output directory should be where the files are
186                 // for versions of docbkx-tools that honor <targetsFilename>.
187                 if (!outputDir.exists()) {
188                     outputDir = new File(m.getDocbkxOutputDirectory(),
189                             getFormat() + File.separator + docName);
190                 }
191 
192                 try {
193                     String[] extensions = {"fo", getFormat()};
194                     Iterator<File> files =
195                             FileUtils.iterateFiles(outputDir, extensions, true);
196                     while (files.hasNext()) {
197                         FileUtils.forceDelete(files.next());
198                     }
199                 } catch (IOException e) {
200                     throw new MojoExecutionException(
201                             "Cannot delete a file: " + e.getMessage());
202                 }
203             }
204         }
205 
206         /**
207          * Build documents from DocBook XML sources.
208          *
209          * @throws MojoExecutionException Failed to build the output.
210          */
211         void build() throws MojoExecutionException {
212 
213             for (String docName : m.getDocNames()) {
214                 ArrayList<MojoExecutor.Element> cfg = new ArrayList<MojoExecutor.Element>();
215                 cfg.addAll(m.getBaseConfiguration());
216                 cfg.add(element(name("foCustomization"), m.path(m.getFoCustomization())));
217                 cfg.add(element(name("fop1Extensions"), "1"));
218 
219                 if (getFormat().equalsIgnoreCase("pdf")) {
220                     cfg.add(element(name("insertOlinkPdfFrag"), "1"));
221                 }
222 
223                 // Due to https://code.google.com/p/docbkx-tools/issues/detail?id=112
224                 // RTF generation does not work with docbkx-tools 2.0.15 or 2.0.16.
225                 // New features like <fopLogLevel> cannot be used with RTF for now.
226                 if (!getFormat().equalsIgnoreCase("rtf")) {
227                     cfg.add(element(name("fopLogLevel"), m.getFopLogLevel()));
228                 }
229 
230                 // Due to https://code.google.com/p/docbkx-tools/issues/detail?id=112
231                 // skip olink resolution with RTF for now.
232                 if (!getFormat().equalsIgnoreCase("rtf")) {
233                     cfg.add(element(name("targetDatabaseDocument"), getTargetDatabaseDocument()));
234                 }
235 
236                 cfg.add(element(name("targetDirectory"), m.path(m.getDocbkxOutputDirectory()) + "/" + getFormat()));
237 
238                 final String fontDir = m.path(m.getFontsDirectory());
239                 cfg.add(Fop.getFontsElement(fontDir));
240 
241                 cfg.add(element(name("includes"), docName + "/" + m.getDocumentSrcName()));
242                 cfg.add(element(name("currentDocid"), docName));
243 
244                 // Due to https://code.google.com/p/docbkx-tools/issues/detail?id=112
245                 // if the format is RTF, stick with 2.0.14 for now.
246                 String docbkxVersion = m.getDocbkxVersion();
247                 if (format.equalsIgnoreCase("rtf")) {
248                     docbkxVersion = "2.0.14";
249                 }
250 
251                 executeMojo(
252                         plugin(
253                                 groupId("com.agilejava.docbkx"),
254                                 artifactId("docbkx-maven-plugin"),
255                                 version(docbkxVersion),
256                                 dependencies(
257                                         dependency(
258                                                 groupId("net.sf.offo"),
259                                                 artifactId("fop-hyph"),
260                                                 version(m.getFopHyphVersion())))),
261                         goal("generate-" + getFormat()),
262                         configuration(cfg.toArray(new Element[cfg.size()])),
263                         executionEnvironment(m.getProject(), m.getSession(), m.getPluginManager()));
264 
265                 // Avoid each new document overwriting the last.
266                 File file = FileUtils.getFile(
267                         m.getDocbkxOutputDirectory(), getFormat(),
268                         FilenameUtils.getBaseName(m.getDocumentSrcName()) + "." + getFormat());
269 
270                 try {
271                     NameUtils.renameDocument(file, docName, m.getProjectName());
272                 } catch (IOException e) {
273                     throw new MojoExecutionException("Failed to rename document", e);
274                 }
275             }
276         }
277     }
278 }