-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconstraint-solver.js
More file actions
109 lines (88 loc) · 3.9 KB
/
constraint-solver.js
File metadata and controls
109 lines (88 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
function ConstraintSolver(model) {
if (!(this instanceof ConstraintSolver))
return new ConstraintSolver(model);
var self = this;
var solver = this.solver = new Logic.Solver();
function getCrossTreeConstraints() {
return model.xmlModel.rules.get().map(self.crossTreeConstraint.bind(self));
}
var featureConstraintSemantics = [
function root(feature) {
if (!feature.parent)
return feature.name;
},
function mandatory(feature) {
if (feature.parent && feature.mandatory)
return Logic.equiv(feature.name, feature.parent.name);
},
function optional(feature) {
if (feature.parent)
return Logic.implies(feature.name, feature.parent.name);
},
function alternative(feature) {
if (feature.alternative) {
var children = feature.children.map(featureName());
var alternativeConstraints = [];
for (var i = 0; i < children.length; i++)
for (var j = 0; j < i; j++)
alternativeConstraints.push(Logic.not(Logic.and(children[i], children[j])));
return Logic.and(Logic.equiv(feature.name, Logic.or(children)), alternativeConstraints);
}
},
function or(feature) {
if (feature.or) {
var children = feature.children.map(featureName());
return Logic.equiv(feature.name, Logic.or(children));
}
}
];
model.features.forEach(function(feature) {
featureConstraintSemantics.forEach(function(semantics) {
var formula = semantics(feature);
if (formula)
solver.require(formula);
});
});
getCrossTreeConstraints().forEach(function(constraint) {
solver.require(constraint);
});
}
ConstraintSolver.prototype.crossTreeConstraint = function(rule) {
var self = this, op = rule.tagName, num = $(rule).children().length;
function constrainedChild(n) {
return self.crossTreeConstraint($(rule).children()[n]);
}
if (op === "eq" && num === 2)
return Logic.equiv(constrainedChild(0), constrainedChild(1));
if (op === "imp" && num === 2)
return Logic.implies(constrainedChild(0), constrainedChild(1));
if (op === "conj" && num === 2)
return Logic.and(constrainedChild(0), constrainedChild(1));
if (op === "disj" && num === 2)
return Logic.or(constrainedChild(0), constrainedChild(1));
if (op === "not" && num === 1)
return Logic.not(constrainedChild(0));
if (op === "var" && num === 0)
return $(rule).text();
throw "unknown operation " + op + " with " + num + " arguments encountered";
};
ConstraintSolver.prototype.configurationConstraint = function(configuration, excludeFeature) {
function not(excludeFeature) {
return function(feature) {
return !excludeFeature || excludeFeature.name !== feature.name;
};
}
return Logic.and(configuration.selectedFeatures.filter(not(excludeFeature)).map(featureName()),
configuration.deselectedFeatures.filter(not(excludeFeature)).map(featureName()).map(function(feature) {
return Logic.not(feature);
}));
};
ConstraintSolver.prototype.isValid = function(configuration) {
return !!this.solver.solveAssuming(this.configurationConstraint(configuration));
};
ConstraintSolver.prototype.isDeactivated = function(configuration, feature) {
return !this.solver.solveAssuming(Logic.and(this.configurationConstraint(configuration, feature), feature.name));
};
ConstraintSolver.prototype.isActivated = function(configuration, feature) {
return !this.solver.solveAssuming(Logic.and(this.configurationConstraint(configuration, feature), Logic.not(feature.name)));
};