001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014-2015 ForgeRock AS. 015 */ 016package org.forgerock.doc.maven.utils; 017 018import freemarker.template.Configuration; 019import freemarker.template.Template; 020import freemarker.template.TemplateException; 021import freemarker.template.TemplateExceptionHandler; 022import org.apache.commons.io.FileUtils; 023import org.apache.commons.io.FilenameUtils; 024import org.apache.maven.plugin.MojoExecutionException; 025import org.forgerock.doc.maven.AbstractDocbkxMojo; 026import org.forgerock.doc.maven.utils.helper.NameMethod; 027 028import java.io.File; 029import java.io.FileOutputStream; 030import java.io.IOException; 031import java.io.OutputStreamWriter; 032import java.io.Writer; 033import java.util.ArrayList; 034import java.util.HashMap; 035import java.util.List; 036import java.util.Map; 037import java.util.Set; 038 039/** 040 * Build olink target database files. 041 * 042 * <br> 043 * 044 * O(utside)links are a DocBook convention for describing links between XML documents. 045 * For background, read Bob Stayton's explanation of 046 * <a href="http://www.sagehill.net/docbookxsl/Olinking.html" 047 * >Olinking between documents</a>. 048 * 049 * <br> 050 * 051 * The olink resolution mechanism described therein and used here 052 * depends on the relative locations of output files. 053 */ 054public final class OLinkUtils { 055 056 /** 057 * Construct an olink target database document, except for chunked HTML. 058 * 059 * <br> 060 * 061 * The document is an XML file that wraps target data documents. 062 * On a first pass, the docbkx-tools plugin applies the DocBook stylesheets 063 * with settings to generate such target data documents 064 * for each top-level DocBook document in a build. 065 * On a second pass, the docbkx-tools plugin applies the DocBook stylesheets 066 * with settings to resolve olinks in output documents. 067 * During the second pass, the docbkx-tools plugin requires 068 * this wrapper document to handle the target data documents 069 * with the correct relative locations. 070 * 071 * <br> 072 * 073 * You pass the path to the target database document to docbkx-tools 074 * as the value of the {@code <targetDatabaseDocument>} parameter. 075 * 076 * @param file The file in which to write the target database. 077 * @param format Output format such as {@code pdf}, {@code html}. 078 * @param mojo Mojo with configuration information about the project. 079 * 080 * @throws IOException Failed to write to the target database file. 081 * @throws MojoExecutionException Failed to read document names from the mojo. 082 */ 083 public static void createTargetDatabase(File file, 084 String format, 085 AbstractDocbkxMojo mojo) 086 throws IOException, MojoExecutionException { 087 createTargetDatabase(file, format, mojo, false); 088 } 089 090 /** 091 * Construct an olink target database document for chunked HTML. 092 * 093 * <br> 094 * 095 * The document is an XML file that wraps target data documents. 096 * On a first pass, the docbkx-tools plugin applies the DocBook stylesheets 097 * with settings to generate such target data documents 098 * for each top-level DocBook document in a build. 099 * 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 <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 <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}