fix #54: simplify filters for contradicting rules and ignore unsound rules alltogether

This commit is contained in:
Konstantin Käfer 2011-01-25 12:39:20 -05:00
parent bbcca46f6f
commit 7e33846de5
5 changed files with 74 additions and 9 deletions

View File

@ -305,8 +305,13 @@ mess.Renderer = function Renderer(env) {
// TODO: run uniq on this.
filters.push.apply(filters, negatedFilters);
// add this definition's filter's negations to the list
negatedFilters.push.apply(negatedFilters, negation);
// Throw out contradicting rules.
if (!tree.Filter.sound(filters)) {
continue;
} else {
// add this definition's filter's negations to the list
negatedFilters.push.apply(negatedFilters, negation);
}
definition.selector.filters = filters;
definition.selector.zoom = zoom;

View File

@ -68,6 +68,7 @@ tree.Definition.prototype.unique_rules = function() {
tree.Definition.prototype.toXML = function(env) {
if (this._xml) return this._xml;
var sym = this.symbolizers();
if (!env.returnErrors && sym.length !== 1) {
throw {

View File

@ -9,22 +9,63 @@ tree.Filter = function Filter(key, op, val, index) {
tree.Filter.prototype.toXML = function(env) {
if (this.val.is) {
this.val = this.val.toString((this.val.is == 'string'));
var value = this.val.toString((this.val.is == 'string'));
} else {
var value = this.val;
}
return '[' + this.key + '] ' +
this.op.toString() +
' ' +
this.val;
value;
};
/**
* Negate this filter: warning - this changes
* the filter itself.
*
* TODO: should this have an index?
*/
tree.Filter.prototype.negate = function() {
return new tree.Filter(this.key, this.op.negate(), this.val);
return new tree.Filter(this.key, this.op.negate(), this.val, this.index);
};
tree.Filter.prototype.conflictsWith = function(filter) {
if (this.key !== filter.key) return;
if (this.val.toString() !== filter.val.toString() &&
this.op.value === '=' &&
filter.op.value === '=') {
return true;
}
if (this.val.toString() === filter.val.toString() &&
((this.op.value === '!=' && filter.op.value === '=') ||
(this.op.value === '=' && filter.op.value === '!='))) {
return true;
}
// Doesn't yet check for > and <.
};
/**
* Removes all filters from the list that are overridden by this one.
* In case another filter is present that overrides this one, the filter won't
* be added to the list of filters.
*/
tree.Filter.prototype.overrides = function(filter) {
if (this.key !== filter.key) return;
if (this.op.value === '=') return true;
};
tree.Filter.sound = function(filters) {
for (var i = 0; i < filters.length; i++) {
for (var j = i + 1; j < filters.length; j++) {
if (filters[i].conflictsWith(filters[j])) {
return false;
}
}
}
return true;
};
})(require('mess/tree'));

View File

@ -25,7 +25,7 @@ tree.Operation.prototype.eval = function(env) {
return a.operate(this.op, b);
};
tree.operate = function(op, a, b) {
tree.operate = function operate(op, a, b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;

View File

@ -55,11 +55,29 @@ tree.Selector.prototype.layers = function(env) {
}).join(' ');
};
tree.Selector.prototype.simplifiedFilters = function() {
var simplified = [];
filters: for (var i = 0; i < this.filters.length; i++) {
// Operate from the back so that we don't run into renumbering problems
// when deleting items from the array.
for (var j = simplified.length - 1; j >= 0; j--) {
if (simplified[j].overrides(this.filters[i])) {
continue filters;
}
else if (this.filters[i].overrides(simplified[j])) {
simplified.splice(j, 1);
}
}
simplified.push(this.filters[i]);
}
return simplified;
};
tree.Selector.prototype.combinedFilter = function(env) {
var filters = this.zoom ? this.zoom.toXML() : [];
if (this.filters.length) {
filters.push('<Filter>' + this.filters.map(function(f) {
filters.push('<Filter>' + this.simplifiedFilters().map(function(f) {
// TODO: this is required to support selectors
// that haven't been processed in the render() function.
if (f instanceof tree.Filter) {