Skip to content

Commit

Permalink
make it possible to specify the content type for data inputs from the…
Browse files Browse the repository at this point in the history
… command line and Ant task instead of the hard-coded text/plain and thus correctly Base64-encode non-text data input and use user-given content type preferred over content type that is determined from the content or name
  • Loading branch information
Vampire committed May 2, 2013
1 parent 84a6a2a commit f5bc23d
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 59 deletions.
11 changes: 11 additions & 0 deletions docs/src/anttask.xml
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,17 @@ Calabash</citetitle> from Apache Ant using the
<td>input type, either <literal>XML</literal> or <literal>DATA</literal></td>
<td>No; default is <literal>XML</literal></td>
</tr>
<tr>
<td>contentType</td>
<td>content type, if this is equal to <literal>application/xml</literal>,
ends in <literal>+xml</literal>, starts with <literal>text/</literal>
or has a charset of <literal>utf-8</literal> specified, the input
is treated as text, otherwise the input gets base64 encoded
automatically before it is given to the pipeline</td>
<td>No; default is guessed from the resource content, as fallback from the
resource name and as second fallback <literal>application/octet-stream</literal>
is used</td>
</tr>
<tr>
<td>if</td>
<td>This parameter behaves like standard Ant if's as documented at
Expand Down
19 changes: 16 additions & 3 deletions docs/src/configuration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,11 @@ left out and the input will be applied to the primary non-parameter input port.<
<tbody>
<row>
<entry>Command line (long):</entry>
<entry><literal>--data-input</literal> <replaceable>port</replaceable>=<replaceable>uri</replaceable></entry>
<entry><literal>--data-input</literal> <replaceable>contentType</replaceable>@<replaceable>port</replaceable>=<replaceable>uri</replaceable></entry>
</row>
<row>
<entry>Command line (short):</entry>
<entry><literal>-d</literal><replaceable>port</replaceable>=<replaceable>uri</replaceable></entry>
<entry><literal>-d</literal><replaceable>contentType</replaceable>@<replaceable>port</replaceable>=<replaceable>uri</replaceable></entry>
</row>
<row>
<entry>Ant task:</entry>
Expand All @@ -559,7 +559,20 @@ left out and the input will be applied to the primary non-parameter input port.<
</tgroup>
</informaltable>

<para>The input specified will be base64 encoded.</para>
<para>If the port and uri do not contain an at sign, the contentType specification can be
left out. In this case the content type will be determined from the contents of the URI.
If the content type cannot be determined from the contents of the URI, the content type
is guessed from the URI name. If the content type cannot be guessed from the URI name,
it is set to "<literal>application/octet-stream</literal>". If you have to give an explicit
content type because the port or uri contain an at sign, but you don't know the content
type, you can set it to "<literal>content/unknown</literal>" which is the same as leaving
it out and will try to determine the content type from content or name.</para>

<para>If the detected or explicitly specified content type is equal to
<literal>application/xml</literal>, ends in <literal>+xml</literal>, starts with
<literal>text/</literal> or has a charset of <literal>utf-8</literal> specified,
the input is treated as text, otherwise the input gets base64 encoded automatically
before it is given to the pipeline.</para>

<para>If the uri does not contain an equals sign, the port specification can be
left out and the input will be applied to the primary non-parameter input port.</para>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/running.xml
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ will run Java with the correct class path and other arguments.</para>
<arg choice="plain">-d</arg>
<arg choice="plain">--data-input</arg>
</group>
<arg choice="opt"><replaceable>port</replaceable>=</arg><replaceable>uri</replaceable>
<arg choice="opt"><replaceable>contentType</replaceable>@</arg><arg choice="opt"><replaceable>port</replaceable>=</arg><replaceable>uri</replaceable>
</arg>
<arg rep="repeat">
<group choice="req">
Expand Down
58 changes: 29 additions & 29 deletions resources/etc/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,35 @@ Usage: com.xmlcalabash.drivers.Main [switches/options] [pipeline.xpl] [options]
or: com.xmlcalabash.drivers.Main [switches/options] { [inputs/parameters] step [options] }+

