1 // ========================================================================== 2 // Project: SC.Statechart - A Statechart Framework for SproutCore 3 // Copyright: ©2010, 2011 Michael Cohen, and contributors. 4 // Portions @2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 /*globals SC */ 9 10 SC.StatechartSequenceMatcher = SC.Object.extend({ 11 12 statechartMonitor: null, 13 14 match: null, 15 16 MISMATCH: {}, 17 18 begin: function() { 19 this._stack = []; 20 this.beginSequence(); 21 this._start = this._stack[0]; 22 return this; 23 }, 24 25 end: function() { 26 this.endSequence(); 27 28 if (this._stack.length > 0) { 29 throw new Error("can not match sequence. sequence matcher has been left in an invalid state"); 30 } 31 32 var monitor = this.statechartMonitor, 33 result = this._matchSequence(this._start, 0) === monitor.sequence.length; 34 35 this.set('match', result); 36 37 return result; 38 }, 39 40 entered: function() { 41 this._addStatesToCurrentGroup('entered', arguments); 42 return this; 43 }, 44 45 exited: function() { 46 this._addStatesToCurrentGroup('exited', arguments); 47 return this; 48 }, 49 50 beginConcurrent: function() { 51 var group = { 52 type: 'concurrent', 53 values: [] 54 }; 55 if (this._peek()) this._peek().values.push(group); 56 this._stack.push(group); 57 return this; 58 }, 59 60 endConcurrent: function() { 61 this._stack.pop(); 62 return this; 63 }, 64 65 beginSequence: function() { 66 var group = { 67 type: 'sequence', 68 values: [] 69 }; 70 if (this._peek()) this._peek().values.push(group); 71 this._stack.push(group); 72 return this; 73 }, 74 75 endSequence: function() { 76 this._stack.pop(); 77 return this; 78 }, 79 80 _peek: function() { 81 var len = this._stack.length; 82 return len === 0 ? null : this._stack[len - 1]; 83 }, 84 85 _addStatesToCurrentGroup: function(action, states) { 86 var group = this._peek(), len = states.length, i = 0; 87 for (; i < len; i += 1) { 88 group.values.push({ action: action, state: states[i] }); 89 } 90 }, 91 92 _matchSequence: function(sequence, marker) { 93 var values = sequence.values, 94 len = values.length, 95 i = 0, val, 96 monitor = this.statechartMonitor; 97 98 if (len === 0) return marker; 99 if (marker > monitor.sequence.length) return this.MISMATCH; 100 101 for (; i < len; i += 1) { 102 val = values[i]; 103 104 if (val.type === 'sequence') { 105 marker = this._matchSequence(val, marker); 106 } else if (val.type === 'concurrent') { 107 marker = this._matchConcurrent(val, marker); 108 } else if (!this._matchItems(val, monitor.sequence[marker])){ 109 return this.MISMATCH; 110 } else { 111 marker += 1; 112 } 113 114 if (marker === this.MISMATCH) return this.MISMATCH; 115 } 116 117 return marker; 118 }, 119 120 // A 121 // B (concurrent [X, Y]) 122 // X 123 // M 124 // N 125 // Y 126 // O 127 // P 128 // C 129 // 130 // 0 1 2 3 4 5 6 7 8 131 // ^ ^ 132 // A B (X M N) (Y O P) C 133 // ^ ^ 134 // A B (Y O P) (X M N) C 135 136 _matchConcurrent: function(concurrent, marker) { 137 var values = SC.clone(concurrent.values), 138 len = values.length, 139 i = 0, val, tempMarker = marker, match = false, 140 monitor = this.statechartMonitor; 141 142 if (len === 0) return marker; 143 if (marker > monitor.sequence.length) return this.MISMATCH; 144 145 while (values.length > 0) { 146 for (i = 0; i < len; i += 1) { 147 val = values[i]; 148 149 if (val.type === 'sequence') { 150 tempMarker = this._matchSequence(val, marker); 151 } else if (val.type === 'concurrent') { 152 tempMarker = this._matchConcurrent(val, marker); 153 } else if (!this._matchItems(val, monitor.sequence[marker])){ 154 tempMarker = this.MISMATCH; 155 } else { 156 tempMarker = marker + 1; 157 } 158 159 if (tempMarker !== this.MISMATCH) break; 160 } 161 162 if (tempMarker === this.MISMATCH) return this.MISMATCH; 163 values.removeAt(i); 164 len = values.length; 165 marker = tempMarker; 166 } 167 168 return marker; 169 }, 170 171 _matchItems: function(matcherItem, monitorItem) { 172 if (!matcherItem || !monitorItem) return false; 173 174 if (matcherItem.action !== monitorItem.action) { 175 return false; 176 } 177 178 if (SC.typeOf(matcherItem.state) === SC.T_OBJECT && matcherItem.state === monitorItem.state) { 179 return true; 180 } 181 182 if (matcherItem.state === monitorItem.state.get('name')) { 183 return true; 184 } 185 186 return false; 187 } 188 189 });