1 /** 2 * @fileoverview An abstract class representing expressions with predicates. 3 * baseExprWithPredictes are immutable objects that evaluate their 4 * predicates against nodesets and return the modified nodesets. 5 * 6 */ 7 8 9 goog.provide('xrx.xpath.Predicates'); 10 11 goog.require('goog.array'); 12 goog.require('xrx.xpath.Context'); 13 goog.require('xrx.xpath.Expr'); 14 15 16 17 /** 18 * An abstract class for expressions with predicates. 19 * 20 * @constructor 21 * @param {!Array.<!xrx.xpath.Expr>} predicates The array of predicates. 22 * @param {boolean=} opt_reverse Whether to iterate over the nodeset in reverse. 23 */ 24 xrx.xpath.Predicates = function(predicates, opt_reverse) { 25 26 /** 27 * List of predicates 28 * 29 * @private 30 * @type {!Array.<!xrx.xpath.Expr>} 31 */ 32 this.predicates_ = predicates; 33 34 35 /** 36 * Which direction to iterate over the predicates 37 * 38 * @private 39 * @type {boolean} 40 */ 41 this.reverse_ = !!opt_reverse; 42 }; 43 44 45 /** 46 * Evaluates the predicates against the given nodeset. 47 * 48 * @param {!xrx.xpath.NodeSet} nodeset The nodes against which to evaluate 49 * the predicates. 50 * @param {number=} opt_start The index of the first predicate to evaluate, 51 * defaults to 0. 52 * @return {!xrx.xpath.NodeSet} nodeset The filtered nodeset. 53 */ 54 xrx.xpath.Predicates.prototype.evaluatePredicates = 55 function(nodeset, opt_start) { 56 for (var i = opt_start || 0; i < this.predicates_.length; i++) { 57 var predicate = this.predicates_[i]; 58 var iter = nodeset.iterator(); 59 var l = nodeset.getLength(); 60 var node; 61 for (var j = 0; node = iter.next(); j++) { 62 var position = this.reverse_ ? (l - j) : (j + 1); 63 var exrs = predicate.evaluate(new 64 xrx.xpath.Context(/** @type {xrx.node} */ (node), position, l)); 65 var keep; 66 if (typeof exrs == 'number') { 67 keep = (position == exrs); 68 } else if (typeof exrs == 'string' || typeof exrs == 'boolean') { 69 keep = !!exrs; 70 } else if (exrs instanceof xrx.xpath.NodeSet) { 71 keep = (exrs.getLength() > 0); 72 } else { 73 throw Error('Predicate.evaluate returned an unexpected type.'); 74 } 75 if (!keep) { 76 iter.remove(); 77 } 78 } 79 } 80 return nodeset; 81 }; 82 83 84 /** 85 * Returns the quickAttr info. 86 * 87 * @return {?{name: string, valueExpr: xrx.xpath.Expr}} 88 */ 89 xrx.xpath.Predicates.prototype.getQuickAttr = function() { 90 return this.predicates_.length > 0 ? 91 this.predicates_[0].getQuickAttr() : null; 92 }; 93 94 95 /** 96 * Returns whether this set of predicates needs context position. 97 * 98 * @return {boolean} Whether something needs context position. 99 */ 100 xrx.xpath.Predicates.prototype.doesNeedContextPosition = function() { 101 for (var i = 0; i < this.predicates_.length; i++) { 102 var predicate = this.predicates_[i]; 103 if (predicate.doesNeedContextPosition() || 104 predicate.getDataType() == xrx.xpath.DataType.NUMBER || 105 predicate.getDataType() == xrx.xpath.DataType.VOID) { 106 return true; 107 } 108 } 109 return false; 110 }; 111 112 113 /** 114 * Returns the length of this set of predicates. 115 * 116 * @return {number} The number of expressions. 117 */ 118 xrx.xpath.Predicates.prototype.getLength = function() { 119 return this.predicates_.length; 120 }; 121 122 123 /** 124 * Returns the set of predicates. 125 * 126 * @return {!Array.<!xrx.xpath.Expr>} The predicates. 127 */ 128 xrx.xpath.Predicates.prototype.getPredicates = function() { 129 return this.predicates_; 130 }; 131 132 133 /** 134 * @override 135 */ 136 xrx.xpath.Predicates.prototype.toString = function() { 137 return goog.array.reduce(this.predicates_, function(prev, curr) { 138 return prev + xrx.xpath.Expr.indent(curr); 139 }, 'Predicates:'); 140 }; 141