Where switches are:
-a, --schema-aware Turn on schema-aware processing,
this also sets saxon-processor to 'ee' implicitly
-b, --binding prefix=uri Specify namespace binding
-c, --config configfile Specify a particular configuration file
-d, --data-input [port=]uri Bind the specified input port to data, if no port is specified,
the first unbound input port is used
-D, --debug Turn on debugging
-E, --entity-resolver className Specify a resolver class for entity resolution
-G, --log-style logstyle Specify the default style for p:log output;
Must be 'off', 'plain', 'wrapped' (default), or 'directory'
-i, --input [port=]uri Bind the specified input port, if no port is specified,
the first unbound input port is used
-l, --library library.xpl Load the specified library
-o, --output [port=]uri Bind the specified output port, if no port is specified,
the first unbound output port is used
-p, --with-param [port@]param=value Specify a parameter
--profile file Specify a file, or '-' for console output,
where to write profiling information of the
pipeline that was run
-P, --saxon-processor edition Request a specific edition of Saxon;
Must be 'he' (default), 'pe' or 'ee'
-s, --step stepname Run the step named 'stepname' instead of a pipeline
-S, --safe-mode Request 'safe' execution
-U, --uri-resolver className Specify a resolver class for URI resolution
-v, --version Show XML Calabash version
-X, --extension extname Enable the 'extname' extension;
valid extname values are 'general-values', 'xpointer-on-text',
'transparent-json', 'json-flavor=<flavor>' and 'use-xslt-10'
--saxon-configuration file Load the specified Saxon configuration
-a, --schema-aware Turn on schema-aware processing,
this also sets saxon-processor to 'ee' implicitly
-b, --binding prefix=uri Specify namespace binding
-c, --config configfile Specify a particular configuration file
-d, --data-input [contentType@][port=]uri Bind the specified input port to data, if no port is specified,
the first unbound input port is used
-D, --debug Turn on debugging
-E, --entity-resolver className Specify a resolver class for entity resolution
-G, --log-style logstyle Specify the default style for p:log output;
Must be 'off', 'plain', 'wrapped' (default), or 'directory'
-i, --input [port=]uri Bind the specified input port, if no port is specified,
the first unbound input port is used
-l, --library library.xpl Load the specified library
-o, --output [port=]uri Bind the specified output port, if no port is specified,
the first unbound output port is used
-p, --with-param [port@]param=value Specify a parameter
--profile file Specify a file, or '-' for console output,
where to write profiling information of the
pipeline that was run
-P, --saxon-processor edition Request a specific edition of Saxon;
Must be 'he' (default), 'pe' or 'ee'
-s, --step stepname Run the step named 'stepname' instead of a pipeline
-S, --safe-mode Request 'safe' execution
-U, --uri-resolver className Specify a resolver class for URI resolution
-v, --version Show XML Calabash version
-X, --extension extname Enable the 'extname' extension;
valid extname values are 'general-values', 'xpointer-on-text',
'transparent-json', 'json-flavor=<flavor>' and 'use-xslt-10'
--saxon-configuration file Load the specified Saxon configuration

For more information, consult http://xmlcalabash.com/docs/reference/ or
the documentation that came with the distribution.
Expand Down
59 changes: 53 additions & 6 deletions src/com/xmlcalabash/drivers/CalabashTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.tools.ant.types.resources.Union;
import org.apache.tools.ant.util.FileNameMapper;

import static com.xmlcalabash.util.Input.Type.DATA;
import static com.xmlcalabash.util.Input.Type.XML;
import static java.lang.Long.MAX_VALUE;
import static java.util.Arrays.asList;
Expand Down Expand Up @@ -282,7 +283,7 @@ public void addConfiguredInput(Input input) {
return;
}

