/* 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; import java.math.BigInteger; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import net.opengis.ows11.AllowedValuesType; import net.opengis.ows11.CodeType; import net.opengis.ows11.Ows11Factory; import net.opengis.ows11.ValueType; import net.opengis.wps10.CRSsType; import net.opengis.wps10.ComplexDataDescriptionType; import net.opengis.wps10.DataInputsType; import net.opengis.wps10.DefaultType; import net.opengis.wps10.DescribeProcessType; import net.opengis.wps10.InputDescriptionType; import net.opengis.wps10.LiteralInputType; import net.opengis.wps10.LiteralOutputType; import net.opengis.wps10.OutputDescriptionType; import net.opengis.wps10.ProcessDescriptionType; import net.opengis.wps10.ProcessDescriptionsType; import net.opengis.wps10.ProcessOutputsType; import net.opengis.wps10.SupportedCRSsType; import net.opengis.wps10.SupportedComplexDataInputType; import net.opengis.wps10.SupportedComplexDataType; import net.opengis.wps10.Wps10Factory; import org.geoserver.ows.Ows11Util; import org.geoserver.wfs.xml.XSProfile; import org.geoserver.wps.ppio.BoundingBoxPPIO; import org.geoserver.wps.ppio.ComplexPPIO; import org.geoserver.wps.ppio.LiteralPPIO; import org.geoserver.wps.ppio.ProcessParameterIO; import org.geoserver.wps.ppio.RawDataPPIO; import org.geoserver.wps.process.AbstractRawData; import org.geoserver.wps.process.GeoServerProcessors; import org.geotools.data.Parameter; import org.geotools.process.ProcessFactory; import org.opengis.feature.type.Name; import org.springframework.context.ApplicationContext; /** * First-call DescribeProcess class * * @author Lucas Reed, Refractions Research Inc * @author Justin Deoliveira, OpenGEO */ public class DescribeProcess { static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(DescribeProcess.class); WPSInfo wps; ApplicationContext context; Locale locale; XSProfile xsp; Wps10Factory wpsf = Wps10Factory.eINSTANCE; Ows11Factory owsf = Ows11Factory.eINSTANCE; /** * Maps the primitive types that can still be used in process input/output descriptions to * object wrappers that we can use in process descriptions */ static final Map<Class, Class> PRIMITIVE_TO_WRAPPER; static { PRIMITIVE_TO_WRAPPER = new HashMap<Class, Class>(); PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class); PRIMITIVE_TO_WRAPPER.put(short.class, Short.class); PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class); PRIMITIVE_TO_WRAPPER.put(long.class, Long.class); PRIMITIVE_TO_WRAPPER.put(float.class, Float.class); PRIMITIVE_TO_WRAPPER.put(double.class, Double.class); PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class); } public DescribeProcess(WPSInfo wps, ApplicationContext context) { this.wps = wps; this.context = context; locale = Locale.getDefault(); //TODO: creating this ever time this operation is performed is sort of silly // some sort of singleton would be nice xsp = new XSProfile(); } public ProcessDescriptionsType run(DescribeProcessType request) { ProcessDescriptionsType pds = wpsf.createProcessDescriptionsType(); pds.setLang("en"); for ( Iterator i = request.getIdentifier().iterator(); i.hasNext(); ) { CodeType id = (CodeType) i.next(); processDescription( id, pds ); } return pds; } void processDescription( CodeType id, ProcessDescriptionsType pds ) { Name name = Ows11Util.name(id); ProcessFactory pf = GeoServerProcessors.createProcessFactory(name); if ( pf == null || pf.create(name) == null) { throw new WPSException( "No such process: " + id.getValue() ); } ProcessDescriptionType pd = wpsf.createProcessDescriptionType(); pds.getProcessDescription().add( pd ); pd.setProcessVersion( "1.0.0" ); pd.setIdentifier( Ows11Util.code( id.getValue() ) ); pd.setTitle( Ows11Util.languageString(pf.getTitle(name)) ); pd.setAbstract( Ows11Util.languageString(pf.getDescription(name)) ); pd.setStatusSupported(true); pd.setStoreSupported(true); //data inputs DataInputsType inputs = wpsf.createDataInputsType(); pd.setDataInputs(inputs); dataInputs( inputs, pf, name ); //process outputs ProcessOutputsType outputs = wpsf.createProcessOutputsType(); pd.setProcessOutputs( outputs ); processOutputs( outputs, pf, name ); } void dataInputs( DataInputsType inputs, ProcessFactory pf, Name name) { Collection<String> outputMimeParameters = AbstractRawData.getOutputMimeParameters(name, pf) .values(); for(Parameter<?> p : pf.getParameterInfo(name).values()) { // skip the output mime choice params, they will be filled automatically by WPS if (outputMimeParameters.contains(p.key)) { continue; } InputDescriptionType input = wpsf.createInputDescriptionType(); inputs.getInput().add( input ); input.setIdentifier( Ows11Util.code( p.key ) ); input.setTitle( Ows11Util.languageString( p.title ) ); input.setAbstract( Ows11Util.languageString( p.description ) ); // WPS spec specifies non-negative for unlimited inputs, so -1 -> 0 input.setMaxOccurs( p.maxOccurs == -1 ? BigInteger.valueOf( Long.MAX_VALUE ) : BigInteger.valueOf( p.maxOccurs ) ); input.setMinOccurs( BigInteger.valueOf( p.minOccurs ) ); List<ProcessParameterIO> ppios = ProcessParameterIO.findAll( p, context); if ( ppios.isEmpty() ) { throw new WPSException( "Could not find process parameter for type " + p.key + "," + p.type ); } //handle the literal case if (ppios.get( 0 ) instanceof LiteralPPIO ) { LiteralPPIO lppio = (LiteralPPIO) ppios.get( 0 ); LiteralInputType literal = wpsf.createLiteralInputType(); input.setLiteralData( literal ); // map the java class to an xml type name if ( !String.class.equals(lppio.getType()) ) { Class type = lppio.getType(); if(PRIMITIVE_TO_WRAPPER.containsKey(type)) { type = PRIMITIVE_TO_WRAPPER.get(type); } Name typeName = xsp.name(type); if ( typeName != null ) { literal.setDataType(Ows11Util.type("xs:" + typeName.getLocalPart())); } } if (p.metadata.get(Parameter.OPTIONS) != null) { List<Object> options = (List<Object>) p.metadata.get(Parameter.OPTIONS); Object[] optionsArray = (Object[]) options.toArray(new Object[options.size()]); addAllowedValues(literal, optionsArray); } else if (lppio.getType().isEnum()) { Object[] enumValues = lppio.getType().getEnumConstants(); addAllowedValues(literal, enumValues); } else { literal.setAnyValue( owsf.createAnyValueType() ); } try { if (p.sample != null) { literal.setDefaultValue(lppio.encode(p.sample)); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Failed to fill the default value for input " + p.key + " of process " + name, e); } } else if(ppios.get( 0 ) instanceof BoundingBoxPPIO) { input.setBoundingBoxData(buildSupportedCRSType()); } else { //handle the complex data case SupportedComplexDataInputType complex = wpsf.createSupportedComplexDataInputType(); input.setComplexData( complex ); complex.setSupported( wpsf.createComplexDataCombinationsType() ); for ( ProcessParameterIO ppio : ppios ) { ComplexPPIO cppio = (ComplexPPIO) ppio; ComplexDataDescriptionType format = null; if (ppio instanceof RawDataPPIO) { String[] mimeTypes = AbstractRawData.getMimeTypes(p); for (String mimeType : mimeTypes) { ComplexDataDescriptionType ddt = wpsf .createComplexDataDescriptionType(); ddt.setMimeType(mimeType); complex.getSupported().getFormat().add(ddt); if (format == null) { format = ddt; } } } else { format = wpsf.createComplexDataDescriptionType(); format.setMimeType(cppio.getMimeType()); // add to supported complex.getSupported().getFormat().add(format); } //handle the default if ( complex.getDefault() == null ) { ComplexDataDescriptionType def = wpsf.createComplexDataDescriptionType(); def.setMimeType( format.getMimeType() ); complex.setDefault( wpsf.createComplexDataCombinationType() ); complex.getDefault().setFormat( def ); } } } } } private void addAllowedValues(LiteralInputType literal, Object[] values) { AllowedValuesType allowed = owsf.createAllowedValuesType(); for (Object value : values) { ValueType vt = owsf.createValueType(); vt.setValue(value.toString()); allowed.getValue().add(vt); } literal.setAllowedValues(allowed); } private SupportedCRSsType buildSupportedCRSType() { SupportedCRSsType supportedCRS = wpsf.createSupportedCRSsType(); DefaultType def = wpsf.createDefaultType(); def.setCRS("EPSG:4326"); supportedCRS.setDefault(def); // TODO: redo the bindings, supported crs should contain a list, not a single value CRSsType crss = wpsf.createCRSsType(); crss.setCRS("EPSG:4326"); supportedCRS.setSupported(crss); return supportedCRS; } void processOutputs( ProcessOutputsType outputs, ProcessFactory pf, Name name) { Map<String,Parameter<?>> outs = pf.getResultInfo(name, null); for ( Parameter p : outs.values() ) { OutputDescriptionType output = wpsf.createOutputDescriptionType(); outputs.getOutput().add( output ); output.setIdentifier( Ows11Util.code( p.key ) ); output.setTitle( Ows11Util.languageString( p.title ) ); List<ProcessParameterIO> ppios = ProcessParameterIO.findAll( p, context); if ( ppios.isEmpty() ) { throw new WPSException( "Could not find process parameter for type " + p.key + "," + p.type ); } //handle the literal case if ( ppios.get( 0 ) instanceof LiteralPPIO ) { LiteralPPIO lppio = (LiteralPPIO) ppios.get( 0 ); LiteralOutputType literal = wpsf.createLiteralOutputType(); output.setLiteralOutput(literal); //map the java class to an xml type name if ( !String.class.equals( lppio.getType() ) ) { Class type = lppio.getType(); if(PRIMITIVE_TO_WRAPPER.containsKey(type)) { type = PRIMITIVE_TO_WRAPPER.get(type); } Name typeName = xsp.name(type); if ( typeName != null ) { literal.setDataType( Ows11Util.type( typeName.getLocalPart() ) ); } } } else if(ppios.get(0) instanceof BoundingBoxPPIO) { output.setBoundingBoxOutput(buildSupportedCRSType()); } else { //handle the complex data case SupportedComplexDataType complex = wpsf.createSupportedComplexDataType(); output.setComplexOutput( complex ); complex.setSupported( wpsf.createComplexDataCombinationsType() ); for ( ProcessParameterIO ppio : ppios ) { ComplexPPIO cppio = (ComplexPPIO) ppio; ComplexDataDescriptionType format = null; if (ppio instanceof RawDataPPIO) { String[] mimeTypes = AbstractRawData.getMimeTypes(p); for (String mimeType : mimeTypes) { ComplexDataDescriptionType ddt = wpsf .createComplexDataDescriptionType(); ddt.setMimeType(mimeType); complex.getSupported().getFormat().add(ddt); if (format == null) { format = ddt; } } } else { format = wpsf.createComplexDataDescriptionType(); format.setMimeType(cppio.getMimeType()); // add to supported complex.getSupported().getFormat().add(format); } //handle the default if ( complex.getDefault() == null ) { ComplexDataDescriptionType def = wpsf.createComplexDataDescriptionType(); def.setMimeType( format.getMimeType() ); complex.setDefault( wpsf.createComplexDataCombinationType() ); complex.getDefault().setFormat( def ); } } } } } }