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
This commit is contained in:
Salman Qureshi
2012-07-24 12:15:01 +00:00
parent 227983653f
commit 8b2c9052ce
6 changed files with 317 additions and 2 deletions

View File

@@ -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);
}

View File

@@ -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<XPathReplacement> replacements = readReplacementsFile(getOption(_replacements_file, "replacements.config"));
XdmNode updatedDoc = makeReplacements (source.read(), replacements);
result.write(updatedDoc);
}
private List<XPathReplacement> readReplacementsFile(String fileName) {
List<XPathReplacement> xpathReplacements = new ArrayList<XPathReplacement>();
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<XPathReplacement> 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<Replacement>, ProcessMatchingNodes {
String xpath;
List<Replacement> replacements;
ProcessMatch matcher;
public XPathReplacement(String _xpath) {
this.xpath = _xpath;
replacements = new ArrayList<Replacement>();
}
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<Replacement> 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;
}
}

View File

@@ -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("/");

View File

@@ -254,5 +254,9 @@
<implementation type="cxu:compare"
class-name="com.xmlcalabash.extensions.xmlunit.Compare" />
<!-- Rackspace Extensions -->
<implementation type="cx:replace-text"
class-name="com.rackspace.cloud.api.docs.ReplaceTextXProcStep" />
</xproc-config>

View File

@@ -406,6 +406,59 @@
</p:group>
</p:declare-step>
<p:declare-step
xmlns:l="http://xproc.org/library"
xml:id="search-and-replace"
xmlns:c="http://www.w3.org/ns/xproc-step"
type="l:search-and-replace"
name="search-and-replace-step">
<p:input port="source" primary="true" sequence="true"/>
<p:output port="result" sequence="true">
<p:pipe step="group" port="result"/>
</p:output>
<p:input port="parameters" kind="parameter"/>
<ut:parameters name="params"/>
<p:sink/>
<p:group name="group">
<p:output port="result" primary="true">
<p:pipe step="replace" port="result"/>
</p:output>
<p:variable name="inputSrcFile" select="//c:param[@name = 'inputSrcFile']/@value">
<p:pipe step="params" port="parameters"/>
</p:variable>
<p:variable name="project.build.directory" select="//c:param[@name = 'project.build.directory']/@value">
<p:pipe step="params" port="parameters"/>
</p:variable>
<p:variable name="replacementsFile" select="//c:param[@name = 'replacementsFile']/@value">
<p:pipe step="params" port="parameters"/>
</p:variable>
<cx:replace-text name="replace">
<p:input port="source">
<p:pipe step="search-and-replace-step" port="source"/>
</p:input>
<p:with-option name="replacements.file" select="$replacementsFile">
</p:with-option>
</cx:replace-text>
<p:store encoding="utf-8" indent="true" omit-xml-declaration="false">
<p:with-option name="href"
select="concat('file://',$project.build.directory,'/docbkx/',$inputSrcFile)"
/>
</p:store>
</p:group>
</p:declare-step>
<!-- Search and replace calabash extension -->
<p:declare-step
type="cx:replace-text"
xml:id="replace-text">
<p:input port="source" primary="true" sequence="true"/>
<p:output port="result" primary="true" sequence="true"/>
<p:option name="replacements.file" cx:type="xsd:string"/>
</p:declare-step>
<!--+========================================================+
| Step parameters
|
@@ -430,5 +483,4 @@
</p:input>
</p:parameters>
</p:declare-step>
</p:library>

View File

@@ -48,6 +48,11 @@
<l:extensions-info/>
<cx:message>
<p:with-option name="message" select="'Making replacements'"/>
</cx:message>
<l:search-and-replace/>
<cx:message>
<p:with-option name="message" select="'Normalize wadls'"/>
</cx:message>