From ceb95410314ee64d6920f4e1a1a5b2f4b41cd197 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 7 Apr 2013 17:36:54 -0600 Subject: [PATCH 1/3] Correcting or and adding not Previously, OR filters always returned true (fixes #500). --- src/ol/filter/logicalfilter.js | 42 +++++++--- test/spec/ol/filter/logicalfilter.test.js | 97 +++++++++++++++++++++++ 2 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 test/spec/ol/filter/logicalfilter.test.js diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js index 89f438fc33..cd5cc66d9e 100644 --- a/src/ol/filter/logicalfilter.js +++ b/src/ol/filter/logicalfilter.js @@ -1,6 +1,7 @@ goog.provide('ol.filter.Logical'); goog.provide('ol.filter.LogicalOperator'); +goog.require('goog.asserts'); goog.require('ol.filter.Filter'); @@ -9,7 +10,7 @@ goog.require('ol.filter.Filter'); * @constructor * @extends {ol.filter.Filter} * @param {Array.} filters Filters to and-combine. - * @param {!ol.filter.LogicalOperator} operator Operator. + * @param {ol.filter.LogicalOperator} operator Operator. */ ol.filter.Logical = function(filters, operator) { goog.base(this); @@ -19,9 +20,10 @@ ol.filter.Logical = function(filters, operator) { * @private */ this.filters_ = filters; + goog.asserts.assert(filters.length > 0, 'Must supply at least one filter'); /** - * @type {!ol.filter.LogicalOperator} + * @type {ol.filter.LogicalOperator} */ this.operator = operator; @@ -35,14 +37,29 @@ goog.inherits(ol.filter.Logical, ol.filter.Filter); ol.filter.Logical.prototype.applies = function(feature) { var filters = this.filters_, i = 0, ii = filters.length, - operator = this.operator, - start = operator(true, false), - result = start; - while (result === start && i < ii) { - result = operator(result, filters[i].applies(feature)); - ++i; + result; + switch (this.operator) { + case ol.filter.LogicalOperator.AND: + result = true; + while (result && i < ii) { + result = result && filters[i].applies(feature); + ++i; + } + break; + case ol.filter.LogicalOperator.OR: + result = false; + while (!result && i < ii) { + result = result || filters[i].applies(feature); + ++i; + } + break; + case ol.filter.LogicalOperator.NOT: + result = !filters[i].applies(feature); + break; + default: + goog.asserts.assert(false, 'Unsupported operation: ' + this.operator); } - return result; + return !!result; }; @@ -55,9 +72,10 @@ ol.filter.Logical.prototype.getFilters = function() { /** - * @enum {!Function} + * @enum {string} */ ol.filter.LogicalOperator = { - AND: /** @return {boolean} result. */ function(a, b) { return a && b; }, - OR: /** @return {boolean} result. */ function(a, b) { return a || b; } + AND: '&&', + OR: '||', + NOT: '!' }; diff --git a/test/spec/ol/filter/logicalfilter.test.js b/test/spec/ol/filter/logicalfilter.test.js new file mode 100644 index 0000000000..61eadbb1ee --- /dev/null +++ b/test/spec/ol/filter/logicalfilter.test.js @@ -0,0 +1,97 @@ +goog.provide('ol.test.filter.Logical'); + + +describe('ol.filter.Logical', function() { + + var OR = ol.filter.LogicalOperator.OR; + var AND = ol.filter.LogicalOperator.AND; + var NOT = ol.filter.LogicalOperator.NOT; + var include = new ol.filter.Filter(function() {return true}); + var exclude = new ol.filter.Filter(function() {return false}); + + var apple = new ol.Feature({}); + var orange = new ol.Feature({}); + var duck = new ol.Feature({}); + + var isApple = new ol.filter.Filter(function(feature) { + return feature === apple; + }); + var isOrange = new ol.filter.Filter(function(feature) { + return feature === orange; + }); + var isDuck = new ol.filter.Filter(function(feature) { + return feature === duck; + }); + + describe('constructor', function() { + it('creates a new filter', function() { + var filter = new ol.filter.Logical([include, exclude], OR); + expect(filter).to.be.a(ol.filter.Logical); + }); + }); + + describe('#operator', function() { + it('can be OR', function() { + var filter = new ol.filter.Logical([include, exclude], OR); + expect(filter.operator).to.be(OR); + }); + + it('can be AND', function() { + var filter = new ol.filter.Logical([include, exclude], AND); + expect(filter.operator).to.be(AND); + }); + + it('can be NOT', function() { + var filter = new ol.filter.Logical([include], NOT); + expect(filter.operator).to.be(NOT); + }); + }); + + describe('#applies', function() { + + it('works for OR', function() { + var isFruit = new ol.filter.Logical([isApple, isOrange], OR); + + expect(isApple.applies(apple)).to.be(true); + expect(isOrange.applies(apple)).to.be(false); + expect(isFruit.applies(apple)).to.be(true); + + expect(isApple.applies(duck)).to.be(false); + expect(isOrange.applies(duck)).to.be(false); + expect(isFruit.applies(duck)).to.be(false); + }); + + it('works for AND', function() { + expect(include.applies(apple)).to.be(true); + expect(isApple.applies(apple)).to.be(true); + expect(isDuck.applies(apple)).to.be(false); + + var pass = new ol.filter.Logical([include, isApple], AND); + expect(pass.applies(apple)).to.be(true); + + var fail = new ol.filter.Logical([isApple, isDuck], AND); + expect(fail.applies(apple)).to.be(false); + }); + + it('works for NOT', function() { + expect(isApple.applies(apple)).to.be(true); + expect(isDuck.applies(apple)).to.be(false); + expect(isDuck.applies(duck)).to.be(true); + expect(isDuck.applies(apple)).to.be(false); + + var notApple = new ol.filter.Logical([isApple], NOT); + expect(notApple.applies(apple)).to.be(false); + expect(notApple.applies(duck)).to.be(true); + + var notDuck = new ol.filter.Logical([isDuck], NOT); + expect(notDuck.applies(apple)).to.be(true); + expect(notDuck.applies(duck)).to.be(false); + }); + }); + +}); + +goog.require('ol.Feature'); +goog.require('ol.filter.Filter'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); From 27e23cce9c51892ca62197a423eebdbc1744a621 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 7 Apr 2013 17:37:31 -0600 Subject: [PATCH 2/3] Convenient factories for logical filters --- src/ol/filter/logicalfilter.js | 37 ++++++++++++++++++ test/spec/ol/filter/logicalfilter.test.js | 47 +++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js index cd5cc66d9e..3ba8dd932c 100644 --- a/src/ol/filter/logicalfilter.js +++ b/src/ol/filter/logicalfilter.js @@ -1,5 +1,8 @@ goog.provide('ol.filter.Logical'); goog.provide('ol.filter.LogicalOperator'); +goog.provide('ol.filter.and'); +goog.provide('ol.filter.not'); +goog.provide('ol.filter.or'); goog.require('goog.asserts'); goog.require('ol.filter.Filter'); @@ -79,3 +82,37 @@ ol.filter.LogicalOperator = { OR: '||', NOT: '!' }; + + +/** + * Create a filter that evaluates to true if any of the provided filters + * evaluate to true. + * @param {...ol.filter.Filter} var_filters Filters. + * @return {ol.filter.Logical} A logical filter. + */ +ol.filter.and = function(var_filters) { + var filters = Array.prototype.slice.call(arguments); + return new ol.filter.Logical(filters, ol.filter.LogicalOperator.AND); +}; + + +/** + * Create a new filter that is the logical compliment of another. + * @param {ol.filter.Filter} filter The filter to negate. + * @return {ol.filter.Logical} A logical filter. + */ +ol.filter.not = function(filter) { + return new ol.filter.Logical([filter], ol.filter.LogicalOperator.NOT); +}; + + +/** + * Create a filter that evaluates to true if all of the provided filters + * evaluate to true. + * @param {...ol.filter.Filter} var_filters Filters. + * @return {ol.filter.Logical} A logical filter. + */ +ol.filter.or = function(var_filters) { + var filters = Array.prototype.slice.call(arguments); + return new ol.filter.Logical(filters, ol.filter.LogicalOperator.OR); +}; diff --git a/test/spec/ol/filter/logicalfilter.test.js b/test/spec/ol/filter/logicalfilter.test.js index 61eadbb1ee..970c0d0c10 100644 --- a/test/spec/ol/filter/logicalfilter.test.js +++ b/test/spec/ol/filter/logicalfilter.test.js @@ -91,7 +91,54 @@ describe('ol.filter.Logical', function() { }); +describe('ol.filter.and', function() { + it('creates the a logical AND filter', function() { + var a = new ol.filter.Filter(); + var b = new ol.filter.Filter(); + var c = new ol.filter.Filter(); + var and = ol.filter.and(a, b, c); + expect(and).to.be.a(ol.filter.Logical); + expect(and.operator).to.be(ol.filter.LogicalOperator.AND); + var filters = and.getFilters(); + expect(filters[0]).to.be(a); + expect(filters[1]).to.be(b); + expect(filters[2]).to.be(c); + }); +}); + +describe('ol.filter.not', function() { + it('creates the logical compliment of another filter', function() { + var include = new ol.filter.Filter(function() {return true;}); + var notInclude = ol.filter.not(include); + expect(notInclude).to.be.a(ol.filter.Logical); + expect(notInclude.applies()).to.be(false); + + var exclude = new ol.filter.Filter(function() {return false;}); + var notExclude = ol.filter.not(exclude); + expect(notExclude).to.be.a(ol.filter.Logical); + expect(notExclude.applies()).to.be(true); + }); +}); + +describe('ol.filter.or', function() { + it('creates the a logical OR filter', function() { + var a = new ol.filter.Filter(); + var b = new ol.filter.Filter(); + var c = new ol.filter.Filter(); + var and = ol.filter.or(a, b, c); + expect(and).to.be.a(ol.filter.Logical); + expect(and.operator).to.be(ol.filter.LogicalOperator.OR); + var filters = and.getFilters(); + expect(filters[0]).to.be(a); + expect(filters[1]).to.be(b); + expect(filters[2]).to.be(c); + }); +}); + goog.require('ol.Feature'); goog.require('ol.filter.Filter'); goog.require('ol.filter.Logical'); goog.require('ol.filter.LogicalOperator'); +goog.require('ol.filter.and'); +goog.require('ol.filter.not'); +goog.require('ol.filter.or'); From f0c62294f996e3c2c5aaf1e2a8d881b22463ef9d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 7 Apr 2013 17:42:52 -0600 Subject: [PATCH 3/3] Doc fixes --- src/ol/filter/logicalfilter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js index 3ba8dd932c..c32b675b1f 100644 --- a/src/ol/filter/logicalfilter.js +++ b/src/ol/filter/logicalfilter.js @@ -85,10 +85,10 @@ ol.filter.LogicalOperator = { /** - * Create a filter that evaluates to true if any of the provided filters + * Create a filter that evaluates to true if all of the provided filters * evaluate to true. * @param {...ol.filter.Filter} var_filters Filters. - * @return {ol.filter.Logical} A logical filter. + * @return {ol.filter.Logical} A logical AND filter. */ ol.filter.and = function(var_filters) { var filters = Array.prototype.slice.call(arguments); @@ -99,7 +99,7 @@ ol.filter.and = function(var_filters) { /** * Create a new filter that is the logical compliment of another. * @param {ol.filter.Filter} filter The filter to negate. - * @return {ol.filter.Logical} A logical filter. + * @return {ol.filter.Logical} A logical NOT filter. */ ol.filter.not = function(filter) { return new ol.filter.Logical([filter], ol.filter.LogicalOperator.NOT); @@ -107,10 +107,10 @@ ol.filter.not = function(filter) { /** - * Create a filter that evaluates to true if all of the provided filters + * Create a filter that evaluates to true if any of the provided filters * evaluate to true. * @param {...ol.filter.Filter} var_filters Filters. - * @return {ol.filter.Logical} A logical filter. + * @return {ol.filter.Logical} A logical OR filter. */ ol.filter.or = function(var_filters) { var filters = Array.prototype.slice.call(arguments);