142 lines
4.8 KiB
JavaScript
142 lines
4.8 KiB
JavaScript
var Immutable = require('immutable');
|
|
var Trie = require('./trie.js');
|
|
var Patch = require('./patch.js');
|
|
var Util = require('./util.js');
|
|
|
|
function ensureMatchingProjectionNames(specs) {
|
|
if (!(specs.length > 0)) {
|
|
throw new Error("Syndicate: DemandMatcher needs at least one spec");
|
|
}
|
|
|
|
var names = null;
|
|
specs.forEach(function (spec) {
|
|
if (names === null) {
|
|
names = Trie.projectionNames(spec);
|
|
} else {
|
|
if (JSON.stringify(names) !== JSON.stringify(Trie.projectionNames(spec))) {
|
|
throw new Error("Syndicate: DemandMatcher needs identical capture names");
|
|
}
|
|
}
|
|
});
|
|
return names;
|
|
}
|
|
|
|
function defaultHandler(side, movement) {
|
|
return function (captures) {
|
|
console.error("Syndicate: Unhandled "+movement+" in "+side, captures);
|
|
};
|
|
}
|
|
|
|
function DemandMatcher(demandSpecs, supplySpecs, options) {
|
|
options = Util.extend({
|
|
metaLevel: 0,
|
|
demandMetaLevel: null,
|
|
supplyMetaLevel: null,
|
|
onDemandIncrease: defaultHandler('demand', 'increase'),
|
|
onDemandDecrease: function (captures) {},
|
|
onSupplyIncrease: function (captures) {},
|
|
onSupplyDecrease: defaultHandler('supply', 'decrease')
|
|
}, options);
|
|
|
|
this.demandProjectionNames = ensureMatchingProjectionNames(demandSpecs);
|
|
this.supplyProjectionNames = ensureMatchingProjectionNames(supplySpecs);
|
|
|
|
this.demandSpecs = demandSpecs;
|
|
this.supplySpecs = supplySpecs;
|
|
|
|
this.demandPatterns = demandSpecs.map(function (s) { return Trie.projectionToPattern(s); });
|
|
this.supplyPatterns = supplySpecs.map(function (s) { return Trie.projectionToPattern(s); });
|
|
|
|
this.demandMetaLevel =
|
|
(options.demandMetaLevel === null) ? options.metaLevel : options.demandMetaLevel;
|
|
this.supplyMetaLevel =
|
|
(options.supplyMetaLevel === null) ? options.metaLevel : options.supplyMetaLevel;
|
|
|
|
function metaWrap(n) {
|
|
return function (s) { return Patch.prependAtMeta(s, n); };
|
|
}
|
|
this.demandProjections = demandSpecs.map(metaWrap(this.demandMetaLevel));
|
|
this.supplyProjections = supplySpecs.map(metaWrap(this.supplyMetaLevel));
|
|
|
|
this.onDemandIncrease = options.onDemandIncrease;
|
|
this.onDemandDecrease = options.onDemandDecrease;
|
|
this.onSupplyIncrease = options.onSupplyIncrease;
|
|
this.onSupplyDecrease = options.onSupplyDecrease;
|
|
|
|
this.currentDemand = Immutable.Set();
|
|
this.currentSupply = Immutable.Set();
|
|
}
|
|
|
|
DemandMatcher.prototype.boot = function () {
|
|
var p = Patch.emptyPatch;
|
|
function extend(ml) {
|
|
return function (pat) { p = p.andThen(Patch.sub(pat, ml)); };
|
|
}
|
|
this.demandPatterns.forEach(extend(this.demandMetaLevel));
|
|
this.supplyPatterns.forEach(extend(this.supplyMetaLevel));
|
|
return p;
|
|
};
|
|
|
|
DemandMatcher.prototype.handleEvent = function (e) {
|
|
if (e.type === "stateChange") {
|
|
this.handlePatch(e.patch);
|
|
}
|
|
};
|
|
|
|
DemandMatcher.prototype.extractKeys = function (trie, projections, keyCount, whichSide) {
|
|
var ks = Immutable.Set();
|
|
projections.forEach(function (proj) {
|
|
var moreKs = Trie.trieKeys(Trie.project(trie, proj), keyCount);
|
|
if (!moreKs) {
|
|
throw new Error("Syndicate: wildcard "+whichSide+" detected:\n" +
|
|
JSON.stringify(proj) + "\n" +
|
|
Trie.prettyTrie(trie));
|
|
}
|
|
ks = ks.union(moreKs);
|
|
});
|
|
return ks;
|
|
};
|
|
|
|
DemandMatcher.prototype.handlePatch = function (p) {
|
|
var self = this;
|
|
|
|
var dN = self.demandProjectionNames.length;
|
|
var sN = self.supplyProjectionNames.length;
|
|
var addedDemand = this.extractKeys(p.added, self.demandProjections, dN, 'demand');
|
|
var removedDemand = this.extractKeys(p.removed, self.demandProjections, dN, 'demand');
|
|
var addedSupply = this.extractKeys(p.added, self.supplyProjections, sN, 'supply');
|
|
var removedSupply = this.extractKeys(p.removed, self.supplyProjections, sN, 'supply');
|
|
|
|
self.currentSupply = self.currentSupply.union(addedSupply);
|
|
self.currentDemand = self.currentDemand.subtract(removedDemand);
|
|
|
|
removedSupply.forEach(function (captures) {
|
|
if (self.currentDemand.has(captures)) {
|
|
self.onSupplyDecrease(Trie.captureToObject(captures, self.supplyProjectionNames));
|
|
}
|
|
});
|
|
addedSupply.forEach(function (captures) {
|
|
if (!self.currentDemand.has(captures)) {
|
|
self.onSupplyIncrease(Trie.captureToObject(captures, self.supplyProjectionNames));
|
|
}
|
|
});
|
|
|
|
removedDemand.forEach(function (captures) {
|
|
if (self.currentSupply.has(captures)) {
|
|
self.onDemandDecrease(Trie.captureToObject(captures, self.demandProjectionNames));
|
|
}
|
|
});
|
|
addedDemand.forEach(function (captures) {
|
|
if (!self.currentSupply.has(captures)) {
|
|
self.onDemandIncrease(Trie.captureToObject(captures, self.demandProjectionNames));
|
|
}
|
|
});
|
|
|
|
self.currentSupply = self.currentSupply.subtract(removedSupply);
|
|
self.currentDemand = self.currentDemand.union(addedDemand);
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
module.exports.DemandMatcher = DemandMatcher;
|