blossom
Version:
Modern, Cross-Platform Application Framework
189 lines (150 loc) • 4.68 kB
JavaScript
// ==========================================================================
// Project: SC.Statechart - A Statechart Framework for SproutCore
// Copyright: ©2010, 2011 Michael Cohen, and contributors.
// Portions @2011 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
/*globals SC */
SC.StatechartSequenceMatcher = SC.Object.extend({
statechartMonitor: null,
match: null,
MISMATCH: {},
begin: function() {
this._stack = [];
this.beginSequence();
this._start = this._stack[0];
return this;
},
end: function() {
this.endSequence();
if (this._stack.length > 0) {
throw "can not match sequence. sequence matcher has been left in an invalid state";
}
var monitor = this.statechartMonitor,
result = this._matchSequence(this._start, 0) === monitor.sequence.length;
this.set('match', result);
return result;
},
entered: function() {
this._addStatesToCurrentGroup('entered', arguments);
return this;
},
exited: function() {
this._addStatesToCurrentGroup('exited', arguments);
return this;
},
beginConcurrent: function() {
var group = {
type: 'concurrent',
values: []
};
if (this._peek()) this._peek().values.push(group);
this._stack.push(group);
return this;
},
endConcurrent: function() {
this._stack.pop();
return this;
},
beginSequence: function() {
var group = {
type: 'sequence',
values: []
};
if (this._peek()) this._peek().values.push(group);
this._stack.push(group);
return this;
},
endSequence: function() {
this._stack.pop();
return this;
},
_peek: function() {
var len = this._stack.length;
return len === 0 ? null : this._stack[len - 1];
},
_addStatesToCurrentGroup: function(action, states) {
var group = this._peek(), len = states.length, i = 0;
for (; i < len; i += 1) {
group.values.push({ action: action, state: states[i] });
}
},
_matchSequence: function(sequence, marker) {
var values = sequence.values,
len = values.length,
i = 0, val,
monitor = this.statechartMonitor;
if (len === 0) return marker;
if (marker > monitor.sequence.length) return this.MISMATCH;
for (; i < len; i += 1) {
val = values[i];
if (val.type === 'sequence') {
marker = this._matchSequence(val, marker);
} else if (val.type === 'concurrent') {
marker = this._matchConcurrent(val, marker);
} else if (!this._matchItems(val, monitor.sequence[marker])){
return this.MISMATCH;
} else {
marker += 1;
}
if (marker === this.MISMATCH) return this.MISMATCH;
}
return marker;
},
// A
// B (concurrent [X, Y])
// X
// M
// N
// Y
// O
// P
// C
//
// 0 1 2 3 4 5 6 7 8
// ^ ^
// A B (X M N) (Y O P) C
// ^ ^
// A B (Y O P) (X M N) C
_matchConcurrent: function(concurrent, marker) {
var values = SC.clone(concurrent.values),
len = values.length,
i = 0, val, tempMarker = marker, match = false,
monitor = this.statechartMonitor;
if (len === 0) return marker;
if (marker > monitor.sequence.length) return this.MISMATCH;
while (values.length > 0) {
for (i = 0; i < len; i += 1) {
val = values[i];
if (val.type === 'sequence') {
tempMarker = this._matchSequence(val, marker);
} else if (val.type === 'concurrent') {
tempMarker = this._matchConcurrent(val, marker);
} else if (!this._matchItems(val, monitor.sequence[marker])){
tempMarker = this.MISMATCH;
} else {
tempMarker = marker + 1;
}
if (tempMarker !== this.MISMATCH) break;
}
if (tempMarker === this.MISMATCH) return this.MISMATCH;
values.removeAt(i);
len = values.length;
marker = tempMarker;
}
return marker;
},
_matchItems: function(matcherItem, monitorItem) {
if (!matcherItem || !monitorItem) return false;
if (matcherItem.action !== monitorItem.action) {
return false;
}
if (SC.typeOf(matcherItem.state) === SC.T_OBJECT && matcherItem.state === monitorItem.state) {
return true;
}
if (matcherItem.state === monitorItem.state.get('name')) {
return true;
}
return false;
}
});