matcher.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview DOM pattern matcher. Allows for simple searching of DOM
  16. * using patterns descended from {@link goog.dom.pattern.AbstractPattern}.
  17. *
  18. * @author robbyw@google.com (Robby Walker)
  19. */
  20. goog.provide('goog.dom.pattern.Matcher');
  21. goog.require('goog.dom.TagIterator');
  22. goog.require('goog.dom.pattern.MatchType');
  23. goog.require('goog.iter');
  24. // TODO(robbyw): Allow for backtracks of size > 1.
  25. /**
  26. * Given a set of patterns and a root node, this class tests the patterns in
  27. * parallel.
  28. *
  29. * It is not (yet) a smart matcher - it doesn't do any advanced backtracking.
  30. * Given the pattern <code>DIV, SPAN</code> the matcher will not match
  31. * <code>DIV, DIV, SPAN</code> because it starts matching at the first
  32. * <code>DIV</code>, fails to match <code>SPAN</code> at the second, and never
  33. * backtracks to try again.
  34. *
  35. * It is also possible to have a set of complex patterns that when matched in
  36. * parallel will miss some possible matches. Running multiple times will catch
  37. * all matches eventually.
  38. *
  39. * @constructor
  40. * @final
  41. */
  42. goog.dom.pattern.Matcher = function() {
  43. /**
  44. * Array of patterns to attempt to match in parallel.
  45. *
  46. * @private {Array<goog.dom.pattern.AbstractPattern>}
  47. */
  48. this.patterns_ = [];
  49. /**
  50. * Array of callbacks to call when a pattern is matched. The indexing is the
  51. * same as the {@link #patterns_} array.
  52. *
  53. * @private {Array<Function>}
  54. */
  55. this.callbacks_ = [];
  56. };
  57. /**
  58. * Adds a pattern to be matched. The callback can return an object whose keys
  59. * are processing instructions.
  60. *
  61. * @param {goog.dom.pattern.AbstractPattern} pattern The pattern to add.
  62. * @param {Function} callback Function to call when a match is found. Uses
  63. * the above semantics.
  64. */
  65. goog.dom.pattern.Matcher.prototype.addPattern = function(pattern, callback) {
  66. this.patterns_.push(pattern);
  67. this.callbacks_.push(callback);
  68. };
  69. /**
  70. * Resets all the patterns.
  71. *
  72. * @private
  73. */
  74. goog.dom.pattern.Matcher.prototype.reset_ = function() {
  75. for (var i = 0, len = this.patterns_.length; i < len; i++) {
  76. this.patterns_[i].reset();
  77. }
  78. };
  79. /**
  80. * Test the given node against all patterns.
  81. *
  82. * @param {goog.dom.TagIterator} position A position in a node walk that is
  83. * located at the token to process.
  84. * @return {boolean} Whether a pattern modified the position or tree
  85. * and its callback resulted in DOM structure or position modification.
  86. * @private
  87. */
  88. goog.dom.pattern.Matcher.prototype.matchToken_ = function(position) {
  89. for (var i = 0, len = this.patterns_.length; i < len; i++) {
  90. var pattern = this.patterns_[i];
  91. switch (pattern.matchToken(position.node, position.tagType)) {
  92. case goog.dom.pattern.MatchType.MATCH:
  93. case goog.dom.pattern.MatchType.BACKTRACK_MATCH:
  94. var callback = this.callbacks_[i];
  95. // Callbacks are allowed to modify the current position, but must
  96. // return true if the do.
  97. if (callback(pattern.matchedNode, position, pattern)) {
  98. return true;
  99. }
  100. default:
  101. // Do nothing.
  102. break;
  103. }
  104. }
  105. return false;
  106. };
  107. /**
  108. * Match the set of patterns against a match tree.
  109. *
  110. * @param {Node} node The root node of the tree to match.
  111. */
  112. goog.dom.pattern.Matcher.prototype.match = function(node) {
  113. var position = new goog.dom.TagIterator(node);
  114. this.reset_();
  115. goog.iter.forEach(position, function() {
  116. while (this.matchToken_(position)) {
  117. // Since we've moved, our old pattern statuses don't make sense any more.
  118. // Reset them.
  119. this.reset_();
  120. }
  121. }, this);
  122. };