Author: aaime Date: 2012-04-12 07:29:44 -0700 (Thu, 12 Apr 2012) New Revision: 38662 Added: trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/VectorIdentityRTProcess.java Modified: trunk/modules/unsupported/process/src/main/java/org/geotools/process/factory/AnnotationDrivenProcessFactory.java trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/ProcessFunction.java trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/RenderingProcessFunction.java trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/BeanProcessFactoryTest.java Log: [GEOT-4060] Process parameters not passed to RenderingTransformation invert methods, patch by Martin Davis Modified: trunk/modules/unsupported/process/src/main/java/org/geotools/process/factory/AnnotationDrivenProcessFactory.java =================================================================== --- trunk/modules/unsupported/process/src/main/java/org/geotools/process/factory/AnnotationDrivenProcessFactory.java 2012-04-12 13:56:48 UTC (rev 38661) +++ trunk/modules/unsupported/process/src/main/java/org/geotools/process/factory/AnnotationDrivenProcessFactory.java 2012-04-12 14:29:44 UTC (rev 38662) @@ -41,7 +41,14 @@ /** * A process factory that uses annotations to determine much of the metadata - * needed to describe a process. + * needed to describe a {@link Process}. + * <p> + * The annotations supported are: + * <ul> + * <li>{@link DescribeProcess} - describes the process functionality + * <li>{@link DescribeResult} - describes a process result + * <li>{@link DescribeParameter} - describes a process parameter + * </ul> * * @author jody * @author aaime @@ -339,8 +346,12 @@ protected abstract Object createProcessBean(Name name); /** - * Executes the method as a process; when process execute is called the method will be - * invoked to produce a result. + * A wrapper which executes the given method as a {@link Process}. + * When the process {@link #execute(Map, ProgressListener)} is called + * the method is invoked to produce a result. + * The mapping from the process parameters to the method parameters + * is determined by the {@link DescribeParameter} annotations on the method parameters. + * */ class InvokeMethodProcess implements Process { /** @@ -352,6 +363,12 @@ */ Object targetObject; + /** + * Creates a wrapper for invoking a method as a process + * + * @param method method to invoke. May be static + * @param targetObject object used to invoke method. May be null + */ public InvokeMethodProcess(Method method, Object targetObject) { this.method = method; this.targetObject = targetObject; @@ -518,22 +535,38 @@ /** - * Executes the method as a rendering process. + * A wrapper which executes the given method as a {@linkplain RenderingProcess}. + * When the process {@link #execute(Map, ProgressListener)} is called + * the method is invoked to produce a result. + * The mapping from the process parameters to the method parameters + * is determined by the {@link DescribeParameter} annotations on the method parameters. * <p> - * This implementation supports the additional methods required for RenderingProcess: + * This implementation supports the additional methods required for a RenderingProcess: * <ul> * <li>invertQuery * <li>invertGridGeometry * </ul> + * The signature of these methods in the Process class is annotation-driven. + * Each method must accept a {@link Query} and a {@link GridGeometry} as its final parameters, + * but may have any number of parameters preceding them. + * These parameters must be a subset of the parameters of the given execution + * method, and they use the same annotation to describe them. + * */ class InvokeMethodRenderingProcess extends InvokeMethodProcess implements RenderingProcess { + /** + * Creates a wrapper for invoking a method as a process + * + * @param method method to invoke. May be static + * @param targetObject object used to invoke method. May be null + */ public InvokeMethodRenderingProcess(Method method, Object targetObject) { super(method, targetObject); } public Query invertQuery(Map<String, Object> input, Query targetQuery, - GridGeometry gridGeometry) throws ProcessException { + GridGeometry targetGridGeometry) throws ProcessException { Method invertQueryMethod = lookupInvertQuery(targetObject, method.getName() ); if (invertQueryMethod == null) { @@ -543,7 +576,7 @@ try { Object[] args = buildProcessArguments(invertQueryMethod, input, null, true); args[args.length - 2] = targetQuery; - args[args.length - 1] = gridGeometry; + args[args.length - 1] = targetGridGeometry; return (Query) invertQueryMethod.invoke(targetObject, args); Modified: trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/ProcessFunction.java =================================================================== --- trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/ProcessFunction.java 2012-04-12 13:56:48 UTC (rev 38661) +++ trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/ProcessFunction.java 2012-04-12 14:29:44 UTC (rev 38662) @@ -41,8 +41,12 @@ import org.opengis.filter.expression.Literal; /** - * A function wrapping a {@link Process} with a single output. All inputs to the function are - * supposed to evaluate to Map<String, Object> where the key is the name of an argument and the + * A wrapper allowing a {@link Process} with a single output to be called as a {@link Function}. + * Since Function parameters are positional and Process parameters are named, + * the following strategy is used to allow specifying named Process parameters + * as function inputs. + * All inputs to the function must evaluate to Map<String, Object>, + * with a single entry where the key is the name of a process parameter and the * value is the argument value * * @author Andrea Aime - GeoSolutions @@ -96,7 +100,41 @@ } public Object evaluate(Object object) { - // collect the entries + Map<String, Object> processInputs = evaluateInputs(object); + + // execute the process + try { + ExceptionProgressListener listener = new ExceptionProgressListener(); + Map<String, Object> results = process.execute(processInputs, listener); + + // some processes have the bad habit of not throwing exceptions, but to + // report them to the listener + if(listener.getExceptions().size() > 0) { + // uh oh, an exception occurred during processing + Throwable t = listener.getExceptions().get(0); + throw new RuntimeException("Failed to evaluate process function, error is: " + + t.getMessage(), t); + } + + return getResult(results, processInputs); + } catch (ProcessException e) { + throw new RuntimeException("Failed to evaluate the process function, error is: " + + e.getMessage(), e); + } + } + + /** + * Evaluates the process input expressions. + * The object provides the context for evaluating the input expressions, + * and may be null if no context is available + * (for instance, when being called to evaluation the inputs + * for the {@link RenderingProcessFunction} inversion methods). + * + * @param object the object to evaluate the input expressions against. + * @return the map of inputs + */ + protected Map<String, Object> evaluateInputs(Object object) { + // collect the entries Map<String, Object> processInputs = new HashMap<String, Object>(); for (Expression input : inputExpressions) { Object result = input.evaluate(object, Map.class); @@ -168,28 +206,9 @@ } } } + return processInputs; + } - // execute the process - try { - ExceptionProgressListener listener = new ExceptionProgressListener(); - Map<String, Object> results = process.execute(processInputs, listener); - - // some processes have the bad habit of not throwing exceptions, but to - // report them to the listener - if(listener.getExceptions().size() > 0) { - // uh oh, an exception occurred during processing - Throwable t = listener.getExceptions().get(0); - throw new RuntimeException("Failed to evaluate process function, error is: " - + t.getMessage(), t); - } - - return getResult(results, processInputs); - } catch (ProcessException e) { - throw new RuntimeException("Failed to evaluate the process function, error is: " - + e.getMessage(), e); - } - } - private Object getResult(Map<String, Object> results, Map<String, Object> processInputs) { if (results.size() == 1) { return results.values().iterator().next(); Modified: trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/RenderingProcessFunction.java =================================================================== --- trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/RenderingProcessFunction.java 2012-04-12 13:56:48 UTC (rev 38661) +++ trunk/modules/unsupported/process/src/main/java/org/geotools/process/function/RenderingProcessFunction.java 2012-04-12 14:29:44 UTC (rev 38662) @@ -48,10 +48,11 @@ public Query invertQuery(Query targetQuery, GridGeometry gridGeometry) { RenderingProcess process = (RenderingProcess) this.process; - Map<String, Object> params = new HashMap<String,Object>(); - params.putAll(parameters); + // evaluate input expressions + // at this point do not have an object to evaluate them against + Map<String, Object> inputs = evaluateInputs(null); try { - return process.invertQuery(params, targetQuery, gridGeometry); + return process.invertQuery(inputs, targetQuery, gridGeometry); } catch (ProcessException e) { throw new RuntimeException("Failed to invert the query, error is: " + e.getMessage(), e); @@ -60,10 +61,11 @@ public GridGeometry invertGridGeometry(Query targetQuery, GridGeometry targetGridGeometry) { RenderingProcess process = (RenderingProcess) this.process; - Map<String, Object> params = new HashMap<String,Object>(); - params.putAll(parameters); + // evaluate input expressions + // at this point do not have an object to evaluate them against + Map<String, Object> inputs = evaluateInputs(null); try { - return process.invertGridGeometry(params, targetQuery, targetGridGeometry); + return process.invertGridGeometry(inputs, targetQuery, targetGridGeometry); } catch (ProcessException e) { throw new RuntimeException("Failed to invert the grid geometry, error is: " + e.getMessage(), e); Modified: trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/BeanProcessFactoryTest.java =================================================================== --- trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/BeanProcessFactoryTest.java 2012-04-12 13:56:48 UTC (rev 38661) +++ trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/BeanProcessFactoryTest.java 2012-04-12 14:29:44 UTC (rev 38662) @@ -9,24 +9,35 @@ import junit.framework.TestCase; import org.geotools.data.Parameter; +import org.geotools.data.Query; import org.geotools.data.collection.ListFeatureCollection; +import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.factory.FactoryIteratorProvider; import org.geotools.factory.GeoTools; -import org.geotools.feature.FeatureCollection; import org.geotools.feature.NameImpl; +import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.process.ProcessException; import org.geotools.process.ProcessFactory; import org.geotools.process.Processors; +import org.geotools.process.RenderingProcess; import org.geotools.util.SimpleInternationalString; +import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.Name; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.InternationalString; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory; + /** * Tests some processes that do not require integration with the application context * * @author Andrea Aime - OpenGeo + * @author Martin Davis - OpenGeo * * * @source $URL$ @@ -37,7 +48,9 @@ public BeanProcessFactory() { super(new SimpleInternationalString("Some bean based processes custom processes"), - "bean", IdentityProcess.class); + "bean", + IdentityProcess.class, + VectorIdentityRTProcess.class); } } @@ -115,4 +128,51 @@ assertNotNull(buffer); } + public void testInvertQuery() throws ProcessException { + // prepare a mock feature collection + SimpleFeatureCollection data = buildTestFeatures(); + + org.geotools.process.Process transformation = factory.create(new NameImpl("bean", "VectorIdentityRT")); + Map<String, Object> inputs = new HashMap<String, Object>(); + inputs.put("data", data); + inputs.put("value", 10); + + RenderingProcess tx = (RenderingProcess) transformation; + Query dummyQuery = tx.invertQuery(inputs, null, null); + + Map<String, Object> result = transformation.execute(inputs, null); + + assertEquals(1, result.size()); + + SimpleFeatureCollection computed = (SimpleFeatureCollection) result.get("result"); + + assertEquals(data, computed); + assertEquals(data, computed); + assertSame(data, computed); + } + + + private SimpleFeatureCollection buildTestFeatures() + { + SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); + tb.setName("test"); + // this should be populated correctly + CoordinateReferenceSystem crs = null; + tb.add("geom", Geometry.class, crs ); + tb.add("count", Integer.class); + SimpleFeatureType schema = tb.buildFeatureType(); + + SimpleFeatureCollection fc = new ListFeatureCollection(schema); + SimpleFeatureBuilder fb = new SimpleFeatureBuilder(schema); + + GeometryFactory factory = new GeometryFactory(new PackedCoordinateSequenceFactory()); + + Geometry point = factory.createPoint(new Coordinate(10, 10)); + fb.add(point); + fb.add(5); + + fc.add(fb.buildFeature(null)); + + return fc; + } } Added: trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/VectorIdentityRTProcess.java =================================================================== --- trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/VectorIdentityRTProcess.java (rev 0) +++ trunk/modules/unsupported/process/src/test/java/org/geotools/process/factory/VectorIdentityRTProcess.java 2012-04-12 14:29:44 UTC (rev 38662) @@ -0,0 +1,43 @@ +package org.geotools.process.factory; + +import org.geotools.data.Query; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.process.ProcessException; +import org.geotools.process.gs.GSProcess; +import org.opengis.coverage.grid.GridGeometry; + +/** + * A simple Rendering Transformation process for testing aspects of how transformations are called. + * + * + * @author Martin Davis - OpenGeo + * + */ +@DescribeProcess(title = "SimpleVectorRTProcess", description = "Simple test RT process taking a vector dataset as input.") +public class VectorIdentityRTProcess implements GSProcess { + /** + * Note: for testing purposes only. A real Rendering Transformation must never store state. + */ + int invertQueryValue; + + @DescribeResult(name = "result", description = "The result") + public SimpleFeatureCollection execute( + // process data + @DescribeParameter(name = "data", description = "Features to process") SimpleFeatureCollection data, + @DescribeParameter(name = "value", description = "Value for testing") Integer value) + throws ProcessException { + if (value != invertQueryValue) { + throw new IllegalStateException("Values do not match"); + } + return data; + } + + public Query invertQuery( + @DescribeParameter(name = "value", description = "Value for testing") Integer value, + Query targetQuery, GridGeometry targetGridGeometry) throws ProcessException { + + invertQueryValue = value; + + return targetQuery; + } +} \ No newline at end of file |