OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Base class for all speech rule stores. | 6 * @fileoverview Base class for all speech rule stores. |
7 * | 7 * |
8 * The base rule store implements some basic functionality that is common to | 8 * The base rule store implements some basic functionality that is common to |
9 * most speech rule stores. | 9 * most speech rule stores. |
10 */ | 10 */ |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 | 64 |
65 | 65 |
66 /** | 66 /** |
67 * @override | 67 * @override |
68 */ | 68 */ |
69 cvox.BaseRuleStore.prototype.lookupRule = function(node, dynamic) { | 69 cvox.BaseRuleStore.prototype.lookupRule = function(node, dynamic) { |
70 if (!node || | 70 if (!node || |
71 (node.nodeType != Node.ELEMENT_NODE && node.nodeType != Node.TEXT_NODE)) { | 71 (node.nodeType != Node.ELEMENT_NODE && node.nodeType != Node.TEXT_NODE)) { |
72 return null; | 72 return null; |
73 } | 73 } |
74 var matchingRules = this.speechRules_.filter( | 74 var matchingRules = this.speechRules_.filter(goog.bind(function(rule) { |
75 goog.bind( | 75 return this.testDynamicConstraints(dynamic, rule) && |
76 function(rule) { | 76 this.testPrecondition_(/** @type {!Node} */ (node), rule); |
77 return this.testDynamicConstraints(dynamic, rule) && | 77 }, this)); |
78 this.testPrecondition_(/** @type {!Node} */ (node), rule);}, | |
79 this)); | |
80 return (matchingRules.length > 0) ? | 78 return (matchingRules.length > 0) ? |
81 this.pickMostConstraint_(dynamic, matchingRules) : null; | 79 this.pickMostConstraint_(dynamic, matchingRules) : |
| 80 null; |
82 }; | 81 }; |
83 | 82 |
84 | 83 |
85 /** | 84 /** |
86 * @override | 85 * @override |
87 */ | 86 */ |
88 cvox.BaseRuleStore.prototype.defineRule = function( | 87 cvox.BaseRuleStore.prototype.defineRule = function( |
89 name, dynamic, action, prec, cstr) { | 88 name, dynamic, action, prec, cstr) { |
90 try { | 89 try { |
91 var postc = cvox.SpeechRule.Action.fromString(action); | 90 var postc = cvox.SpeechRule.Action.fromString(action); |
92 var cstrList = Array.prototype.slice.call(arguments, 4); | 91 var cstrList = Array.prototype.slice.call(arguments, 4); |
93 var fullPrec = new cvox.SpeechRule.Precondition(prec, cstrList); | 92 var fullPrec = new cvox.SpeechRule.Precondition(prec, cstrList); |
94 var dynamicCstr = {}; | 93 var dynamicCstr = {}; |
95 dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE] = dynamic; | 94 dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE] = dynamic; |
96 var rule = new cvox.SpeechRule(name, dynamicCstr, fullPrec, postc); | 95 var rule = new cvox.SpeechRule(name, dynamicCstr, fullPrec, postc); |
97 } catch (err) { | 96 } catch (err) { |
98 if (err.name == 'RuleError') { | 97 if (err.name == 'RuleError') { |
99 console.log('Rule Error ', prec, '(' + dynamic + '):', err.message); | 98 console.log('Rule Error ', prec, '(' + dynamic + '):', err.message); |
100 return null; | 99 return null; |
101 } | 100 } else { |
102 else { | |
103 throw err; | 101 throw err; |
104 } | 102 } |
105 } | 103 } |
106 this.addRule(rule); | 104 this.addRule(rule); |
107 return rule; | 105 return rule; |
108 }; | 106 }; |
109 | 107 |
110 | 108 |
111 /** | 109 /** |
112 * @override | 110 * @override |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 | 173 |
176 | 174 |
177 /** | 175 /** |
178 * Removes duplicates of the given rule from the rule store. Thereby duplicates | 176 * Removes duplicates of the given rule from the rule store. Thereby duplicates |
179 * are identified by having the same precondition and dynamic constraint. | 177 * are identified by having the same precondition and dynamic constraint. |
180 * @param {cvox.SpeechRule} rule The rule. | 178 * @param {cvox.SpeechRule} rule The rule. |
181 */ | 179 */ |
182 cvox.BaseRuleStore.prototype.removeDuplicates = function(rule) { | 180 cvox.BaseRuleStore.prototype.removeDuplicates = function(rule) { |
183 for (var i = this.speechRules_.length - 1, oldRule; | 181 for (var i = this.speechRules_.length - 1, oldRule; |
184 oldRule = this.speechRules_[i]; i--) { | 182 oldRule = this.speechRules_[i]; i--) { |
185 if (oldRule != rule && | 183 if (oldRule != rule && |
186 cvox.BaseRuleStore.compareDynamicConstraints_( | 184 cvox.BaseRuleStore.compareDynamicConstraints_( |
187 oldRule.dynamicCstr, rule.dynamicCstr) && | 185 oldRule.dynamicCstr, rule.dynamicCstr) && |
188 cvox.BaseRuleStore.comparePreconditions_(oldRule, rule)) { | 186 cvox.BaseRuleStore.comparePreconditions_(oldRule, rule)) { |
189 this.speechRules_.splice(i, 1); | 187 this.speechRules_.splice(i, 1); |
190 } | 188 } |
191 } | 189 } |
192 }; | 190 }; |
193 | 191 |
194 | 192 |
195 // TODO (sorge) These should move into the speech rule functions. | 193 // TODO (sorge) These should move into the speech rule functions. |
196 /** | 194 /** |
197 * Checks if we have a custom query and applies it. Otherwise returns null. | 195 * Checks if we have a custom query and applies it. Otherwise returns null. |
198 * @param {!Node} node The initial node. | 196 * @param {!Node} node The initial node. |
199 * @param {string} funcName A function name. | 197 * @param {string} funcName A function name. |
200 * @return {Array<Node>} The list of resulting nodes. | 198 * @return {Array<Node>} The list of resulting nodes. |
201 */ | 199 */ |
202 cvox.BaseRuleStore.prototype.applyCustomQuery = function( | 200 cvox.BaseRuleStore.prototype.applyCustomQuery = function(node, funcName) { |
203 node, funcName) { | |
204 var func = this.customQueries.lookup(funcName); | 201 var func = this.customQueries.lookup(funcName); |
205 return func ? func(node) : null; | 202 return func ? func(node) : null; |
206 }; | 203 }; |
207 | 204 |
208 | 205 |
209 /** | 206 /** |
210 * Applies either an Xpath selector or a custom query to the node | 207 * Applies either an Xpath selector or a custom query to the node |
211 * and returns the resulting node list. | 208 * and returns the resulting node list. |
212 * @param {!Node} node The initial node. | 209 * @param {!Node} node The initial node. |
213 * @param {string} expr An Xpath expression string or a name of a custom | 210 * @param {string} expr An Xpath expression string or a name of a custom |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 }; | 248 }; |
252 | 249 |
253 | 250 |
254 /** | 251 /** |
255 * Tests whether a speech rule satisfies a set of dynamic constraints. | 252 * Tests whether a speech rule satisfies a set of dynamic constraints. |
256 * @param {!cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints. | 253 * @param {!cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints. |
257 * @param {cvox.SpeechRule} rule The rule. | 254 * @param {cvox.SpeechRule} rule The rule. |
258 * @return {boolean} True if the preconditions apply to the node. | 255 * @return {boolean} True if the preconditions apply to the node. |
259 * @protected | 256 * @protected |
260 */ | 257 */ |
261 cvox.BaseRuleStore.prototype.testDynamicConstraints = function( | 258 cvox.BaseRuleStore.prototype.testDynamicConstraints = function(dynamic, rule) { |
262 dynamic, rule) { | |
263 // We allow a default value for each dynamic constraints attribute. | 259 // We allow a default value for each dynamic constraints attribute. |
264 // The idea is that when we can not find a speech rule matching the value for | 260 // The idea is that when we can not find a speech rule matching the value for |
265 // a particular attribute in the dynamic constraintwe choose the one that has | 261 // a particular attribute in the dynamic constraintwe choose the one that has |
266 // the value 'default'. | 262 // the value 'default'. |
267 var allKeys = /** @type {Array<cvox.SpeechRule.DynamicCstrAttrib>} */ ( | 263 var allKeys = /** @type {Array<cvox.SpeechRule.DynamicCstrAttrib>} */ ( |
268 Object.keys(dynamic)); | 264 Object.keys(dynamic)); |
269 return allKeys.every( | 265 return allKeys.every(function(key) { |
270 function(key) { | 266 return dynamic[key] == rule.dynamicCstr[key] || |
271 return dynamic[key] == rule.dynamicCstr[key] || | 267 rule.dynamicCstr[key] == 'default'; |
272 rule.dynamicCstr[key] == 'default'; | 268 }); |
273 }); | |
274 }; | 269 }; |
275 | 270 |
276 | 271 |
277 /** | 272 /** |
278 * Get a set of all dynamic constraint values. | 273 * Get a set of all dynamic constraint values. |
279 * @return {!Object<cvox.SpeechRule.DynamicCstrAttrib, Array<string>>} The | 274 * @return {!Object<cvox.SpeechRule.DynamicCstrAttrib, Array<string>>} The |
280 * object with all annotations. | 275 * object with all annotations. |
281 */ | 276 */ |
282 cvox.BaseRuleStore.prototype.getDynamicConstraintValues = function() { | 277 cvox.BaseRuleStore.prototype.getDynamicConstraintValues = function() { |
283 var result = {}; | 278 var result = {}; |
(...skipping 18 matching lines...) Expand all Loading... |
302 * @param {cvox.SpeechRule} rule The speech rule to match. | 297 * @param {cvox.SpeechRule} rule The speech rule to match. |
303 * @return {number} The number of matching dynamic constraint values. | 298 * @return {number} The number of matching dynamic constraint values. |
304 * @private | 299 * @private |
305 */ | 300 */ |
306 cvox.BaseRuleStore.prototype.countMatchingDynamicConstraintValues_ = function( | 301 cvox.BaseRuleStore.prototype.countMatchingDynamicConstraintValues_ = function( |
307 dynamic, rule) { | 302 dynamic, rule) { |
308 var result = 0; | 303 var result = 0; |
309 for (var i = 0, key; key = this.dynamicCstrAttribs[i]; i++) { | 304 for (var i = 0, key; key = this.dynamicCstrAttribs[i]; i++) { |
310 if (dynamic[key] == rule.dynamicCstr[key]) { | 305 if (dynamic[key] == rule.dynamicCstr[key]) { |
311 result++; | 306 result++; |
312 } else break; | 307 } else |
| 308 break; |
313 } | 309 } |
314 return result; | 310 return result; |
315 }; | 311 }; |
316 | 312 |
317 | 313 |
318 /** | 314 /** |
319 * Picks the result of the most constraint rule by prefering those: | 315 * Picks the result of the most constraint rule by prefering those: |
320 * 1) that best match the dynamic constraints. | 316 * 1) that best match the dynamic constraints. |
321 * 2) with the most additional constraints. | 317 * 2) with the most additional constraints. |
322 * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints. | 318 * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints. |
323 * @param {!Array<cvox.SpeechRule>} rules An array of rules. | 319 * @param {!Array<cvox.SpeechRule>} rules An array of rules. |
324 * @return {cvox.SpeechRule} The most constraint rule. | 320 * @return {cvox.SpeechRule} The most constraint rule. |
325 * @private | 321 * @private |
326 */ | 322 */ |
327 cvox.BaseRuleStore.prototype.pickMostConstraint_ = function(dynamic, rules) { | 323 cvox.BaseRuleStore.prototype.pickMostConstraint_ = function(dynamic, rules) { |
328 rules.sort(goog.bind( | 324 rules.sort(goog.bind(function(r1, r2) { |
329 function(r1, r2) { | 325 var count1 = this.countMatchingDynamicConstraintValues_(dynamic, r1); |
330 var count1 = this.countMatchingDynamicConstraintValues_(dynamic, r1); | 326 var count2 = this.countMatchingDynamicConstraintValues_(dynamic, r2); |
331 var count2 = this.countMatchingDynamicConstraintValues_(dynamic, r2); | 327 // Rule one is a better match, don't swap. |
332 // Rule one is a better match, don't swap. | 328 if (count1 > count2) { |
333 if (count1 > count2) { | 329 return -1; |
334 return -1; | 330 } |
335 } | 331 // Rule two is a better match, swap. |
336 // Rule two is a better match, swap. | 332 if (count2 > count1) { |
337 if (count2 > count1) { | 333 return 1; |
338 return 1; | 334 } |
339 } | 335 // When same number of dynamic constraint attributes matches for |
340 // When same number of dynamic constraint attributes matches for | 336 // both rules, compare length of static constraints. |
341 // both rules, compare length of static constraints. | 337 return ( |
342 return (r2.precondition.constraints.length - | 338 r2.precondition.constraints.length - |
343 r1.precondition.constraints.length);}, | 339 r1.precondition.constraints.length); |
344 this)); | 340 }, this)); |
345 return rules[0]; | 341 return rules[0]; |
346 }; | 342 }; |
347 | 343 |
348 | 344 |
349 /** | 345 /** |
350 * Test the precondition of a speech rule. | 346 * Test the precondition of a speech rule. |
351 * @param {!Node} node on which to test applicability of the rule. | 347 * @param {!Node} node on which to test applicability of the rule. |
352 * @param {cvox.SpeechRule} rule The rule to be tested. | 348 * @param {cvox.SpeechRule} rule The rule to be tested. |
353 * @return {boolean} True if the preconditions apply to the node. | 349 * @return {boolean} True if the preconditions apply to the node. |
354 * @private | 350 * @private |
355 */ | 351 */ |
356 cvox.BaseRuleStore.prototype.testPrecondition_ = function(node, rule) { | 352 cvox.BaseRuleStore.prototype.testPrecondition_ = function(node, rule) { |
357 var prec = rule.precondition; | 353 var prec = rule.precondition; |
358 return this.applyQuery(node, prec.query) === node && | 354 return this.applyQuery(node, prec.query) === node && |
359 prec.constraints.every( | 355 prec.constraints.every(goog.bind(function(cstr) { |
360 goog.bind(function(cstr) { | 356 return this.applyConstraint(node, cstr); |
361 return this.applyConstraint(node, cstr);}, | 357 }, this)); |
362 this)); | |
363 }; | 358 }; |
364 | 359 |
365 | 360 |
366 // TODO (sorge) Define the following methods directly on the dynamic constraint | 361 // TODO (sorge) Define the following methods directly on the dynamic constraint |
367 // and precondition classes, respectively. | 362 // and precondition classes, respectively. |
368 /** | 363 /** |
369 * Compares two dynamic constraints and returns true if they are equal. | 364 * Compares two dynamic constraints and returns true if they are equal. |
370 * @param {cvox.SpeechRule.DynamicCstr} cstr1 First dynamic constraints. | 365 * @param {cvox.SpeechRule.DynamicCstr} cstr1 First dynamic constraints. |
371 * @param {cvox.SpeechRule.DynamicCstr} cstr2 Second dynamic constraints. | 366 * @param {cvox.SpeechRule.DynamicCstr} cstr2 Second dynamic constraints. |
372 * @return {boolean} True if the dynamic constraints are equal. | 367 * @return {boolean} True if the dynamic constraints are equal. |
373 * @private | 368 * @private |
374 */ | 369 */ |
375 cvox.BaseRuleStore.compareDynamicConstraints_ = function( | 370 cvox.BaseRuleStore.compareDynamicConstraints_ = function(cstr1, cstr2) { |
376 cstr1, cstr2) { | |
377 if (Object.keys(cstr1).length != Object.keys(cstr2).length) { | 371 if (Object.keys(cstr1).length != Object.keys(cstr2).length) { |
378 return false; | 372 return false; |
379 } | 373 } |
380 for (var key in cstr1) { | 374 for (var key in cstr1) { |
381 if (!cstr2[key] || cstr1[key] !== cstr2[key]) { | 375 if (!cstr2[key] || cstr1[key] !== cstr2[key]) { |
382 return false; | 376 return false; |
383 } | 377 } |
384 } | 378 } |
385 return true; | 379 return true; |
386 }; | 380 }; |
387 | 381 |
388 | 382 |
389 /** | 383 /** |
390 * Compares two static constraints (i.e., lists of precondition constraints) and | 384 * Compares two static constraints (i.e., lists of precondition constraints) and |
391 * returns true if they are equal. | 385 * returns true if they are equal. |
392 * @param {Array<string>} cstr1 First static constraints. | 386 * @param {Array<string>} cstr1 First static constraints. |
393 * @param {Array<string>} cstr2 Second static constraints. | 387 * @param {Array<string>} cstr2 Second static constraints. |
394 * @return {boolean} True if the static constraints are equal. | 388 * @return {boolean} True if the static constraints are equal. |
395 * @private | 389 * @private |
396 */ | 390 */ |
397 cvox.BaseRuleStore.compareStaticConstraints_ = function( | 391 cvox.BaseRuleStore.compareStaticConstraints_ = function(cstr1, cstr2) { |
398 cstr1, cstr2) { | |
399 if (cstr1.length != cstr2.length) { | 392 if (cstr1.length != cstr2.length) { |
400 return false; | 393 return false; |
401 } | 394 } |
402 for (var i = 0, cstr; cstr = cstr1[i]; i++) { | 395 for (var i = 0, cstr; cstr = cstr1[i]; i++) { |
403 if (cstr2.indexOf(cstr) == -1) { | 396 if (cstr2.indexOf(cstr) == -1) { |
404 return false; | 397 return false; |
405 } | 398 } |
406 } | 399 } |
407 return true; | 400 return true; |
408 }; | 401 }; |
409 | 402 |
410 | 403 |
411 /** | 404 /** |
412 * Compares the preconditions of two speech rules. | 405 * Compares the preconditions of two speech rules. |
413 * @param {cvox.SpeechRule} rule1 The first speech rule. | 406 * @param {cvox.SpeechRule} rule1 The first speech rule. |
414 * @param {cvox.SpeechRule} rule2 The second speech rule. | 407 * @param {cvox.SpeechRule} rule2 The second speech rule. |
415 * @return {boolean} True if the preconditions are equal. | 408 * @return {boolean} True if the preconditions are equal. |
416 * @private | 409 * @private |
417 */ | 410 */ |
418 cvox.BaseRuleStore.comparePreconditions_ = function(rule1, rule2) { | 411 cvox.BaseRuleStore.comparePreconditions_ = function(rule1, rule2) { |
419 var prec1 = rule1.precondition; | 412 var prec1 = rule1.precondition; |
420 var prec2 = rule2.precondition; | 413 var prec2 = rule2.precondition; |
421 if (prec1.query != prec2.query) { | 414 if (prec1.query != prec2.query) { |
422 return false; | 415 return false; |
423 } | 416 } |
424 return cvox.BaseRuleStore.compareStaticConstraints_( | 417 return cvox.BaseRuleStore.compareStaticConstraints_( |
425 prec1.constraints, prec2.constraints); | 418 prec1.constraints, prec2.constraints); |
426 }; | 419 }; |
OLD | NEW |