From 8b2c9052ce54881ff8ef57e542163669e8a73d17 Mon Sep 17 00:00:00 2001 From: Salman Qureshi Date: Tue, 24 Jul 2012 12:15:01 +0000 Subject: [PATCH] Xproc search and replace Added custom extension for calabash to handle search and replace functionality. Added xproc pipeline/step to pick pom filename configuration and call calabash extension java code. Linked xproc and calabash extension using the configuration.xml --- .../com/rackspace/cloud/api/docs/PDFMojo.java | 14 +- .../cloud/api/docs/ReplaceTextXProcStep.java | 233 ++++++++++++++++++ .../rackspace/cloud/api/docs/WebHelpMojo.java | 9 + src/main/resources/etc/configuration.xml | 4 + src/main/resources/rackspace-library.xpl | 54 +++- src/main/resources/test.xpl | 5 + 6 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/rackspace/cloud/api/docs/ReplaceTextXProcStep.java diff --git a/src/main/java/com/rackspace/cloud/api/docs/PDFMojo.java b/src/main/java/com/rackspace/cloud/api/docs/PDFMojo.java index 7788075..ca14943 100644 --- a/src/main/java/com/rackspace/cloud/api/docs/PDFMojo.java +++ b/src/main/java/com/rackspace/cloud/api/docs/PDFMojo.java @@ -147,6 +147,13 @@ public abstract class PDFMojo extends AbstractFoMojo { * default-value="" */ private String canonicalUrlBase; + + /** + * @parameter + * expression="${generate-pdf.replacementsFile}" + * default-value="" + */ + private String replacementsFile; /** * @@ -380,7 +387,10 @@ public abstract class PDFMojo extends AbstractFoMojo { } transformer.setParameter("branding", branding); - transformer.setParameter("docbook.infile",sourceDocBook.getAbsolutePath()); + //transformer.setParameter("docbook.infile",sourceDocBook.getAbsolutePath()); + String srcFilename = sourceDocBook.getName(); + getLog().info("SOURCE FOR COVER PAGE: "+this.projectBuildDirectory+"/docbkx/"+srcFilename); + transformer.setParameter("docbook.infile", this.projectBuildDirectory+"/docbkx/"+srcFilename); transformer.transform (new StreamSource(coverImageTemplate), new StreamResult(coverImage)); } catch (TransformerConfigurationException e) @@ -406,8 +416,10 @@ public abstract class PDFMojo extends AbstractFoMojo { map.put("security", security); map.put("canonicalUrlBase", canonicalUrlBase); + map.put("replacementsFile", replacementsFile); map.put("failOnValidationError", failOnValidationError); map.put("project.build.directory", this.projectBuildDirectory); + map.put("inputSrcFile", inputFilename); //String outputDir=System.getProperty("project.build.outputDirectory "); return CalabashHelper.createSource(source, pathToPipelineFile, map); } diff --git a/src/main/java/com/rackspace/cloud/api/docs/ReplaceTextXProcStep.java b/src/main/java/com/rackspace/cloud/api/docs/ReplaceTextXProcStep.java new file mode 100644 index 0000000..868fdbb --- /dev/null +++ b/src/main/java/com/rackspace/cloud/api/docs/ReplaceTextXProcStep.java @@ -0,0 +1,233 @@ +package com.rackspace.cloud.api.docs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.saxon.s9api.QName; +import net.sf.saxon.s9api.SaxonApiException; +import net.sf.saxon.s9api.XdmNode; + +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; + +import com.xmlcalabash.core.XProcRuntime; +import com.xmlcalabash.io.ReadablePipe; +import com.xmlcalabash.io.WritablePipe; +import com.xmlcalabash.library.DefaultStep; +import com.xmlcalabash.model.RuntimeValue; +import com.xmlcalabash.runtime.XAtomicStep; +import com.xmlcalabash.util.ProcessMatch; +import com.xmlcalabash.util.ProcessMatchingNodes; + +public class ReplaceTextXProcStep extends DefaultStep { + private static final QName _replacements_file = new QName("", "replacements.file"); + private static Pattern XPATH_LINE = Pattern.compile("^XPATH=(.+)$"); + private static Pattern COMMENT_LINE = Pattern.compile("^#(.+)$"); + private static Pattern REPLACEMENT_LINE = Pattern.compile("(.+)->(.*)"); + + private ReadablePipe source = null; + private WritablePipe result = null; + private ProcessMatch matcher = null; + + private Log log = null; + + public Log getLog() + { + if ( log == null ) + { + log = new SystemStreamLog(); + } + + return log; + } + + + public ReplaceTextXProcStep(XProcRuntime runtime, XAtomicStep step) { + super(runtime,step); + } + + public void setInput(String port, ReadablePipe pipe) { + source = pipe; + } + + public void setOutput(String port, WritablePipe pipe) { + result = pipe; + } + + public void reset() { + source.resetReader(); + result.resetWriter(); + } + + public void run() throws SaxonApiException { + super.run(); + + List replacements = readReplacementsFile(getOption(_replacements_file, "replacements.config")); + XdmNode updatedDoc = makeReplacements (source.read(), replacements); + + result.write(updatedDoc); + } + + private List readReplacementsFile(String fileName) { + List xpathReplacements = new ArrayList(); + XPathReplacement currentXPath = new XPathReplacement("//text()"); + + File replacementsFile = new File(fileName); + long fileLength = replacementsFile.length(); + if(fileLength>0){ + BufferedReader br = null; + try { + getLog().info("REPLACEMENTS FILE = " + replacementsFile.getAbsolutePath()); + br = new BufferedReader(new FileReader(replacementsFile)); + + String line; + while((line = br.readLine()) != null) { + Matcher xpathLine = XPATH_LINE.matcher(line); + Matcher commentLine = COMMENT_LINE.matcher(line); + Matcher replacementLine = REPLACEMENT_LINE.matcher(line); + if (xpathLine.matches()) { + currentXPath = new XPathReplacement(xpathLine.group(1).trim()); + xpathReplacements.add(currentXPath); + } else if (commentLine.matches()) { + /*ignore comment line. + * Although this could have been handled in the default else below. + * Had to create an explicit case here so that any reference to the token separator "->" + * in comments does not cause any issues. + */ + } else if (replacementLine.matches()) { + currentXPath.add(replacementLine.group(1).trim(), replacementLine.group(2).trim()); + } else { + //ignore input line + } + } + + if (xpathReplacements.size()==0) { + getLog().info("SKIPPING REPLACEMENTS: Replacements file is empty or was not found at specified location '"+fileName+ "'."); + } + } catch (IOException e) { + getLog().error("Unable to process replacements config file", e); + } finally { + try { + br.close(); + } catch (IOException e) { + getLog().error("Unable to release/close replacements config file", e); + } + } + } else { + getLog().info("SKIPPING REPLACEMENTS: Replacements file is empty or was not found at specified location '"+fileName+ "'."); + } + + return xpathReplacements; + } + + private XdmNode makeReplacements(XdmNode doc, List replacements) { + for (XPathReplacement xpathRepl : replacements) { + matcher = new ProcessMatch(runtime, xpathRepl); + xpathRepl.setMatcher(matcher); + + matcher.match(doc, new RuntimeValue(xpathRepl.getXPath())); + doc = matcher.getResult(); + } + + return doc; + } +} + +class XPathReplacement implements Iterable, ProcessMatchingNodes { + String xpath; + List replacements; + ProcessMatch matcher; + + public XPathReplacement(String _xpath) { + this.xpath = _xpath; + replacements = new ArrayList(); + } + + public String getXPath() { + return xpath; + } + + public void setMatcher (ProcessMatch matcher) { + this.matcher = matcher; + } + + public void add(String oldVal, String newVal) { + replacements.add(new Replacement(oldVal, newVal)); + } + + private String computeReplacement(XdmNode node) { + String newValue = node.getStringValue(); + for (Replacement repl : this.replacements) { + if(repl.oldValue.startsWith("\"") && repl.oldValue.endsWith("\"")) { + newValue = (newValue.replace(repl.oldValue.substring(1,repl.oldValue.length()-1), repl.newValue)); + } else { + newValue = (newValue.replaceAll(repl.oldValue, repl.newValue)); + } + + } + return newValue; + } + + @Override + public Iterator iterator() { + return replacements.iterator(); + } + + @Override + public boolean processStartDocument(XdmNode node) throws SaxonApiException { + return true;//process children + } + + @Override + public void processEndDocument(XdmNode node) throws SaxonApiException { + //do nothing + } + + @Override + public boolean processStartElement(XdmNode node) throws SaxonApiException { + return true;//process children + } + + @Override + public void processAttribute(XdmNode node) throws SaxonApiException { + String newValue = computeReplacement(node); + matcher.addAttribute(node, newValue); + } + + @Override + public void processEndElement(XdmNode node) throws SaxonApiException { } + + @Override + public void processText(XdmNode node) throws SaxonApiException { + String newValue = computeReplacement(node); + matcher.addText(newValue); + } + + @Override + public void processComment(XdmNode node) throws SaxonApiException { + String newValue = computeReplacement(node); + matcher.addText(newValue);} + + @Override + public void processPI(XdmNode node) throws SaxonApiException { + String newValue = computeReplacement(node); + matcher.addText(newValue); + } +} + +class Replacement { + public String oldValue; + public String newValue; + + public Replacement(String _oldVal, String _newVal) { + this.oldValue = _oldVal; + this.newValue = _newVal; + } +} \ No newline at end of file diff --git a/src/main/java/com/rackspace/cloud/api/docs/WebHelpMojo.java b/src/main/java/com/rackspace/cloud/api/docs/WebHelpMojo.java index c9dd723..6682843 100644 --- a/src/main/java/com/rackspace/cloud/api/docs/WebHelpMojo.java +++ b/src/main/java/com/rackspace/cloud/api/docs/WebHelpMojo.java @@ -158,6 +158,13 @@ public abstract class WebHelpMojo extends AbstractWebhelpMojo { */ private String canonicalUrlBase; + /** + * @parameter + * expression="${generate-webhelp.replacementsFile}" + * default-value="" + */ + private String replacementsFile; + /** * * @parameter @@ -405,8 +412,10 @@ public abstract class WebHelpMojo extends AbstractWebhelpMojo { map.put("security", this.security); map.put("canonicalUrlBase", this.canonicalUrlBase); + map.put("replacementsFile", this.replacementsFile); map.put("failOnValidationError", this.failOnValidationError); map.put("project.build.directory", this.projectBuildDirectory); + map.put("inputSrcFile", inputFilename); int lastSlash=inputFilename.lastIndexOf("/"); diff --git a/src/main/resources/etc/configuration.xml b/src/main/resources/etc/configuration.xml index c63ebf7..92714f4 100644 --- a/src/main/resources/etc/configuration.xml +++ b/src/main/resources/etc/configuration.xml @@ -254,5 +254,9 @@ + + + diff --git a/src/main/resources/rackspace-library.xpl b/src/main/resources/rackspace-library.xpl index 80d6b39..ce1ae9b 100644 --- a/src/main/resources/rackspace-library.xpl +++ b/src/main/resources/rackspace-library.xpl @@ -406,6 +406,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +