blob: 0246d8d2bc6e2a5d9e9879066b8a815c0a16e0b5 [file] [log] [blame] [edit]
/**
* @license
* The MIT License
*
* Copyright (c) 2007 Cybozu Labs, Inc.
* Copyright (c) 2012 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/**
* @fileoverview An class representing operations on path expressions.
* @author [email protected] (Michael Zhou)
*/
goog.provide('wgxpath.PathExpr');
goog.require('goog.array');
goog.require('goog.dom.NodeType');
goog.require('wgxpath.Context');
goog.require('wgxpath.DataType');
goog.require('wgxpath.Expr');
goog.require('wgxpath.NodeSet');
/**
* Constructor for PathExpr.
*
* @param {!wgxpath.Expr} filter A filter expression.
* @param {!Array.<!wgxpath.Step>} steps The steps in the location path.
* @extends {wgxpath.Expr}
* @constructor
*/
wgxpath.PathExpr = function(filter, steps) {
wgxpath.Expr.call(this, filter.getDataType());
/**
* @type {!wgxpath.Expr}
* @private
*/
this.filter_ = filter;
/**
* @type {!Array.<!wgxpath.Step>}
* @private
*/
this.steps_ = steps;
this.setNeedContextPosition(filter.doesNeedContextPosition());
this.setNeedContextNode(filter.doesNeedContextNode());
if (this.steps_.length == 1) {
var firstStep = this.steps_[0];
if (!firstStep.doesIncludeDescendants() &&
firstStep.getAxis() == wgxpath.Step.Axis.ATTRIBUTE) {
var test = firstStep.getTest();
if (test.getName() != '*') {
this.setQuickAttr({
name: test.getName(),
valueExpr: null
});
}
}
}
};
goog.inherits(wgxpath.PathExpr, wgxpath.Expr);
/**
* Constructor for RootHelperExpr.
*
* @extends {wgxpath.Expr}
* @constructor
*/
wgxpath.PathExpr.RootHelperExpr = function() {
wgxpath.Expr.call(this, wgxpath.DataType.NODESET);
};
goog.inherits(wgxpath.PathExpr.RootHelperExpr, wgxpath.Expr);
/**
* Evaluates the root-node helper expression.
*
* @param {!wgxpath.Context} ctx The context to evaluate the expression in.
* @return {!wgxpath.NodeSet} The evaluation result.
*/
wgxpath.PathExpr.RootHelperExpr.prototype.evaluate = function(ctx) {
var nodeset = new wgxpath.NodeSet();
var node = ctx.getNode();
if (node.nodeType == goog.dom.NodeType.DOCUMENT) {
nodeset.add(node);
} else {
nodeset.add(/** @type {!Node} */ (node.ownerDocument));
}
return nodeset;
};
/**
* @override
*/
wgxpath.PathExpr.RootHelperExpr.prototype.toString = function() {
return 'Root Helper Expression';
};
/**
* Constructor for ContextHelperExpr.
*
* @extends {wgxpath.Expr}
* @constructor
*/
wgxpath.PathExpr.ContextHelperExpr = function() {
wgxpath.Expr.call(this, wgxpath.DataType.NODESET);
};
goog.inherits(wgxpath.PathExpr.ContextHelperExpr, wgxpath.Expr);
/**
* Evaluates the context-node helper expression.
*
* @param {!wgxpath.Context} ctx The context to evaluate the expression in.
* @return {!wgxpath.NodeSet} The evaluation result.
*/
wgxpath.PathExpr.ContextHelperExpr.prototype.evaluate = function(ctx) {
var nodeset = new wgxpath.NodeSet();
nodeset.add(ctx.getNode());
return nodeset;
};
/**
* @override
*/
wgxpath.PathExpr.ContextHelperExpr.prototype.toString = function() {
return 'Context Helper Expression';
};
/**
* Returns whether the token is a valid PathExpr operator.
*
* @param {string} token The token to be checked.
* @return {boolean} Whether the token is a valid operator.
*/
wgxpath.PathExpr.isValidOp = function(token) {
return token == '/' || token == '//';
};
/**
* @override
* @return {!wgxpath.NodeSet} The nodeset result.
*/
wgxpath.PathExpr.prototype.evaluate = function(ctx) {
var nodeset = this.filter_.evaluate(ctx);
if (!(nodeset instanceof wgxpath.NodeSet)) {
throw Error('Filter expression must evaluate to nodeset.');
}
var steps = this.steps_;
for (var i = 0, l0 = steps.length; i < l0 && nodeset.getLength(); i++) {
var step = steps[i];
var reverse = step.getAxis().isReverse();
var iter = nodeset.iterator(reverse);
nodeset = null;
var node, next;
if (!step.doesNeedContextPosition() &&
step.getAxis() == wgxpath.Step.Axis.FOLLOWING) {
for (node = iter.next(); next = iter.next(); node = next) {
if (node.contains && !node.contains(next)) {
break;
} else {
if (!(next.compareDocumentPosition(/** @type {!Node} */ (node)) &
8)) {
break;
}
}
}
nodeset = step.evaluate(new
wgxpath.Context(/** @type {wgxpath.Node} */ (node)));
} else if (!step.doesNeedContextPosition() &&
step.getAxis() == wgxpath.Step.Axis.PRECEDING) {
node = iter.next();
nodeset = step.evaluate(new
wgxpath.Context(/** @type {wgxpath.Node} */ (node)));
} else {
node = iter.next();
nodeset = step.evaluate(new
wgxpath.Context(/** @type {wgxpath.Node} */ (node)));
while ((node = iter.next()) != null) {
var result = step.evaluate(new
wgxpath.Context(/** @type {wgxpath.Node} */ (node)));
nodeset = wgxpath.NodeSet.merge(nodeset, result);
}
}
}
return /** @type {!wgxpath.NodeSet} */ (nodeset);
};
/**
* @override
*/
wgxpath.PathExpr.prototype.toString = function() {
var text = 'Path Expression:';
text += wgxpath.Expr.indent(this.filter_);
if (this.steps_.length) {
var steps = goog.array.reduce(this.steps_, function(prev, curr) {
return prev + wgxpath.Expr.indent(curr);
}, 'Steps:');
text += wgxpath.Expr.indent(steps);
}
return text;
};