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 <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 <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 }