
com.xmlcalabash.XMLCalabashTask.groovy Maven / Gradle / Ivy
package com.xmlcalabash
import com.xmlcalabash.core.XProcConfiguration
import com.xmlcalabash.core.XProcException
import com.xmlcalabash.core.XProcRuntime
import com.xmlcalabash.io.ReadableData
import com.xmlcalabash.io.ReadablePipe
import com.xmlcalabash.io.WritableDocument
import com.xmlcalabash.model.RuntimeValue
import com.xmlcalabash.model.Serialization
import com.xmlcalabash.runtime.XPipeline
import com.xmlcalabash.util.Closer
import com.xmlcalabash.util.Input
import com.xmlcalabash.util.Output
import com.xmlcalabash.util.UserArgs
import net.sf.saxon.s9api.QName
import net.sf.saxon.s9api.XdmNode
import org.gradle.api.internal.ConventionTask
import org.xml.sax.InputSource
import static com.xmlcalabash.core.XProcConstants.c_data
import static java.lang.String.format
class XMLCalabashTask extends ConventionTask {
protected boolean schemaAware = false
protected boolean safeMode = false
protected boolean debug = false
protected String pipelineURI = null
protected Hashtable nsBindings = new Hashtable ()
protected String configFile = null
protected String saxonConfigFile = null
protected String entityResolver = null
protected String uriResolver = null
protected String library = null // Should be a list...
protected String step = null
protected String profile = null
protected String edition = null
protected boolean extensionValues = false
protected boolean xpointerOnText = false
protected boolean transparentJson = false
protected boolean ignoreInvalidXmlBase = false
@org.gradle.api.tasks.Optional
protected String jsonFlavor = null
protected boolean allowTextResults = false
protected boolean useXslt10 = false
protected boolean htmlSerializer = false;
protected UserArgs userArgs = null
protected Hashtable seenOptions = new Hashtable ();
protected URI baseURI = project.getProjectDir().toURI();
protected XProcConfiguration xprocConfiguration = null
protected XProcRuntime runtime = null
protected XPipeline pipeline = null
protected Map portOutputs = null
XMLCalabashTask() {
userArgs = new UserArgs()
}
@org.gradle.api.tasks.InputFile
String getPipeline() {
return pipelineURI
}
def setPipeline(String pipeline) {
pipelineURI = baseURI.resolve(pipeline).toASCIIString()
getInputs().file(pipelineURI)
return this
}
@org.gradle.api.tasks.Input
boolean getDebug() {
return debug
}
def setDebug(boolean debug) {
this.debug = debug
return this
}
@org.gradle.api.tasks.Input
boolean getSafeMode() {
return safeMode
}
def setSafeMode(boolean safe) {
this.safeMode = safe
return this
}
@org.gradle.api.tasks.Input
@org.gradle.api.tasks.Optional
String getProfilePipeline() {
return profile
}
def setProfilePipeline(String profile) {
this.profile = profile
return this
}
@org.gradle.api.tasks.Input
@org.gradle.api.tasks.Optional
String getSaxonEdition() {
if (edition == null) {
return "he"
} else {
return edition
}
}
def setSaxonEdition(String edition) {
this.edition = edition
return this
}
@org.gradle.api.tasks.Input
boolean getSchemaAware() {
return schemaAware
}
def setSchemaAware(boolean aware) {
schemaAware = aware
return this
}
@org.gradle.api.tasks.Input
@org.gradle.api.tasks.Optional
String getEntityResolver() {
return entityResolver
}
def setEntityResolver(String resolverClass) {
entityResolver = resolverClass
return this
}
@org.gradle.api.tasks.Input
@org.gradle.api.tasks.Optional
String getUriResolver() {
return uriResolver
}
def setUriResolver(String resolverClass) {
uriResolver = resolverClass
return this
}
def namespaceBinding(String prefix, String uri) {
nsBindings.put(prefix,uri)
return this
}
@org.gradle.api.tasks.InputFile
@org.gradle.api.tasks.Optional
String getConfigFile() {
return configFile
}
def setConfigFile(String config) {
configFile = config
return this
}
@org.gradle.api.tasks.InputFile
@org.gradle.api.tasks.Optional
String getSaxonConfigFile() {
return saxonConfigFile
}
def setSaxonConfigFile(String config) {
saxonConfigFile = config
return this
}
@org.gradle.api.tasks.InputFile
@org.gradle.api.tasks.Optional
String getLibrary() {
return library
}
def setLibrary(String library) {
this.library = baseURI.resolve(library).toASCIIString()
return this
}
@org.gradle.api.tasks.Input
@org.gradle.api.tasks.Optional
String getStep() {
return step
}
/* Remove step property until issue #4 is fixed
def setStep(String qname) {
step = qname
return this
}
*/
def input(String port, File file) {
return input(port, file.getAbsolutePath())
}
def input(String port, String filename) {
String fn = baseURI.resolve(filename).toASCIIString()
getInputs().file(fn)
userArgs.addInput(port, fn, Input.Type.XML)
return this
}
def dataInput(String port, File file) {
return dataInput(port, file.absolutePath)
}
def dataInput(String port, String filename) {
return dataInput(port, filename, null)
}
def dataInput(String port, String filename, String contentType) {
String fn = baseURI.resolve(filename).toASCIIString()
getInputs().file(fn)
if (contentType == null) {
userArgs.addInput(port, fn, Input.Type.DATA)
} else {
userArgs.addInput(port, fn, Input.Type.DATA, contentType)
}
return this
}
def output(String port, File file) {
return output(port, file.absolutePath)
}
def output(String port, String filename) {
String fn = baseURI.resolve(filename).toASCIIString()
getOutputs().file(fn)
userArgs.addOutput(port, fn)
return this
}
def param(String qname, File file) {
return param(qname, file, null)
}
def param(String qname, File file, String port) {
String value = file.getAbsolutePath()
getInputs().file(value)
return param(qname, file.toURI().toASCIIString(), port)
}
def param(String qname, Integer value) {
return param(qname, value.toString(), null)
}
def param(String qname, Integer value, String port) {
return setParam(qname, value.toString(), "xs:integer", port)
}
def param(String qname, Float value) {
return param(qname, value.toString(), null)
}
def param(String qname, Float value, String port) {
return setParam(qname, value.toString(), "xs:float", port)
}
def param(String qname, Boolean value) {
return param(qname, value.toString(), null)
}
def param(String qname, Boolean value, String port) {
return setParam(qname, value.toString(), "xs:boolean", port)
}
def param(String qname, String value) {
return param(qname, value, null)
}
def param(String qname, String value, String port) {
return setParam(qname, value, "xs:string", port)
}
def param(String qname, URL value) {
return param(qname, value, null)
}
def param(String qname, URL value, String port) {
return setParam(qname, value.toString(), "xs:anyURI", port)
}
def param(String qname, URI value) {
return param(qname, value, null)
}
def param(String qname, URI value, String port) {
return setParam(qname, value.toASCIIString(), "xs:anyURI", port)
}
private XMLCalabashTask setParam(String qname, String value, String type, String port) {
if (port == null) {
userArgs.addParam(qname, value)
} else {
userArgs.addParam(port, qname, value)
}
return this
}
def option(String qname, File file) {
String value = file.getAbsolutePath()
getInputs().file(value)
return setOption(qname, file.toURI().toASCIIString(), "xs:anyURI")
}
def option(String qname, String value) {
return setOption(qname, value, "xs:string")
}
def option(String qname, URI value) {
return setOption(qname, value.toASCIIString(), "xs:anyURI")
}
def option(String qname, URL value) {
return setOption(qname, value.toString(), "xs:anyURI")
}
def option(String qname, Integer value) {
return setOption(qname, value.toString(), "xs:integer")
}
def option(String qname, Float value) {
return setOption(qname, value.toString(), "xs:float")
}
def option(String qname, Boolean value) {
return setOption(qname, value.toString(), "xs:boolean")
}
private XMLCalabashTask setOption(String qname, String value, String type) {
userArgs.addOption(qname, value)
seenOptions.put(qname, value)
return this
}
def setExtensionValues(boolean value) {
extensionValues = value
return this
}
@org.gradle.api.tasks.Input
boolean getExtensionValues() {
return extensionValues
}
def setIgnoreInvalidXmlBase(boolean value) {
ignoreInvalidXmlBase = value
return this
}
@org.gradle.api.tasks.Input
boolean getIgnoreInvalidXmlBase() {
return ignoreInvalidXmlBase
}
def setXpointerOnText(boolean value) {
xpointerOnText = value
return this
}
@org.gradle.api.tasks.Input
boolean getXpointerOnText() {
return xpointerOnText
}
def setTransparentJson(boolean value) {
transparentJson = value
return this
}
@org.gradle.api.tasks.Input
boolean getTransparentJson() {
return transparentJson
}
def setJsonFlavor(String value) {
jsonFlavor = value
return this
}
@org.gradle.api.tasks.Input
String getJsonFlavor() {
return jsonFlavor
}
def setAllowTextResults(boolean value) {
allowTextResults = value
return this
}
@org.gradle.api.tasks.Input
boolean getAllowTextResults() {
return allowTextResults
}
def setUseXslt10(boolean value) {
useXslt10 = value
return this
}
@org.gradle.api.tasks.Input
boolean getUseXslt10() {
return useXslt10
}
def setHtmlSerializer(boolean value) {
htmlSerializer = value
return this
}
@org.gradle.api.tasks.Input
boolean getHtmlSerializer() {
return htmlSerializer
}
protected void setupRuntime() {
if (getPipeline() == null && getStep() == null) {
throw notAllowed("You must specify a pipeline or a step.")
}
if (getPipeline() != null && getStep() != null) {
throw notAllowed("You must specify either a pipeline or a step.")
}
// On reflection, I'm not sure XML Calabash is well designed for this kind of embedding
userArgs.setSaxonProcessor(getSaxonEdition())
userArgs.setSchemaAware(getSchemaAware())
userArgs.setSafeMode(getSafeMode())
userArgs.setDebug(getDebug())
userArgs.setExtensionValues(getExtensionValues())
userArgs.setIgnoreInvalidXmlBase(getIgnoreInvalidXmlBase())
userArgs.setAllowXPointerOnText(getXpointerOnText())
userArgs.setTransparentJSON(getTransparentJson())
userArgs.setAllowTextResults(getAllowTextResults())
userArgs.setUseXslt10(getUseXslt10())
userArgs.setHtmlSerializer(getHtmlSerializer())
for (pfx in nsBindings.keySet()) {
userArgs.addBinding(pfx, nsBindings.get(pfx))
}
if (getLibrary() != null) {
userArgs.addLibrary(getLibrary())
}
if (getPipeline() != null) {
userArgs.setPipeline(getPipeline())
}
if (getStep() != null) {
userArgs.setCurStepName(getStep())
}
if (getConfigFile() != null) {
userArgs.setConfig(getConfigFile())
}
if (getSaxonConfigFile() != null) {
userArgs.setSaxonConfig(getSaxonConfigFile())
}
if (getEntityResolver() != null) {
userArgs.setEntityResolverClass(getEntityResolver())
}
if (getUriResolver() != null) {
userArgs.setUriResolverClass(getUriResolver())
}
if (getProfilePipeline() != null) {
userArgs.setProfile(profile)
}
if (getJsonFlavor() != null) {
userArgs.setJsonFlavor(getJsonFlavor())
}
xprocConfiguration = userArgs.createConfiguration()
runtime = new XProcRuntime(xprocConfiguration);
}
protected void processInputs() {
setupRuntime()
pipeline = runtime.load(userArgs.getPipeline())
// Process parameters from the configuration...
for (String port : xprocConfiguration.params.keySet()) {
Map parameters = xprocConfiguration.params.get(port);
setParametersOnPipeline(pipeline, port, parameters);
}
// Now process parameters from the command line...
for (String port : userArgs.getParameterPorts()) {
Map parameters = userArgs.getParameters(port);
setParametersOnPipeline(pipeline, port, parameters);
}
Set ports = pipeline.getInputs();
Set userArgsInputPorts = userArgs.getInputPorts();
Set cfgInputPorts = xprocConfiguration.inputs.keySet();
Set allPorts = new HashSet();
allPorts.addAll(userArgsInputPorts);
allPorts.addAll(cfgInputPorts);
// map a given input without port specification to the primary non-parameter input implicitly
for (String port : ports) {
if (!allPorts.contains(port) && allPorts.contains(null)
&& pipeline.getDeclareStep().getInput(port).getPrimary()
&& !pipeline.getDeclareStep().getInput(port).getParameterInput()) {
if (userArgsInputPorts.contains(null)) {
userArgs.setDefaultInputPort(port);
allPorts.remove(null);
allPorts.add(port);
}
break;
}
}
for (String port : allPorts) {
if (!ports.contains(port)) {
throw new XProcException("There is a binding for the port '" + port + "' but the pipeline declares no such port.");
}
pipeline.clearInputs(port);
if (userArgsInputPorts.contains(port)) {
XdmNode doc = null;
for (Input input : userArgs.getInputs(port)) {
switch (input.getType()) {
case Input.Type.XML:
switch (input.getKind()) {
case Input.Kind.URI:
String uri = input.getUri();
if ("-".equals(uri)) {
doc = runtime.parse(new InputSource(System.in));
} else {
doc = runtime.parse(new InputSource(uri));
}
break;
case Input.Kind.INPUT_STREAM:
InputStream inputStream = input.getInputStream();
try {
doc = runtime.parse(new InputSource(inputStream));
} finally {
Closer.close(inputStream);
}
break;
default:
throw new UnsupportedOperationException(format("Unsupported input kind '%s'", input.getKind()));
}
break;
case Input.Type.DATA:
ReadableData rd;
switch (input.getKind()) {
case Input.Kind.URI:
rd = new ReadableData(runtime, c_data, input.getUri(), input.getContentType());
doc = rd.read();
break;
case Input.Kind.INPUT_STREAM:
InputStream inputStream = input.getInputStream();
try {
rd = new ReadableData(runtime, c_data, inputStream, input.getContentType());
doc = rd.read();
} finally {
Closer.close(inputStream);
}
break;
default:
throw new UnsupportedOperationException(format("Unsupported input kind '%s'", input.getKind()));
}
break;
default:
throw new UnsupportedOperationException(format("Unsupported input type '%s'", input.getType()));
}
pipeline.writeTo(port, doc);
}
} else {
for (ReadablePipe pipe : xprocConfiguration.inputs.get(port)) {
XdmNode doc = pipe.read();
pipeline.writeTo(port, doc);
}
}
}
// Implicit binding for stdin?
String implicitPort = null;
for (String port : ports) {
if (!allPorts.contains(port)) {
if (pipeline.getDeclareStep().getInput(port).getPrimary()
&& !pipeline.getDeclareStep().getInput(port).getParameterInput()) {
implicitPort = port;
}
}
}
if (implicitPort != null && !pipeline.hasReadablePipes(implicitPort)) {
XdmNode doc = runtime.parse(new InputSource(System.in));
pipeline.writeTo(implicitPort, doc);
}
portOutputs = new HashMap();
Map userArgsOutputs = userArgs.getOutputs();
for (String port : pipeline.getOutputs()) {
// Bind to "-" implicitly
Output output = null;
if (userArgsOutputs.containsKey(port)) {
output = userArgsOutputs.get(port);
} else if (xprocConfiguration.outputs.containsKey(port)) {
output = new Output(xprocConfiguration.outputs.get(port));
} else if (userArgsOutputs.containsKey(null)
&& pipeline.getDeclareStep().getOutput(port).getPrimary()) {
// Bind unnamed port to primary output port
output = userArgsOutputs.get(null);
}
// Look for explicit binding to "-"
if ((output != null) && (output.getKind() == Output.Kind.URI) && "-".equals(output.getUri())) {
output = null;
}
portOutputs.put(port, output);
}
for (QName optname : xprocConfiguration.options.keySet()) {
RuntimeValue value = new RuntimeValue(xprocConfiguration.options.get(optname), null, null);
pipeline.passOption(optname, value);
}
for (QName optname : userArgs.getOptionNames()) {
RuntimeValue value = new RuntimeValue(userArgs.getOption(optname), null, null);
pipeline.passOption(optname, value);
}
}
protected void processOutputs() {
for (String port : pipeline.getOutputs()) {
Output output;
if (portOutputs.containsKey(port)) {
output = portOutputs.get(port);
} else {
// You didn't bind it, and it isn't going to stdout, so it's going into the bit bucket.
continue;
}
if ((output == null) || ((output.getKind() == Output.Kind.OUTPUT_STREAM) && System.out.equals(output.getOutputStream()))) {
logger.trace("Copy output from " + port + " to stdout");
} else {
switch (output.getKind()) {
case Output.Kind.URI:
logger.trace("Copy output from " + port + " to " + output.getUri());
break;
case Output.Kind.OUTPUT_STREAM:
String outputStreamClassName = output.getOutputStream().getClass().getName();
logger.trace("Copy output from " + port + " to " + outputStreamClassName + " stream");
break;
default:
throw new UnsupportedOperationException(format("Unsupported output kind '%s'", output.getKind()));
}
}
Serialization serial = pipeline.getSerialization(port);
if (serial == null) {
// Use the configuration options
serial = new Serialization(runtime, pipeline.getNode()); // The node's a hack
for (String name : xprocConfiguration.serializationOptions.keySet()) {
String value = xprocConfiguration.serializationOptions.get(name);
if ("byte-order-mark".equals(name)) serial.setByteOrderMark("true".equals(value));
if ("escape-uri-attributes".equals(name)) serial.setEscapeURIAttributes("true".equals(value));
if ("include-content-type".equals(name)) serial.setIncludeContentType("true".equals(value));
if ("indent".equals(name)) serial.setIndent("true".equals(value));
if ("omit-xml-declaration".equals(name)) serial.setOmitXMLDeclaration("true".equals(value));
if ("undeclare-prefixes".equals(name)) serial.setUndeclarePrefixes("true".equals(value));
if ("method".equals(name)) serial.setMethod(new QName("", value));
// FIXME: if ("cdata-section-elements".equals(name)) serial.setCdataSectionElements();
if ("doctype-public".equals(name)) serial.setDoctypePublic(value);
if ("doctype-system".equals(name)) serial.setDoctypeSystem(value);
if ("encoding".equals(name)) serial.setEncoding(value);
if ("media-type".equals(name)) serial.setMediaType(value);
if ("normalization-form".equals(name)) serial.setNormalizationForm(value);
if ("standalone".equals(name)) serial.setStandalone(value);
if ("version".equals(name)) serial.setVersion(value);
}
}
// Command line values override pipeline or configuration specified values
for (String name: ["byte-order-mark", "escape-uri-attributes", "include-content-type",
"indent", "omit-xml-declaration", "undeclare-prefixes", "method",
"doctype-public", "doctype-system", "encoding", "media-type",
"normalization-form", "standalone", "version" ]) {
String value = userArgs.getSerializationParameter(port, name);
if (value == null) {
value = userArgs.getSerializationParameter(name);
if (value == null) {
continue;
}
}
if ("byte-order-mark".equals(name)) serial.setByteOrderMark("true".equals(value));
if ("escape-uri-attributes".equals(name)) serial.setEscapeURIAttributes("true".equals(value));
if ("include-content-type".equals(name)) serial.setIncludeContentType("true".equals(value));
if ("indent".equals(name)) serial.setIndent("true".equals(value));
if ("omit-xml-declaration".equals(name)) serial.setOmitXMLDeclaration("true".equals(value));
if ("undeclare-prefixes".equals(name)) serial.setUndeclarePrefixes("true".equals(value));
if ("method".equals(name)) serial.setMethod(new QName("", value));
// N.B. cdata-section-elements isn't allowed
if ("doctype-public".equals(name)) serial.setDoctypePublic(value);
if ("doctype-system".equals(name)) serial.setDoctypeSystem(value);
if ("encoding".equals(name)) serial.setEncoding(value);
if ("media-type".equals(name)) serial.setMediaType(value);
if ("normalization-form".equals(name)) serial.setNormalizationForm(value);
if ("standalone".equals(name)) serial.setStandalone(value);
if ("version".equals(name)) serial.setVersion(value);
}
// I wonder if there's a better way...
WritableDocument wd = null;
if (output == null) {
wd = new WritableDocument(runtime, null, serial);
} else {
switch (output.getKind()) {
case Output.Kind.URI:
URI furi = new URI(output.getUri());
String filename = furi.getPath();
FileOutputStream outfile = new FileOutputStream(filename);
wd = new WritableDocument(runtime, filename, serial, outfile);
break;
case Output.Kind.OUTPUT_STREAM:
OutputStream outputStream = output.getOutputStream();
wd = new WritableDocument(runtime, null, serial, outputStream);
break;
default:
throw new UnsupportedOperationException(format("Unsupported output kind '%s'", output.getKind()));
}
}
try {
ReadablePipe rpipe = pipeline.readFrom(port);
while (rpipe.moreDocuments()) {
wd.write(rpipe.read());
}
} finally {
if (output != null) {
wd.close();
}
}
}
}
@org.gradle.api.tasks.TaskAction
void exec() {
processInputs()
pipeline.run()
processOutputs()
}
private static void setParametersOnPipeline(XPipeline pipeline, String port, Map parameters) {
if ("*".equals(port)) {
for (QName name : parameters.keySet()) {
pipeline.setParameter(name, new RuntimeValue(parameters.get(name)));
}
} else {
for (QName name : parameters.keySet()) {
pipeline.setParameter(port, name, new RuntimeValue(parameters.get(name)));
}
}
}
private static UnsupportedOperationException notAllowed(final String msg) {
return new UnsupportedOperationException (msg)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy