diff --git a/src/common.js b/src/common.js index e07b6b0e8d..818c9ff655 100644 --- a/src/common.js +++ b/src/common.js @@ -2571,22 +2571,79 @@ return models; } }, + _bool_operator: function(domain) { + var bool_op = 'AND'; + if ((domain.length > 0) && + ((domain[0] == 'AND') || (domain[0] == 'OR'))) { + bool_op = domain[0]; + } + return bool_op; + }, + simplify_nested: function(domain) { + if (!domain.length) { + return []; + } else if (this.is_leaf(domain)) { + return [domain]; + } else if ((domain == 'AND') || (domain == 'OR')) { + return [domain]; + } else if ((domain instanceof Array) && (domain.length == 1)) { + return this.simplify_nested(domain[0]); + } else { + var simplified = []; + var domain_op = this._bool_operator(domain); + for (var branch of domain) { + var simplified_branch = this.simplify_nested(branch); + if ((this._bool_operator(branch) == domain_op) || + (simplified_branch.length == 1)) { + simplified.push(...simplified_branch); + } else { + simplified.push(simplified_branch); + } + } + return simplified; + } + }, + simplify_duplicate: function(domain) { + var dedup_branches = []; + var bool_op = null; + if (~['AND', 'OR'].indexOf(domain[0])) { + bool_op = domain[0]; + domain = domain.slice(1); + } + for (var branch of domain) { + var simplified_branch = this.simplify(branch); + if (simplified_branch.length == 0) { + if (bool_op === 'OR') { + return []; + } else { + continue; + } + } + var found_branch = false; + for (var duped_branch of dedup_branches) { + if (Sao.common.compare( + simplified_branch, duped_branch)) { + found_branch = true; + break; + } + } + if (!found_branch) { + dedup_branches.push(simplified_branch); + } + } + + if (bool_op && (dedup_branches.length > 1)) { + dedup_branches.unshift(bool_op); + } + return dedup_branches; + }, simplify: function(domain) { if (this.is_leaf(domain)) { - return domain; - } else if (~['OR', 'AND'].indexOf(domain)) { - return domain; - } else if ((domain instanceof Array) && (domain.length == 1) && - (~['OR', 'AND'].indexOf(domain[0]))) { + return [domain]; + } else if (!domain.length) { return []; - } else if ((domain instanceof Array) && (domain.length == 1) && - (!this.is_leaf(domain[0]))) { - return this.simplify(domain[0]); - } else if ((domain instanceof Array) && (domain.length == 2) && - ~['AND', 'OR'].indexOf(domain[0])) { - return [this.simplify(domain[1])]; } else { - return domain.map(this.simplify.bind(this)); + return this.simplify_nested(this.simplify_duplicate(domain)); } }, merge: function(domain, domoperator) { diff --git a/tests/sao.js b/tests/sao.js index 304b3f4116..a81cea0f49 100644 --- a/tests/sao.js +++ b/tests/sao.js @@ -2574,7 +2574,7 @@ JSON.stringify(context) + ')'); context = {y: 4, z: 'b'}; QUnit.ok(compare(domain_inversion(domain, 'x', context), - ['OR', [['x', '=', 3]], [['x', '=', 2]]]), + ['OR', ['x', '=', 3], ['x', '=', 2]]), 'domain_inversion(' + JSON.stringify(domain) + ', \'x\', ' + JSON.stringify(context) + ')'); }); @@ -2600,15 +2600,25 @@ var compare = Sao.common.compare; [ - [[['x', '=', 3]], [['x', '=', 3]]], - [[[['x', '=', 3]]], [['x', '=', 3]]], - [['OR', ['x', '=', 3]], [['x', '=', 3]]], - [['OR', [['x', '=', 3]], [['y', '=', 5]]], - ['OR', [['x', '=', 3]], [['y', '=', 5]]]], - [['OR', ['x', '=', 3], ['AND', ['y', '=', 5]]], - ['OR', ['x', '=', 3], [['y', '=', 5]]]], - [['AND'], []], - [['OR'], []] + [[['x', '=', 3]], [['x', '=', 3]]], + [[[['x', '=', 3]]], [['x', '=', 3]]], + [ + [[['x', '=', 3], ['y', '=', 4]]], + [['x', '=', 3], ['y', '=', 4]]], + [['OR', ['x', '=', 3]], [['x', '=', 3]]], + [ + ['OR', [['x', '=', 3]], [['y', '=', 5]]], + ['OR', ['x', '=', 3], ['y', '=', 5]]], + [ + ['OR', ['x', '=', 3], ['AND', ['y', '=', 5]]], + ['OR', ['x', '=', 3], ['y', '=', 5]]], + [[['x', '=', 3], ['OR']], [['x', '=', 3]]], + [['OR', ['x', '=', 3], []], []], + [['OR', ['x', '=', 3], ['OR']], []], + [[['x', '=', 3], []], [['x', '=', 3]]], + [[['x', '=', 3], ['AND']], [['x', '=', 3]]], + [['AND'], []], + [['OR'], []] ].forEach(function(test) { var domain = test[0]; var result = test[1]; @@ -2617,6 +2627,41 @@ }); }); + QUnit.test('DomainInversion simplify deduplicate', function() { + var domain_inversion = new Sao.common.DomainInversion(); + var simplify = domain_inversion.simplify.bind(domain_inversion); + var compare = Sao.common.compare; + + var clause = ['x', '=', 3]; + var another = ['y', '=', 4]; + var third = ['z', '=', 5]; + [ + [[], []], + [['OR', []], []], + [['AND', []], []], + [[clause], [clause]], + [['OR', clause], [clause]], + [[clause, clause], [clause]], + [['OR', clause, clause], [clause]], + [[clause, [clause, clause]], [clause]], + [[clause, another], [clause, another]], + [['OR', clause, another], ['OR', clause, another]], + [[clause, clause, another], [clause, another]], + [[clause, [clause, clause], another], [clause, another]], + [[clause, clause, another, another], [clause, another]], + [[clause, another, clause, another], [clause, another]], + [ + ['AND', ['OR', clause, another], third], + ['AND', ['OR', clause, another], third]], + ].forEach(function(test) { + var domain = test[0]; + var result = test[1]; + QUnit.ok(compare(simplify(domain), result), + 'simplify(' + JSON.stringify(domain) + ')'); + }); + + }); + QUnit.test('DomainInversion merge', function() { var domain_inversion = new Sao.common.DomainInversion(); var merge = domain_inversion.merge.bind(domain_inversion); @@ -2669,7 +2714,7 @@ QUnit.ok(compare(concat([[], []]), []), 'compare(' + JSON.stringify([[], []]) + ')'); QUnit.ok(compare(concat([domain1, domain2], 'OR'), - ['OR', [['a', '=', 1]], [['b', '=', 2]]]), + ['OR', ['a', '=', 1], ['b', '=', 2]]), 'compare(' + JSON.stringify([domain1, domain2]) + ', \'OR\')'); });