diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 34bf4d5937..af46000c6c 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -244,6 +244,7 @@ "OpenLayers/Filter/Logical.js", "OpenLayers/Filter/Comparison.js", "OpenLayers/Filter/Spatial.js", + "OpenLayers/Filter/Function.js", "OpenLayers/Protocol.js", "OpenLayers/Protocol/HTTP.js", "OpenLayers/Protocol/SimpleFilterSerializer.js", diff --git a/lib/OpenLayers/Filter/Function.js b/lib/OpenLayers/Filter/Function.js new file mode 100644 index 0000000000..34cbc7c173 --- /dev/null +++ b/lib/OpenLayers/Filter/Function.js @@ -0,0 +1,52 @@ +/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the Clear BSD license. + * See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Filter.Function + * This class represents a filter function. + * We are using this class for creation of complex + * filters that can contain filter functions as values. + * Nesting function as other functions parameter is supported. + * + * Inherits from + * - + */ +OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: name + * {String} Name of the function. + */ + name: null, + + /** + * APIProperty: params + * {Array( || String || Number)} Function parameters + * For now support only other Functions, String or Number + */ + params: null, + + /** + * Constructor: OpenLayers.Filter.Function + * Creates a filter function. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * function. + * + * Returns: + * {} + */ + initialize: function(options) { + OpenLayers.Filter.prototype.initialize.apply(this, [options]); + }, + + CLASS_NAME: "OpenLayers.Filter.Function" +}); + diff --git a/lib/OpenLayers/Format/Filter/v1.js b/lib/OpenLayers/Format/Filter/v1.js index 913d1f68a7..b89f6b6c0e 100644 --- a/lib/OpenLayers/Format/Filter/v1.js +++ b/lib/OpenLayers/Format/Filter/v1.js @@ -186,6 +186,10 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { "Distance": function(node, obj) { obj.distance = parseInt(this.getChildValue(node)); obj.distanceUnits = node.getAttribute("units"); + }, + "Function": function(node, obj) { + //TODO write decoder for it + return; } } }, @@ -233,6 +237,28 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { return value; }, + /** + * Method: writeOgcExpression + * Limited support for writing OGC expressions. Currently it supports + * ( || String || Number) + * + * Parameters: + * value - ( || String || Number) + * node - {DOMElement} A parent DOM element + * + * Returns: + * {DOMElement} Updated node element. + */ + writeOgcExpression: function(value, node) { + if(value instanceof OpenLayers.Filter.Function){ + var child = this.writeNode("Function", value, node); + node.appendChild(child); + } else { + this.writeNode("Literal", value, node); + } + return node; + }, + /** * Method: write * @@ -303,35 +329,39 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { }, "PropertyIsLessThan": function(filter) { var node = this.createElementNSPlus("ogc:PropertyIsLessThan"); - // no ogc:expression handling for now + // no ogc:expression handling for PropertyName for now this.writeNode("PropertyName", filter, node); - this.writeNode("Literal", filter.value, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); return node; }, "PropertyIsGreaterThan": function(filter) { var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan"); - // no ogc:expression handling for now + // no ogc:expression handling for PropertyName for now this.writeNode("PropertyName", filter, node); - this.writeNode("Literal", filter.value, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); return node; }, "PropertyIsLessThanOrEqualTo": function(filter) { var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo"); - // no ogc:expression handling for now + // no ogc:expression handling for PropertyName for now this.writeNode("PropertyName", filter, node); - this.writeNode("Literal", filter.value, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); return node; }, "PropertyIsGreaterThanOrEqualTo": function(filter) { var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo"); - // no ogc:expression handling for now + // no ogc:expression handling for PropertyName for now this.writeNode("PropertyName", filter, node); - this.writeNode("Literal", filter.value, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); return node; }, "PropertyIsBetween": function(filter) { var node = this.createElementNSPlus("ogc:PropertyIsBetween"); - // no ogc:expression handling for now + // no ogc:expression handling for PropertyName for now this.writeNode("PropertyName", filter, node); this.writeNode("LowerBoundary", filter, node); this.writeNode("UpperBoundary", filter, node); @@ -350,13 +380,13 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { }); }, "LowerBoundary": function(filter) { - // no ogc:expression handling for now + // handle Literals or Functions for now var node = this.createElementNSPlus("ogc:LowerBoundary"); - this.writeNode("Literal", filter.lowerBoundary, node); + this.writeOgcExpression(filter.lowerBoundary, node); return node; }, "UpperBoundary": function(filter) { - // no ogc:expression handling for now + // handle Literals or Functions for now var node = this.createElementNSPlus("ogc:UpperBoundary"); this.writeNode("Literal", filter.upperBoundary, node); return node; @@ -382,6 +412,18 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { }, value: filter.distance }); + }, + "Function": function(filter) { + var node = this.createElementNSPlus("ogc:Function", { + attributes: { + name: filter.name + } + }); + var params = filter.params; + for(var i=0, len=params.length; i' + + '' + + 'the_geom' + + '' + + 'sf:restricted' + + 'the_geom' + + 'cat=3' + + '' + + '' + + ''; + + + var node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "spatial intersect filter with functions correctly written"); + + //test logical filter with custom function + filter = new OpenLayers.Filter.Logical({ + type: OpenLayers.Filter.Logical.AND, + filters: [ + new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, + property: "FOO", + value: new OpenLayers.Filter.Function({ + name : 'customFunction', + params : ['param1', 'param2'] + }) + }) + ] + }); + + out = + '' + + '' + + '' + + 'FOO' + + '' + + 'param1' + + 'param2' + + '' + + '' + + '' + + ''; + + node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "comparison filter with filter functions correctly written"); + + } + + function test_NestedFilterFunctions(t) { + t.plan(1); + + //test spatial dwithin with nested filter function + var filter = new OpenLayers.Filter.Spatial({ + property: 'the_geom', + type: OpenLayers.Filter.Spatial.DWITHIN, + value: new OpenLayers.Filter.Function({ + name : 'collectGeometries', + params: [ + new OpenLayers.Filter.Function({ + name : 'queryCollection', + params: ['sf:roads', 'the_geom', 'INCLUDE'] + }) + ] + }), + distanceUnits: "meters", + distance: 200 + }); + + var out = + '' + + '' + + 'the_geom' + + '' + + '' + + 'sf:roads' + + 'the_geom' + + 'INCLUDE' + + '' + + '' + + '200' + + '' + + ''; + + var parser = new OpenLayers.Format.Filter.v1_0_0(); + var node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written"); + } + diff --git a/tests/Format/Filter/v1_1_0.html b/tests/Format/Filter/v1_1_0.html index ddfe99edfc..52787913a3 100644 --- a/tests/Format/Filter/v1_1_0.html +++ b/tests/Format/Filter/v1_1_0.html @@ -203,6 +203,118 @@ } + function test_FilterFunctions(t) { + t.plan(2); + + var parser = new OpenLayers.Format.Filter.v1_1_0(); + + //test spatial intersects with filter function + var filter = new OpenLayers.Filter.Spatial({ + property: 'the_geom', + type: OpenLayers.Filter.Spatial.INTERSECTS, + value: new OpenLayers.Filter.Function({ + name : 'querySingle', + params: ['sf:restricted', 'the_geom', 'cat=3'] + }) + }); + + var out = + '' + + '' + + 'the_geom' + + '' + + 'sf:restricted' + + 'the_geom' + + 'cat=3' + + '' + + '' + + ''; + + + var node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "spatial intersect filter with functions correctly written"); + + //test logical filter with custom function + filter = new OpenLayers.Filter.Logical({ + type: OpenLayers.Filter.Logical.AND, + filters: [ + new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, + matchCase: false, + property: "FOO", + value: new OpenLayers.Filter.Function({ + name : 'customFunction', + params : ['param1', 'param2'] + }) + }) + ] + }); + + out = + '' + + '' + + '' + + 'FOO' + + '' + + 'param1' + + 'param2' + + '' + + '' + + '' + + ''; + + node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "comparison filter with filter functions correctly written"); + + } + + function test_NestedFilterFunctions(t) { + t.plan(1); + + //test spatial dwithin with nested filter function + var filter = new OpenLayers.Filter.Spatial({ + property: 'the_geom', + type: OpenLayers.Filter.Spatial.DWITHIN, + value: new OpenLayers.Filter.Function({ + name : 'collectGeometries', + params: [ + new OpenLayers.Filter.Function({ + name : 'queryCollection', + params: ['sf:roads', 'the_geom', 'INCLUDE'] + }) + ] + }), + distanceUnits: "meters", + distance: 200 + }); + + var out = + '' + + '' + + 'the_geom' + + '' + + '' + + 'sf:roads' + + 'the_geom' + + 'INCLUDE' + + '' + + '' + + '200' + + '' + + ''; + + var parser = new OpenLayers.Format.Filter.v1_1_0(); + var node = parser.write(filter); + + //test writer + t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written"); + } + +