// This file contains the buildDocbookDoc function that is used for ... building // Docbook documentation! import java.util.regex.Pattern import org.entityfs.el.* import org.entityfs.util.filter.entity.* import org.entityfs.util.* import org.entityfs.util.cap.entity.ECFileResolvableUtil import org.schmant.arg.DirectoryAndFilter import org.schmant.run.* import org.schmant.support.FutureFile import org.schmant.support.entityfs.SchmantFileSystems import org.schmant.support.xml.XmlCatalogResolver import org.schmant.task.io.* import org.schmant.task.meta.* import org.schmant.task.proxy.* import org.schmant.task.text.TextReplaceTF import org.schmant.task.text.apilinks.* import org.schmant.task.xml.catalog.* import org.schmant.task.xml.dom.DomParseXmlTF import org.schmant.task.xml.xslt.* // The XSL transformer factory class final def TRANSFORMER_FACTORY_CLASS = "org.apache.xalan.processor.TransformerFactoryImpl" // The document builder factory class final def DOCUMENT_BUILDER_FACTORY_CLASS = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl" def createSingleHtmlXsltTask() { return new XsltTF(). setParameter("html.stylesheet", "../css/glue_stick_doc.css"). setParameter("img.src.path", ""). setParameter("use.role.for.mediaobject", "1"). setParameter("chunk.section.depth", "0"). setParameter("chunker.output.encoding", "UTF-8"). setParameter("html.ext", ".xhtml") } // Function for building all Docbook documentation // Returns a dependency that is satisfied when all documentation has been built. buildDocbookDoc = { docProject, destDocDir, TaskExecutor te -> // Directories final def destPgDir = Directories.getDirectory(destDocDir, "pg") final def destBdlDir = Directories.getDirectory(destDocDir, "bdl") final def bdlDirs = Directories.listEntities(Directories.getDirectory(docProject, "bdl"), DirectoryFilter.FILTER) // Need Saxon 6 TransformerFactory or the Xalan TransformerFactory as // they seem to be the only TransformerFactories that give parsers that // can parse Docbook XSL:s. final def catalogBuildDep = new CompoundTaskDependency() // A catalog resolver that will be used for resolving external entities. // Instruct it to use Files instead of InputStreams for creating URI // Source:s final def xmlCatalogResolver = new XmlCatalogResolver().setUseFilesInsteadOfStreamsForUris(true) // xmlCatalogResolver.setVerbose(true) if (!props.containsKey("docbookXsl")) { warn("The docbookXsl property is not set. Defaulting to /usr/share/xml/docbook") } def docbookXsl = SchmantFileSystems.getEntityForDirectory(new File(props.getStringValue("docbookXsl", "/usr/share/xml/docbook")), true) // Java cannot handle relative paths in XSL files // (see http://www.sagehill.net/docbookxsl/WriteCatalog.html ) // We have to create a temporary XML catalog for the XSL entities def t = new RecursiveActionTF(). setLogHeader("Adding Docbook stylesheets to XML catalog"). setSource(new DirectoryAndFilter(docbookXsl, EFileFilter.FILTER)). setTaskFactory( new AddUriToCatalogTF(). setXmlCatalog(xmlCatalogResolver). setBaseLocation(new AbsoluteLocation("/xhtml"))).create() catalogBuildDep.add(t) te.add(t) // Add Programmer's Guide entities to the XML catalog. Prefix all files with // http://pg/ so that the XML parser won't try to prepend the Java process' // working directory to entity references that it finds. t = new RecursiveActionTF(). setLogHeader("Adding Programmer's Guide entities to XML catalog"). setSource( new DirectoryAndFilter( Directories.getDirectory(docProject, "pg"), EFileFilter.FILTER)). setTaskFactory( new AddSystemIdToCatalogTF(). setXmlCatalog(xmlCatalogResolver). setLocationPrefix("http://pg/")).create() catalogBuildDep.add(t) te.add(t) // Add Bean Definition Language Guide entities to the XML catalog. Prefix // all files with http://bdl/ bdlDirs.each { dirName = Entities.getName(it) t = new RecursiveActionTF(). setLogHeader("Adding " + dirName + " entities to XML catalog"). setSource( new DirectoryAndFilter( Directories.getDirectory(it, "ex"), EFileFilter.FILTER)). setTaskFactory( new AddSystemIdToCatalogTF(). setXmlCatalog(xmlCatalogResolver). setLocationPrefix("http://bdl/" + dirName + "/ex/")).create() catalogBuildDep.add(t) te.add(t) } def parseDeps = new CompoundTaskDependency() // Parse Docbook single HTML stylesheet def parseSingleHtml = new TemplateCompilerTF(). setLogHeader("Parsing Docbook single HTML XSL stylesheet"). setSource(Directories.getFile(docbookXsl, new RelativeLocation("xhtml/docbook.xsl"))). setTransformerFactoryClassName(TRANSFORMER_FACTORY_CLASS). setXslUriResolver(xmlCatalogResolver).create() te.add(parseSingleHtml, catalogBuildDep) parseDeps.add(parseSingleHtml) // Parse Docbook chunked HTML stylesheet def parseChunkedHtml = new TemplateCompilerTF(). setLogHeader("Parsing Docbook chunked HTML XSL stylesheet"). setTransformerFactoryClassName(TRANSFORMER_FACTORY_CLASS). setSource(Directories.getFile(docbookXsl, new RelativeLocation("xhtml/chunk.xsl"))). setXslUriResolver(xmlCatalogResolver).create() te.add(parseChunkedHtml, catalogBuildDep) parseDeps.add(parseChunkedHtml) def parseProgrammersGuide = new DomParseXmlTF(). setLogHeader("Parsing Programmer's Guide XML"). setDocumentBuilderFactoryClassName(DOCUMENT_BUILDER_FACTORY_CLASS). setNamespaceAware(true). setEntityResolver(xmlCatalogResolver). setSource(Directories.getFile(docProject, new RelativeLocation("pg/pg.xml"))).create() te.add(parseProgrammersGuide, catalogBuildDep) parseDeps.add(parseProgrammersGuide) parseBdlGuides = [:] // Parse Bean Definition Language Guides bdlDirs.each { dirName = Entities.getName(it) t = new DomParseXmlTF(). setLogHeader("Parsing " + it + " Bean Definition Language Guide"). setDocumentBuilderFactoryClassName(DOCUMENT_BUILDER_FACTORY_CLASS). setNamespaceAware(true). setEntityResolver(xmlCatalogResolver). setSource(Directories.getFileMatching(it, "*.xml")).create() te.add(t, catalogBuildDep) parseDeps.add(t) parseBdlGuides[dirName] = t } // The XSL transformation does not seem to be thread safe. Add all // transformation tasks to this compound task to make them run sequentially def transforms = new CompoundTF() def programmersGuideToMultipleHtml = new XsltTF(). setLogHeader("Creating Programmer's Guide multiple page HTML book"). setTarget(new FutureFile(destPgDir, "pg.xhtml")). setTransformerFactoryClassName(TRANSFORMER_FACTORY_CLASS). setParameter("html.stylesheet", "../css/glue_stick_doc.css"). setParameter("img.src.path", ""). setParameter("use.role.for.mediaobject", "1"). setParameter("chunk.section.depth", "0"). setParameter("chunker.output.encoding", "UTF-8"). setParameter("html.ext", ".xhtml"). setParameter("base.dir", ECFileResolvableUtil.getFileObject(destPgDir).getAbsolutePath() + File.separator). setSource(parseProgrammersGuide). setUriResolver(xmlCatalogResolver). setTemplates(parseChunkedHtml).create() transforms.addTask(programmersGuideToMultipleHtml) def programmersGuideToSingleHtml = createSingleHtmlXsltTask(). setLogHeader("Creating Programmer's Guide single page HTML book"). setTransformerFactoryClassName(TRANSFORMER_FACTORY_CLASS). setTarget(new FutureFile(destPgDir, "pg-single.xhtml")). setParameter("base.dir", ECFileResolvableUtil.getFileObject(destPgDir).getAbsolutePath() + File.separator). setSource(parseProgrammersGuide). setUriResolver(xmlCatalogResolver). setTemplates(parseSingleHtml).create() transforms.addTask(programmersGuideToSingleHtml) bdlDirs.each { dirName = Entities.getName(it) t = createSingleHtmlXsltTask(). setLogHeader("Creating " + dirName + " Bean Definition Language Guide"). setTransformerFactoryClassName(TRANSFORMER_FACTORY_CLASS). setTarget(new FutureFile(destBdlDir, dirName + ".xhtml")). setParameter("base.dir", ECFileResolvableUtil.getFileObject(destBdlDir).getAbsolutePath() + File.separator). setSource(parseBdlGuides[dirName]). setUriResolver(xmlCatalogResolver). setTemplates(parseSingleHtml).create() transforms.addTask(t) } transforms = transforms.create() te.add(transforms, parseDeps) // An pg.xhtml file in the Programmer's Guide directory. Delete it. def deleteProgrammersGuideJunk = te.add( { Entities.delete(Directories.getFile(destPgDir, "pg.xhtml")) }, transforms) // Copy Programmer's Guide images to documentation // def copyPgImages = te.add(new CopyTF(). // setLogHeader("Copying Programmer's Guide images"). // addSources(Directories.getAllFilesMatching(Directories.getDirectory(docProject, new RelativeLocation("pg/img")), "*.gif")). // setTarget(Directories.newDirectory(destPgDir, "img"))) def copyPgImages = AlreadySatisfiedTaskDependency.INSTANCE // Add the character encoding meta tag to the XHTML pages def addCharacterEncodingTag = new RecursiveActionTF(). setLogHeader("Post-processing documentation"). addSource(new DirectoryAndFilter(destPgDir, new EFileNameExtensionFilter("xhtml"))). addSource(new DirectoryAndFilter(destBdlDir, new EFileNameExtensionFilter("xhtml"))). setTaskExecutor(te). setTaskFactory( new ReplaceSourceFileTF(). setTaskFactory( new TextReplaceTF(). addReplace( Pattern.compile("<\\s*head(\\p{Print}|\\s)*?>", Pattern.CASE_INSENSITIVE), // \\0 returns the zeroth capture group, i.e. the // entire matched string "\\0"))).create() te.add(addCharacterEncodingTag, [deleteProgrammersGuideJunk]) def addCharacterEncodingDependency = addCharacterEncodingTag.getDependencyForTasksScheduledByThisTask() // Replace fully qualified class names for API links def insertApiLinks = new RecursiveActionTF(). setLogHeader("Inserting API links"). addSource(new DirectoryAndFilter(destPgDir, new EFileNameExtensionFilter("xhtml"))). addSource(new DirectoryAndFilter(destBdlDir, new EFileNameExtensionFilter("xhtml"))). setTaskExecutor(te). setTaskFactory( new ReplaceSourceFileTF(). setTaskFactory( new ApiLinksTF(). setLinkClass("apilink"). addApiLink(new ApiLink("org.gluestickdi.", "../api/index.html")). addApiLink(new ApiLink("java.awt.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("java.io.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("java.net.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("java.nio.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("java.text.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("java.util.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("javax.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("org.xml.", "http://java.sun.com/javase/6/docs/api/index.html")). addApiLink(new ApiLink("org.w3c.dom.", "http://java.sun.com/javase/6/docs/api/index.html")))).create() te.add(insertApiLinks, addCharacterEncodingDependency) def insertApiLinksDependency = insertApiLinks.getDependencyForTasksScheduledByThisTask() // Insert accordions def insertAccordions = new RecursiveActionTF(). setLogHeader("Inserting accordions"). // No accordions in bean definition language guides. addSource(new DirectoryAndFilter(destPgDir, new EFileNameExtensionFilter("xhtml"))). setTaskExecutor(te). setTaskFactory( new ReplaceSourceFileTF(). setTaskFactory { cParams -> def sourceFile = cParams.source def targetFile = cParams.target def fileContents = Files.readTextFile(sourceFile) if (fileContents.contains("|accordion:")) { // Which accordions are there? def accordionIds = [] as Set def matches = fileContents =~ /\|accordion:(.+?)\|/ accordionIds.addAll(matches.collect{it[1]}) // Create a task that will insert the following // in the page: // * Import of the jQuery UI scripts and // stylesheets // * An accordion enabling script at the top of // the page // * Surrounding tags for each accordion // * Surrounding tags for each accordion section // Scripts for enabling all accordions: def accordionEnableScripts = '" new TextReplaceTF(). setLogHeader("Inserting accordions into " + sourceFile). setSource(sourceFile). setTarget(targetFile). // Insert the script imports after the title addReplace( Pattern.compile("", Pattern.CASE_INSENSITIVE), '\n\n\n\n'). // Insert the enabling script before the // head end tag addReplace( Pattern.compile("", Pattern.CASE_INSENSITIVE), accordionEnableScripts + "\n"). // The start of an accordion block addReplace( Pattern.compile("\\|accordion:(.+?)\\|"), // Capturing group 1 contains the id of // the accordion '
'). // The start of an accordion section addReplace( Pattern.compile("\\|as:(.+?)\\|"), // Capturing group 1 contains the header // for the section '

\\1

'). // The end of an accordion section addReplace( Pattern.compile("\\|/as\\|"), "
"). // The end of an accordion block addReplace( Pattern.compile("\\|/accordion\\|"), "
").run() } else { // No accordions in this file. Just copy it to // the target. def targetDir = targetFile.parent Entities.copy(sourceFile, targetDir, targetFile.name) } }).create() te.add(insertAccordions, insertApiLinksDependency) def insertAccordionsDependency = insertAccordions.getDependencyForTasksScheduledByThisTask() return new CompoundTaskDependency().addAll([ insertAccordionsDependency, copyPgImages]) }