/* Copyright (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.web;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.serializer.TreeWalker;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wps.web.InputParameterValues.ParameterType;
import org.geoserver.wps.web.InputParameterValues.ParameterValue;
import org.geotools.filter.v1_0.OGC;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.gml2.bindings.GML2EncodingUtils;
import org.geotools.gml3.GML;
import org.geotools.ows.v1_1.OWS;
import org.geotools.referencing.CRS;
import org.geotools.util.Converters;
import org.geotools.util.logging.Logging;
import org.geotools.wps.WPS;
import org.geotools.xlink.XLINK;
import org.geotools.xml.transform.TransformerBase;
import org.geotools.xml.transform.Translator;
import org.hsqldb.lib.StringInputStream;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.w3c.dom.Document;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
/**
* Helper class to turn a {@link ExecuteRequest} into the corresponding WPS 1.0 Execute xml
*
* @author Andrea Aime - OpenGeo
*
*/
class WPSExecuteTransformer extends TransformerBase {
static final Logger LOGGER = Logging.getLogger(WPSExecuteTransformer.class);
public WPSExecuteTransformer() {
this(null);
}
public WPSExecuteTransformer(Catalog catalog) {
this.catalog = catalog;
}
Catalog catalog;
@Override
public Translator createTranslator(ContentHandler handler) {
return new ExecuteRequestTranslator(handler);
}
public class ExecuteRequestTranslator extends TranslatorSupport {
/** wfs namespace */
protected static final String WFS_URI = "http://www.opengis.net/wfs";
protected static final String WPS_URI = "http://www.opengis.net/wps/1.0.0";
protected static final String WCS_URI = "http://www.opengis.net/wcs/1.1.1";
/** xml schema namespace + prefix */
protected static final String XSI_PREFIX = "xsi";
protected static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
public ExecuteRequestTranslator(ContentHandler ch) {
super(ch, null, null);
}
public void encode(Object o) throws IllegalArgumentException {
ExecuteRequest request = (ExecuteRequest) o;
encode(request, true);
}
private void encode(ExecuteRequest request, boolean mainProcess) {
// add all the usual suspects (we know that we encode the
// wfs requests as wfs 1.0 and the wcs requests as 1.1, but
// we really need to move those namespace declaration down to
// the single request elements so that we can mix them)
if (mainProcess) {
AttributesImpl attributes = attributes("version", "1.0.0", "service", "WPS",
"xmlns:xsi", XSI_URI, "xmlns", WPS_URI, "xmlns:wfs", WFS_URI, "xmlns:wps",
WPS_URI, "xmlns:ows", OWS.NAMESPACE, "xmlns:gml", GML.NAMESPACE, "xmlns:ogc",
OGC.NAMESPACE, "xmlns:wcs", WCS_URI, "xmlns:xlink", XLINK.NAMESPACE,
"xsi:schemaLocation", WPS.NAMESPACE + " "
+ "http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd");
start("wps:Execute", attributes);
} else {
AttributesImpl attributes = attributes("version", "1.0.0", "service", "WPS");
start("wps:Execute", attributes);
}
element("ows:Identifier", request.processName);
handleInputs(request.inputs);
handleOutputs(request.outputs);
end("wps:Execute");
}
/**
* Helper to build a set of attributes out of a list of key/value pairs
*
* @param nameValues
* @return
*/
AttributesImpl attributes(String... nameValues) {
AttributesImpl atts = new AttributesImpl();
for (int i = 0; i < nameValues.length; i += 2) {
String name = nameValues[i];
String valu = nameValues[i + 1];
atts.addAttribute(null, null, name, null, valu);
}
return atts;
}
public void handleInputs(List<InputParameterValues> inputs) {
start("wps:DataInputs");
for (InputParameterValues pv : inputs) {
for (int i = 0; i < pv.values.size(); i++) {
ParameterValue value = pv.values.get(i);
if (value == null || value.value == null) {
continue;
}
start("wps:Input");
element("ows:Identifier", pv.paramName);
if (pv.isBoundingBox()) {
ReferencedEnvelope env = (ReferencedEnvelope) value.value;
start("wps:Data");
String crs = null;
if (env.getCoordinateReferenceSystem() != null) {
try {
crs = "EPSG:"
+ CRS.lookupEpsgCode(env.getCoordinateReferenceSystem(),
false);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Could not get EPSG code for " + crs);
}
}
if (crs == null) {
start("wps:BoundingBoxData", attributes("dimensions", "2"));
} else {
start("wps:BoundingBoxData", attributes("crs", crs, "dimensions", "2"));
}
element("ows:LowerCorner", env.getMinX() + " " + env.getMinY());
element("ows:UpperCorner", env.getMaxX() + " " + env.getMaxY());
end("wps:BoundingBoxData");
end("wps:Data");
} else if (pv.isComplex()) {
if (value.type == ParameterType.TEXT) {
handleTextInput(value);
} else if (value.type == ParameterType.VECTOR_LAYER) {
handleVectorInput(value);
} else if (value.type == ParameterType.RASTER_LAYER) {
handleRasterLayerInput(value);
} else if (value.type == ParameterType.REFERENCE) {
handleReferenceInput(value);
} else if (value.type == ParameterType.SUBPROCESS) {
handleSubprocessInput(value);
} else {
// write out a warning without blowing pu
char[] comment = "Can't handle this data type yet".toCharArray();
try {
((LexicalHandler) contentHandler).comment(comment, 0,
comment.length);
} catch (SAXException se) {
throw new RuntimeException(se);
}
}
} else if (pv.isCoordinateReferenceSystem()) {
handleCoordinateReferenceSystem(value);
} else {
start("wps:Data");
element("wps:LiteralData", Converters.convert(value.value, String.class));
end("wps:Data");
}
end("wps:Input");
}
}
end("wps:DataInputs");
}
private void handleCoordinateReferenceSystem(ParameterValue value) {
try {
start("wps:Data");
final CoordinateReferenceSystem crs = (CoordinateReferenceSystem) value.value;
element("wps:LiteralData", CRS.lookupIdentifier(crs, false));
end("wps:Data");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void handleRasterLayerInput(ParameterValue value) {
RasterLayerConfiguration raster = (RasterLayerConfiguration) value.value;
start("wps:Reference", attributes("mimeType", value.mime, "xlink:href",
"http://geoserver/wcs", "method", "POST"));
start("wps:Body");
if (raster != null && raster.getLayerName() != null) {
start("wcs:GetCoverage", attributes("service", "WCS", "version", "1.1.1"));
element("ows:Identifier", raster.getLayerName());
start("wcs:DomainSubset");
ReferencedEnvelope bbox = raster.getSpatialDomain();
String srsUri = GML2EncodingUtils.toURI(bbox.getCoordinateReferenceSystem());
start("gml:BoundingBox", attributes("crs", srsUri));
element("ows:LowerCorner", bbox.getMinX() + " " + bbox.getMinY());
element("ows:UpperCorner", bbox.getMaxX() + " " + bbox.getMaxY());
end("gml:BoundingBox");
end("wcs:DomainSubset");
element("wcs:Output", null, attributes("format", "image/tiff"));
end("wcs:GetCoverage");
}
end("wps:Body");
end("wps:Reference");
}
private void handleVectorInput(ParameterValue value) {
VectorLayerConfiguration vector = (VectorLayerConfiguration) value.value;
start("wps:Reference", attributes("mimeType", value.mime, "xlink:href",
"http://geoserver/wfs", "method", "POST"));
start("wps:Body");
AttributesImpl atts =
attributes("service", "WFS", "version", "1.0.0", "outputFormat", "GML2");
//if the layer name is qualfiied we should include a namespace mapping on the
// GetFeature request
if (catalog != null && vector.layerName != null && vector.layerName.contains(":")) {
String prefix = vector.layerName.split(":")[0];
NamespaceInfo ns = catalog.getNamespaceByPrefix(prefix);
if (ns != null) {
atts.addAttribute("", "", "xmlns:" + prefix, null, ns.getURI());
}
}
start("wfs:GetFeature", atts);
if (vector.layerName == null) {
start("wfs:Query");
} else {
start("wfs:Query", attributes("typeName", vector.layerName));
}
// handle attributes and filters here
end("wfs:Query");
end("wfs:GetFeature");
end("wps:Body");
end("wps:Reference");
}
public void handleReferenceInput(ParameterValue value) {
ReferenceConfiguration reference = (ReferenceConfiguration) value.value;
if (reference.mime != null) {
start("wps:Reference", attributes("mimeType", reference.mime, "xlink:href",
reference.url, "method", reference.method.toString()));
} else {
start("wps:Reference", attributes("xlink:href", reference.url, "method",
reference.method.toString()));
}
if (reference.method == ReferenceConfiguration.Method.POST) {
start("wps:Body");
cdata(reference.body);
end("wps:Body");
}
end("wps:Reference");
}
private void handleSubprocessInput(ParameterValue value) {
ExecuteRequest request = (ExecuteRequest) value.value;
start("wps:Reference", attributes("mimeType", value.mime, "xlink:href",
"http://geoserver/wps", "method", "POST"));
start("wps:Body");
encode(request, false);
end("wps:Body");
end("wps:Reference");
}
private void handleTextInput(ParameterValue value) {
start("wps:Data");
start("wps:ComplexData", attributes("mimeType", value.mime));
String data = Converters.convert(value.value, String.class);
if (data != null) {
Document document = parseAsXML(data);
if (document != null) {
dumpAsXML(document);
} else {
try {
((LexicalHandler) contentHandler).startCDATA();
chars(data);
((LexicalHandler) contentHandler).endCDATA();
} catch (SAXException e) {
throw new RuntimeException(e);
}
}
}
end("wps:ComplexData");
end("wps:Data");
}
private void dumpAsXML(Document document) {
try {
TreeWalker tw = new TreeWalker(contentHandler);
tw.traverse(document);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Document parseAsXML(String data) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
if (!data.startsWith("<?xml")) {
data = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n" + data;
}
return builder.parse(new StringInputStream(data));
} catch (Throwable t) {
LOGGER.log(Level.FINE, "Failed to parse XML, assuming it's plain text", t);
return null;
}
}
public void handleOutputs(List<OutputParameter> outputs) {
start("wps:ResponseForm");
// if we have a single output we return it in raw form, otherwise
// go for a full output document
if (outputs.size() > 1) {
start("wps:ResponseDocument");
for (OutputParameter op : outputs) {
if (op.isComplex()) {
if (op.isComplex()) {
start("wps:Output", attributes("mimeType", op.mimeType));
} else {
start("wps:Output");
}
element("ows:Identifier", op.paramName);
end("wps:Output");
}
}
end("wps:ResponseDocument");
} else if (outputs.size() == 1) {
OutputParameter op = outputs.get(0);
if (op.isComplex()) {
start("wps:RawDataOutput", attributes("mimeType", op.mimeType));
} else {
start("wps:RawDataOutput");
}
element("ows:Identifier", op.paramName);
end("wps:RawDataOutput");
}
end("wps:ResponseForm");
}
}
}