OLinkUtils.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2014-2015 ForgeRock AS.
*/
package org.forgerock.doc.maven.utils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.forgerock.doc.maven.AbstractDocbkxMojo;
import org.forgerock.doc.maven.utils.helper.NameMethod;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Build olink target database files.
*
* <br>
*
* O(utside)links are a DocBook convention for describing links between XML documents.
* For background, read Bob Stayton's explanation of
* <a href="http://www.sagehill.net/docbookxsl/Olinking.html"
* >Olinking between documents</a>.
*
* <br>
*
* The olink resolution mechanism described therein and used here
* depends on the relative locations of output files.
*/
public final class OLinkUtils {
/**
* Construct an olink target database document, except for chunked HTML.
*
* <br>
*
* The document is an XML file that wraps target data documents.
* On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to generate such target data documents
* for each top-level DocBook document in a build.
* On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to resolve olinks in output documents.
* During the second pass, the docbkx-tools plugin requires
* this wrapper document to handle the target data documents
* with the correct relative locations.
*
* <br>
*
* You pass the path to the target database document to docbkx-tools
* as the value of the {@code <targetDatabaseDocument>} parameter.
*
* @param file The file in which to write the target database.
* @param format Output format such as {@code pdf}, {@code html}.
* @param mojo Mojo with configuration information about the project.
*
* @throws IOException Failed to write to the target database file.
* @throws MojoExecutionException Failed to read document names from the mojo.
*/
public static void createTargetDatabase(File file,
String format,
AbstractDocbkxMojo mojo)
throws IOException, MojoExecutionException {
createTargetDatabase(file, format, mojo, false);
}
/**
* Construct an olink target database document for chunked HTML.
*
* <br>
*
* The document is an XML file that wraps target data documents.
* On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to generate such target data documents
* for each top-level DocBook document in a build.
* On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to resolve olinks in output documents.
* During the second pass, the docbkx-tools plugin requires
* this wrapper document to handle the target data documents
* with the correct relative locations.
*
* <br>
*
* You pass the path to the target database document to docbkx-tools
* as the value of the {@code <targetDatabaseDocument>} parameter.
*
* @param file The file in which to write the target database.
* @param format Output format such as {@code pdf}, {@code html}.
* @param mojo Mojo with configuration information about the project.
* @param isChunkedHtml Set {@code true} for chunked HTML.
*
* @throws IOException Failed to write to the target database file.
* @throws MojoExecutionException Failed to read document names from the mojo.
*/
public static void createTargetDatabase(File file,
String format,
AbstractDocbkxMojo mojo,
boolean isChunkedHtml)
throws IOException, MojoExecutionException {
createTargetDatabase(
file,
mojo.getBuildDirectory().getAbsolutePath(),
mojo.getDocNames(),
FilenameUtils.getBaseName(mojo.getDocumentSrcName()),
format,
isChunkedHtml,
mojo.getProjectName(),
mojo.getProjectVersion());
}
/**
* Construct an olink target database document.
*
* <br>
*
* The document is an XML file that wraps target data documents.
* On a first pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to generate such target data documents
* for each top-level DocBook document in a build.
* On a second pass, the docbkx-tools plugin applies the DocBook stylesheets
* with settings to resolve olinks in output documents.
* During the second pass, the docbkx-tools plugin requires
* this wrapper document to handle the target data documents
* with the correct relative locations.
*
* <br>
*
* You pass the path to the target database document to docbkx-tools
* as the value of the {@code <targetDatabaseDocument>} parameter.
*
* @param file The file in which to write the target database.
* @param basePath Full path to parent of docbkx directory,
* such as {@code /path/to/target}.
* @param docNames Names of documents in the set.
* @param documentSrcName Top-level DocBook XML source document base name.
* @param format Output format such as {@code pdf}, {@code html}.
* @param isChunked Set {@code true} for chunked HTML.
* @param projectName Name of the current project, such as {@code OpenAM}.
* @param projectVersion Version for the current project. Can be empty.
*
* @throws IOException Failed to write to the target database file.
*/
private static void createTargetDatabase(File file,
String basePath,
Set<String> docNames,
String documentSrcName,
String format,
boolean isChunked,
String projectName,
String projectVersion)
throws IOException {
// This implementation uses FreeMarker templates.
// For details, see http://freemarker.org.
// FreeMarker templates require a configuration.
Configuration configuration = getConfiguration();
// FreeMarker templates are data driven. The model here is a map.
Map<String, Object> map = getModel(
basePath,
docNames,
documentSrcName,
format,
isChunked,
projectName,
projectVersion);
// Apply the FreeMarker template using the data.
Template template = configuration.getTemplate("olinkdb.ftl");
FileOutputStream out = FileUtils.openOutputStream(file);
Writer writer = new OutputStreamWriter(out);
try {
template.process(map, writer);
} catch (TemplateException e) {
throw new IOException("Failed to write template", e);
} finally {
writer.close();
out.close();
}
}
private static Configuration configuration;
/**
* Get a FreeMarker configuration for applying templates.
*
* @return A FreeMarker configuration.
*/
private static Configuration getConfiguration() {
if (configuration != null) {
return configuration;
}
configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
configuration.setClassForTemplateLoading(OLinkUtils.class, "/templates");
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
return configuration;
}
/**
* Get the FreeMaker data for use when applying templates.
*
* @param basePath Full path to parent of docbkx directory,
* such as {@code /path/to/target}.
* @param docNames Names of documents in the set.
* @param documentSrcName Top-level DocBook XML source document base name.
* @param format Output format such as {@code pdf}, {@code html}.
* @param isChunked Set {@code true} for chunked HTML.
* @param projectName Name of the current project, such as {@code OpenAM}.
* @param projectVersion Version for the current project. Can be empty.
*
* @return FreeMarker data for use when applying templates.
*/
private static Map<String, Object> getModel(String basePath,
Set<String> docNames,
String documentSrcName,
String format,
boolean isChunked,
String projectName,
String projectVersion) {
/*
baseName: base name for document source, such as index
basePath: base of absolute path to target data file
docNames: list of document names such as reference and admin-guide
extension: output file extension such as html, pdf, or xhtml
format: output format such as xhtml5 or epub
isChunked: whether the output format is chunked HTML
name(): wrapper to call NameUtils.renameDoc()
projectName: project name such as OpenAM
projectVersion: project version such as 3.1.0
type: output file type such as html, pdf, or xhtml
*/
Map<String, Object> map = new HashMap<String, Object>();
map.put("baseName", documentSrcName);
map.put("basePath", basePath);
List<String> docs = new ArrayList<String>(docNames.size());
docs.addAll(docNames);
map.put("docNames", docs);
String extension = format;
if (format.equals("xhtml5")) {
format = "xhtml";
extension = "xhtml";
}
if (format.equals("bootstrap")) {
extension = "html";
}
map.put("extension", extension);
map.put("format", format);
map.put("isChunked", isChunked);
map.put("name", new NameMethod());
map.put("projectName", projectName);
map.put("projectVersion", projectVersion);
String type = format;
if (format.equals("pdf") || format.equals("rtf")) {
type = "fo";
}
if (format.equals("xhtml")) {
type = "xhtml";
}
if (format.equals("bootstrap")) {
type = "html";
}
map.put("type", type);
return map;
}
private OLinkUtils() {
// Not used.
}
}