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 2014-2015 ForgeRock AS.
15   */
16  package org.forgerock.doc.maven.utils;
17  
18  import freemarker.template.Configuration;
19  import freemarker.template.Template;
20  import freemarker.template.TemplateException;
21  import freemarker.template.TemplateExceptionHandler;
22  import org.apache.commons.io.FileUtils;
23  import org.apache.commons.io.FilenameUtils;
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.forgerock.doc.maven.AbstractDocbkxMojo;
26  import org.forgerock.doc.maven.utils.helper.NameMethod;
27  
28  import java.io.File;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.io.OutputStreamWriter;
32  import java.io.Writer;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  
39  /**
40   * Build olink target database files.
41   *
42   * <br>
43   *
44   * O(utside)links are a DocBook convention for describing links between XML documents.
45   * For background, read Bob Stayton's explanation of
46   * <a href="http://www.sagehill.net/docbookxsl/Olinking.html"
47   * >Olinking between documents</a>.
48   *
49   * <br>
50   *
51   * The olink resolution mechanism described therein and used here
52   * depends on the relative locations of output files.
53   */
54  public final class OLinkUtils {
55  
56      /**
57       * Construct an olink target database document, except for chunked HTML.
58       *
59       * <br>
60       *
61       * The document is an XML file that wraps target data documents.
62       * On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
63       * with settings to generate such target data documents
64       * for each top-level DocBook document in a build.
65       * On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
66       * with settings to resolve olinks in output documents.
67       * During the second pass, the docbkx-tools plugin requires
68       * this wrapper document to handle the target data documents
69       * with the correct relative locations.
70       *
71       * <br>
72       *
73       * You pass the path to the target database document to docbkx-tools
74       * as the value of the {@code &lt;targetDatabaseDocument>} parameter.
75       *
76       * @param file              The file in which to write the target database.
77       * @param format            Output format such as {@code pdf}, {@code html}.
78       * @param mojo              Mojo with configuration information about the project.
79       *
80       * @throws IOException              Failed to write to the target database file.
81       * @throws MojoExecutionException   Failed to read document names from the mojo.
82       */
83      public static void createTargetDatabase(File file,
84                                              String format,
85                                              AbstractDocbkxMojo mojo)
86              throws IOException, MojoExecutionException {
87          createTargetDatabase(file, format, mojo, false);
88      }
89  
90      /**
91       * Construct an olink target database document for chunked HTML.
92       *
93       * <br>
94       *
95       * The document is an XML file that wraps target data documents.
96       * On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
97       * with settings to generate such target data documents
98       * for each top-level DocBook document in a build.
99       * On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
100      * with settings to resolve olinks in output documents.
101      * During the second pass, the docbkx-tools plugin requires
102      * this wrapper document to handle the target data documents
103      * with the correct relative locations.
104      *
105      * <br>
106      *
107      * You pass the path to the target database document to docbkx-tools
108      * as the value of the {@code &lt;targetDatabaseDocument>} parameter.
109      *
110      * @param file              The file in which to write the target database.
111      * @param format            Output format such as {@code pdf}, {@code html}.
112      * @param mojo              Mojo with configuration information about the project.
113      * @param isChunkedHtml     Set {@code true} for chunked HTML.
114      *
115      * @throws IOException              Failed to write to the target database file.
116      * @throws MojoExecutionException   Failed to read document names from the mojo.
117      */
118     public static void createTargetDatabase(File file,
119                                             String format,
120                                             AbstractDocbkxMojo mojo,
121                                             boolean isChunkedHtml)
122             throws IOException, MojoExecutionException {
123 
124         createTargetDatabase(
125                 file,
126                 mojo.getBuildDirectory().getAbsolutePath(),
127                 mojo.getDocNames(),
128                 FilenameUtils.getBaseName(mojo.getDocumentSrcName()),
129                 format,
130                 isChunkedHtml,
131                 mojo.getProjectName(),
132                 mojo.getProjectVersion());
133     }
134 
135     /**
136      * Construct an olink target database document.
137      *
138      * <br>
139      *
140      * The document is an XML file that wraps target data documents.
141      * On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
142      * with settings to generate such target data documents
143      * for each top-level DocBook document in a build.
144      * On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
145      * with settings to resolve olinks in output documents.
146      * During the second pass, the docbkx-tools plugin requires
147      * this wrapper document to handle the target data documents
148      * with the correct relative locations.
149      *
150      * <br>
151      *
152      * You pass the path to the target database document to docbkx-tools
153      * as the value of the {@code &lt;targetDatabaseDocument>} parameter.
154      *
155      * @param file              The file in which to write the target database.
156      * @param basePath          Full path to parent of docbkx directory,
157      *                          such as {@code /path/to/target}.
158      * @param docNames          Names of documents in the set.
159      * @param documentSrcName   Top-level DocBook XML source document base name.
160      * @param format            Output format such as {@code pdf}, {@code html}.
161      * @param isChunked         Set {@code true} for chunked HTML.
162      * @param projectName       Name of the current project, such as {@code OpenAM}.
163      * @param projectVersion    Version for the current project. Can be empty.
164      *
165      * @throws IOException  Failed to write to the target database file.
166      */
167     private static void createTargetDatabase(File file,
168                                             String basePath,
169                                             Set<String> docNames,
170                                             String documentSrcName,
171                                             String format,
172                                             boolean isChunked,
173                                             String projectName,
174                                             String projectVersion)
175             throws IOException {
176 
177         // This implementation uses FreeMarker templates.
178         // For details, see http://freemarker.org.
179 
180         // FreeMarker templates require a configuration.
181         Configuration configuration = getConfiguration();
182 
183         // FreeMarker templates are data driven. The model here is a map.
184         Map<String, Object> map = getModel(
185                 basePath,
186                 docNames,
187                 documentSrcName,
188                 format,
189                 isChunked,
190                 projectName,
191                 projectVersion);
192 
193         // Apply the FreeMarker template using the data.
194         Template template = configuration.getTemplate("olinkdb.ftl");
195         FileOutputStream out = FileUtils.openOutputStream(file);
196         Writer writer = new OutputStreamWriter(out);
197         try {
198             template.process(map, writer);
199         } catch (TemplateException e) {
200             throw new IOException("Failed to write template", e);
201         } finally {
202             writer.close();
203             out.close();
204         }
205     }
206 
207     private static Configuration configuration;
208 
209     /**
210      * Get a FreeMarker configuration for applying templates.
211      *
212      * @return              A FreeMarker configuration.
213      */
214     private static Configuration getConfiguration() {
215         if (configuration != null) {
216             return configuration;
217         }
218 
219         configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
220         configuration.setClassForTemplateLoading(OLinkUtils.class, "/templates");
221         configuration.setDefaultEncoding("UTF-8");
222         configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
223 
224         return configuration;
225     }
226 
227     /**
228      * Get the FreeMaker data for use when applying templates.
229      *
230      * @param basePath          Full path to parent of docbkx directory,
231      *                          such as {@code /path/to/target}.
232      * @param docNames          Names of documents in the set.
233      * @param documentSrcName   Top-level DocBook XML source document base name.
234      * @param format            Output format such as {@code pdf}, {@code html}.
235      * @param isChunked         Set {@code true} for chunked HTML.
236      * @param projectName       Name of the current project, such as {@code OpenAM}.
237      * @param projectVersion    Version for the current project. Can be empty.
238      *
239      * @return                  FreeMarker data for use when applying templates.
240      */
241     private static Map<String, Object> getModel(String basePath,
242                                                 Set<String> docNames,
243                                                 String documentSrcName,
244                                                 String format,
245                                                 boolean isChunked,
246                                                 String projectName,
247                                                 String projectVersion) {
248 
249         /*
250          baseName:        base name for document source, such as index
251          basePath:        base of absolute path to target data file
252          docNames:        list of document names such as reference and admin-guide
253          extension:       output file extension such as html, pdf, or xhtml
254          format:          output format such as xhtml5 or epub
255          isChunked:       whether the output format is chunked HTML
256          name():          wrapper to call NameUtils.renameDoc()
257          projectName:     project name such as OpenAM
258          projectVersion:  project version such as 3.1.0
259          type:            output file type such as html, pdf, or xhtml
260          */
261 
262         Map<String, Object> map = new HashMap<String, Object>();
263 
264         map.put("baseName", documentSrcName);
265         map.put("basePath", basePath);
266 
267         List<String> docs = new ArrayList<String>(docNames.size());
268         docs.addAll(docNames);
269         map.put("docNames", docs);
270 
271         String extension = format;
272         if (format.equals("xhtml5")) {
273             format = "xhtml";
274             extension = "xhtml";
275         }
276         if (format.equals("bootstrap")) {
277             extension = "html";
278         }
279         map.put("extension", extension);
280 
281         map.put("format", format);
282         map.put("isChunked", isChunked);
283 
284         map.put("name", new NameMethod());
285 
286         map.put("projectName", projectName);
287         map.put("projectVersion", projectVersion);
288 
289         String type = format;
290         if (format.equals("pdf") || format.equals("rtf")) {
291             type = "fo";
292         }
293         if (format.equals("xhtml")) {
294             type = "xhtml";
295         }
296         if (format.equals("bootstrap")) {
297             type = "html";
298         }
299         map.put("type", type);
300 
301         return map;
302     }
303 
304     private OLinkUtils() {
305         // Not used.
306     }
307 }