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