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 2012-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.doc.maven.build; 018 019import org.apache.commons.io.FileUtils; 020import org.apache.commons.io.FilenameUtils; 021import org.apache.maven.plugin.MojoExecutionException; 022import org.forgerock.doc.maven.AbstractDocbkxMojo; 023import org.forgerock.doc.maven.pre.Fop; 024import org.forgerock.doc.maven.utils.NameUtils; 025import org.forgerock.doc.maven.utils.OLinkUtils; 026import org.twdata.maven.mojoexecutor.MojoExecutor; 027 028import java.io.File; 029import java.io.IOException; 030import java.util.ArrayList; 031import java.util.Iterator; 032 033/** 034 * Build FO output formats. 035 */ 036public class Fo { 037 038 /** 039 * The Mojo that holds configuration and related methods. 040 */ 041 private AbstractDocbkxMojo m; 042 043 /** 044 * The Executor to run docbkx-tools. 045 */ 046 private final Executor executor; 047 048 /** 049 * Constructor setting the Mojo that holds the configuration. 050 * 051 * @param mojo The Mojo that holds the configuration. 052 */ 053 public Fo(final AbstractDocbkxMojo mojo) { 054 m = mojo; 055 this.executor = new Executor(); 056 } 057 058 /** 059 * Supported FO formats include "pdf" and "rtf". 060 */ 061 private String format = "pdf"; 062 063 /** 064 * Get the format. 065 * Defaults to PDF unless the format has been set to RTF. 066 * 067 * @return The format, either "pdf" or "rtf". 068 */ 069 public String getFormat() { 070 return format; 071 } 072 073 /** 074 * Set the format to PDF or RTF. 075 * Defaults to PDF unless RTF is specified (case does not matter). 076 * 077 * @param format Either {@code pdf} or {@code rtf}. 078 */ 079 public void setFormat(final String format) { 080 if (format.equalsIgnoreCase("rtf")) { 081 this.format = "rtf"; 082 } else { 083 this.format = "pdf"; 084 } 085 } 086 087 /** 088 * Build documents from DocBook XML sources. 089 * 090 * @throws MojoExecutionException Failed to build output. 091 */ 092 public void execute() throws MojoExecutionException { 093 executor.prepareOlinkDB(); 094 executor.build(); 095 } 096 097 /** 098 * Get absolute path to an Olink target database XML document 099 * 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}