inputMappers.put(port, new TypedFileNameMapper(inputMapper, input.getType()));
inputMappers.put(port, new TypedFileNameMapper(inputMapper, input.getType(), input.getContentType()));
} else {
if (inputMappers.containsKey(port)) {
handleError("Resources used on input port that already has a mapper: " + port);
Expand All @@ -294,7 +295,7 @@ public void addConfiguredInput(Input input) {
}

for (Resource resource : resources.listResources()) {
inputResources.get(port).add(new TypedResource(resource, input.getType()));
inputResources.get(port).add(new TypedResource(resource, input.getType(), input.getContentType()));
}
}
}
Expand Down Expand Up @@ -1048,7 +1049,7 @@ public void execute() {
for (String fileName : inputFileNames) {
FileResource mappedResource = new FileResource(baseDir, fileName);
if (mappedResource.isExists()) {
mappedResources.add(new TypedResource(mappedResource, inputMapper.getType()));
mappedResources.add(new TypedResource(mappedResource, inputMapper.getType(), inputMapper.getContentType()));
} else {
log("Skipping non-exstent mapped resource: " + mappedResource.toString(), Project.MSG_DEBUG);
}
Expand Down Expand Up @@ -1141,7 +1142,7 @@ public void execute() {
List<TypedResource> mappedResources = new ArrayList<TypedResource>();
for (String fileName : inputFileNames) {
FileResource mappedResource = new FileResource(baseDir, fileName);
mappedResources.add(new TypedResource(mappedResource, inputMapper.getType()));
mappedResources.add(new TypedResource(mappedResource, inputMapper.getType(), inputMapper.getContentType()));
}
useInputResources.put(port, mappedResources);
}
Expand Down Expand Up @@ -1269,13 +1270,13 @@ private void process(Map<String, List<TypedResource>> inputResources, Map<String
for (String port : inputResources.keySet()) {
for (TypedResource typedResource : inputResources.get(port)) {
Resource resource = typedResource.getResource();
userArgs.addInput(port, resource.getInputStream(), resource.toString(), typedResource.getType());
userArgs.addInput(port, resource.getInputStream(), resource.toString(), typedResource.getType(), typedResource.getContentType());
}
}
for (Step step : steps) {
for (Input input : step.getInputs()) {
for (Resource resource : input.getResources().listResources()) {
userArgs.addInput(input.getPort(), resource.getInputStream(), resource.toString(), input.getType());
userArgs.addInput(input.getPort(), resource.getInputStream(), resource.toString(), input.getType(), input.getContentType());
}
}
for (Parameter parameter : step.getParameters()) {
Expand Down Expand Up @@ -1499,6 +1500,11 @@ public static class Input extends Port {
*/
private Type type = XML;

/**
* The content type
*/
private String contentType;

/**
* Get the input type
*
Expand All @@ -1516,6 +1522,27 @@ public Type getType() {
public void setType(Type type) {
this.type = type;
}

/**
* Get the content type
*
* @return the content type
*/
public String getContentType() {
if ((contentType != null) && (type != DATA)) {
throw new IllegalStateException("contentType of input can only be set if type is DATA");
}
return contentType;
}

/**
* Set the content type
*
* @param contentType the content type
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
} // Input

/**
Expand Down Expand Up @@ -1757,8 +1784,13 @@ public boolean shouldUse() {
private static class TypedResource {
private Resource resource = null;
private Type type = null;
private String contentType = null;

private TypedResource(Resource resource, Type type) {
this(resource, type, null);
}

private TypedResource(Resource resource, Type type, String contentType) {
if (resource == null) {
throw new IllegalArgumentException("resource must not be null");
}
Expand All @@ -1767,6 +1799,7 @@ private TypedResource(Resource resource, Type type) {
throw new IllegalArgumentException("type must not be null");
}
this.type = type;
this.contentType = contentType;
}

public Resource getResource() {
Expand All @@ -1776,13 +1809,22 @@ public Resource getResource() {
public Type getType() {
return type;
}

public String getContentType() {
return contentType;
}
} // TypedResource

private static class TypedFileNameMapper implements FileNameMapper {
private FileNameMapper fileNameMapper;
private Type type;
private String contentType;

private TypedFileNameMapper(FileNameMapper fileNameMapper, Type type) {
this(fileNameMapper, type, null);
}

private TypedFileNameMapper(FileNameMapper fileNameMapper, Type type, String contentType) {
if (fileNameMapper == null) {
throw new IllegalArgumentException("fileNameMapper must not be null");
}
Expand All @@ -1791,12 +1833,17 @@ private TypedFileNameMapper(FileNameMapper fileNameMapper, Type type) {
throw new IllegalArgumentException("type must not be null");
}
this.type = type;
this.contentType = contentType;
}

public Type getType() {
return type;
}

public String getContentType() {
return contentType;
}

@Override
public void setFrom(String s) {
fileNameMapper.setFrom(s);
Expand Down
4 changes: 2 additions & 2 deletions src/com/xmlcalabash/drivers/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,13 @@ boolean run(UserArgs userArgs, XProcConfiguration config) throws SaxonApiExcepti
ReadableData rd;
switch (input.getKind()) {
case URI:
rd = new ReadableData(runtime, c_data, input.getUri(), "text/plain");
rd = new ReadableData(runtime, c_data, input.getUri(), input.getContentType());
doc = rd.read();
break;

case INPUT_STREAM:
InputStream inputStream = input.getInputStream();
rd = new ReadableData(runtime, c_data, inputStream, "text/plain");
rd = new ReadableData(runtime, c_data, inputStream, input.getContentType());
doc = rd.read();
inputStream.close();
break;
Expand Down
2 changes: 1 addition & 1 deletion src/com/xmlcalabash/io/ReadableData.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private DocumentSequence ensureDocuments() {
stream = (uri == null) ? inputStream : ("-".equals(uri) ? System.in : getStream(dataURI));
String serverContentType = getContentType();

if ("content/unknown".equals(serverContentType) && contentType != null) {
if ((contentType != null) && !"content/unknown".equals(contentType)) {
// pretend...
serverContentType = contentType;
}
Expand Down
21 changes: 19 additions & 2 deletions src/com/xmlcalabash/util/Input.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@
import static com.xmlcalabash.util.Input.Kind.INPUT_STREAM;
import static com.xmlcalabash.util.Input.Kind.NONE;
import static com.xmlcalabash.util.Input.Kind.URI;
import static com.xmlcalabash.util.Input.Type.DATA;
import static com.xmlcalabash.util.Input.Type.XML;

public class Input {
private String uri;
private InputStream inputStream;
private Type type;
private String contentType;
private Kind kind = NONE;

public Input(String uri) {
this(uri, XML);
}

public Input(String uri, Type type) {
this(uri, type, null);
}

public Input(String uri, Type type, String contentType) {
this.uri = uri;
this.type = type;
this.contentType = contentType;
kind = URI;
}

Expand All @@ -28,9 +35,12 @@ public Input(InputStream inputStream, String uri) {
}

public Input(InputStream inputStream, String uri, Type type) {
this(inputStream, uri, type, null);
}

public Input(InputStream inputStream, String uri, Type type, String contentType) {
this(uri, type, contentType);
this.inputStream = inputStream;
this.uri = uri;
this.type = type;
kind = INPUT_STREAM;
}

Expand Down Expand Up @@ -59,6 +69,13 @@ public Type getType() {
return type;
}

public String getContentType() {
if ((contentType != null) && (type != DATA)) {
throw new IllegalStateException("contentType of input can only be set if type is DATA");
}
return contentType;
}

public Kind getKind() {
return kind;
}
Expand Down
Loading

0 comments on commit f5bc23d

Please sign in to comment.