Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package lux.xpath;
import lux.xquery.VariableContext;
/**
* An abstract XPath or XQuery expression.
*
* This class and its subclasses represent XPath expressions. Their
* toString() methods return valid XPath.
*/
public abstract class AbstractExpression implements Visitable {
protected AbstractExpression sup; // the enclosing (parent) expression, if any
protected AbstractExpression subs[]; // enclosed (child) expressions, or a 0-length array, or null (do we need to make this consistent?)
public enum Type {
PATH_EXPRESSION, PATH_STEP, PREDICATE, BINARY_OPERATION, SET_OPERATION,
LITERAL, ROOT, DOT, FUNCTION_CALL, SEQUENCE, UNARY_MINUS, SUBSEQUENCE,
LET, VARIABLE, COMPUTED_ELEMENT, ELEMENT, ATTRIBUTE, TEXT, FLWOR, CONDITIONAL, COMMENT,
DOCUMENT_CONSTRUCTOR, PROCESSING_INSTRUCTION, SATISFIES, INSTANCE_OF, CASTABLE, TREAT
}
private final Type type;
protected AbstractExpression (Type type) {
this.type = type;
}
/** Most types will correspond one-one
* with a subclass of AbstractExpression, but this
* enumerated value provides an integer equivalent that should be
* useful for efficient switch operations, encoding and the like.
* TODO: determine if this is just a waste of time; we could be using instanceof?
* @return the type of this expression
*/
public Type getType () {
return type;
}
public void acceptSubs (ExpressionVisitor visitor) {
for (int i = 0; i < subs.length && !visitor.isDone(); i++) {
int j = visitor.isReverse() ? (subs.length-i-1) : i;
AbstractExpression sub = subs[j].accept (visitor);
if (sub != subs[j]) {
subs[j]= sub;
}
}
}
/**
* @return the super (containing) expression, or null if this is the outermost expression in its tree
*/
public AbstractExpression getSuper() {
return sup;
}
/**
* @return the sub-expressions of this expression.
*/
public AbstractExpression [] getSubs() {
return subs;
}
protected void setSubs (AbstractExpression ... subExprs) {
subs = subExprs;
for (AbstractExpression sub : subs) {
sub.sup = this;
}
}
/** Each subclass must implement the toString(StringBuilder) method by
* appending itself as a syntatically valid XPath/XQuery expression in
* the given buffer.
* @param buf the buffer to append to
*/
public abstract void toString(StringBuilder buf);
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
toString (buf);
return buf.toString();
}
/**
* @return the root of this expression: this will either be a Root(/), a function returning document nodes,
* or null.
*/
public AbstractExpression getRoot () {
return null;
}
/**
* @return whether this expression is a Root or another expression that introduces
* a new query scope, such as a PathExpression beginning with a Root (/), or a subsequence
* of another absolute expression. This method returns false, supplying the common default.
*/
public boolean isAbsolute() {
return getRoot() != null;
}
/**
* @return whether this expression is proven to return results in document order. This method
* returns true iff all its subs return true, or it has none. Warning: incorrect results may occur if
* document-ordering is falsely asserted.
*/
public boolean isDocumentOrdered() {
if (subs != null) {
for (AbstractExpression sub : subs) {
if (!sub.isDocumentOrdered())
return false;
}
}
return true;
}
/**
* If this has absolute subexpressions, replace them with the replacement expression
* (see {@link Root#replaceRoot(AbstractExpression)}
* @param replacement the expression to use in place of '/'
* @return this
*/
public AbstractExpression replaceRoot(AbstractExpression replacement) {
if (subs != null) {
for (int i = 0; i < subs.length; i++) {
AbstractExpression replaced = subs[i].replaceRoot(replacement);
if (replaced != subs[i]) {
subs[i] = replaced;
}
}
}
return this;
}
/**
* append the sub-expression to the buffer, wrapping it in parentheses if its precedence is
* lower than or equal to this expression's. We need parens when precedence is equal because
* otherwise operations simply group left or right, but we have the actual grouping encoded
* in the expression tree and need to preserve that.
*
* Note: we can't just blindly wrap everything in parentheses because parens have special meaning
* in some XPath expressions where they can introduce document-ordering.
*
* @param buf the buffer to append to
* @param sub the sub-expression
*/
protected void appendSub(StringBuilder buf, AbstractExpression sub) {
if (sub.getPrecedence() <= getPrecedence()) {
buf.append ('(');
sub.toString(buf);
buf.append (')');
} else {
sub.toString(buf);
}
}
/**
* @return the head of this expression; ie the leftmost path sub-expression, which is just this
* expression (except for PathExpressions).
*/
public AbstractExpression getHead() {
return this;
}
/**
* @return the tail of this expression; ie everything after the head is removed, which is null
* unless this is a PathExpression {@link PathExpression#getTail}.
*/
public AbstractExpression getTail() {
return null;
}
/**
* This method is called by the optimizer in order to determine an element or attribute QName (or wildcard) against which
* some expression is being compared, in order to generate an appropriate text query.
* @return the rightmost path step in the context of this expression.
*/
public AbstractExpression getLastContextStep () {
return this;
}
/**
* If this expression depends "directly" on a variable, return that variable's binding context: a for or let clause,
* or a global variable definition. This recurses through variables, so if there are aliases it retrieves the ultimate
* context. We need to define directly dependent precisely; what it's used for is to determine with an order by
* expression is dependent on a for-variable, and ultimately whether an order by optimization can be applied.
* @return the binding context of the variable on which this expression depends, or null
*/
public VariableContext getBindingContext () {
return null;
}
/**
* @return a number indicating the *outer* precedence of this expression.
* Expressions with lower precedence numbers have lower
* precedence, ie bind more loosely, than expressions with higher
* precedence. Expressions with no sub-expressions are assigned a high
* precedence. Complex expressions can be seen as having an inner and an outer
* precedence; for example function call expressions behave as a comma with regard
* to their sub-expressions, the arguments, and like parentheses to their enclosing expression.
*/
public abstract int getPrecedence ();
/**
* @param other another expression
* @return whether the two expressions are of the same type and share the same local properties
*/
public boolean equivalent (AbstractExpression other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
if (! (getClass().isAssignableFrom(other.getClass()))) {
return false;
}
return propEquals ((AbstractExpression) other);
}
/**
* @param other another expression
* @return whether this expression is query-geq (fgreater-than-or-equal) to the other, in the sense
* that for all contexts c, exists(other|c) => exists(this|c). In particular, this implementation tests that
* the two expressions are of the same type and have local properties consistent with geq, by calling
* propGreaterEqual.
*/
public boolean geq (AbstractExpression other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
if (! (getClass().isAssignableFrom(other.getClass()))) {
return false;
}
return propGreaterEqual ((AbstractExpression) other);
}
/**
Traverse downwards, comparing with fromExpr for equivalence until one bottoms out,
ignoring fromExpr (since it has already been checked).
@param fromExpr
@param fieldExpr
@return whether fieldExpr >= queryExpr
*/
public boolean matchDown (AbstractExpression fieldExpr, AbstractExpression fromExpr) {
if (fieldExpr == fromExpr) {
return true;
}
if (! fieldExpr.geq(this)) {
// if fieldExpr does not encompass this at least formally, it is too restrictive
return false;
}
// all of queryExpr's subs *must* return a value (for a
// non-empty result), so a necessary condition for fieldExpr
// >= queryExpr is that every sub of fieldExpr match *some*
// sub of queryExpr
AbstractExpression[] fsubs = fieldExpr.getSubs();
if (fsubs == null) {
return subs == null || subs.length == 0 || isRestrictive();
}
AbstractExpression qsubMatched = null;
OUTER: for (AbstractExpression fsub : fsubs) {
if (fsub == fromExpr) {
continue;
}
for (AbstractExpression sub : subs) {
if (sub.matchDown(fsub, null)) {
qsubMatched = sub;
continue OUTER;
}
}
// no equivalent sub found
return false;
}
if (!isRestrictive()) {
// at least one of queryExpr's children must return a value, so
// in addition it is necessary that every child of queryExpr be
// matched by some child of fieldExpr
OUTER: for (AbstractExpression sub : subs) {
if (sub == qsubMatched) {
continue;
}
for (AbstractExpression fsub : fsubs) {
if (sub.matchDown(fsub, null)) {
continue OUTER;
}
}
return false;
}
}
return true;
}
/**
* @return a hashcode that is consistent with {@link #equivalent(AbstractExpression)}
*/
int equivHash () {
return type.ordinal();
}
/**
* @param oex another expression
* @return whether the other expression and this one have all the same local properties
*/
protected boolean propEquals (AbstractExpression oex) {
return (oex.getType() == type);
}
/**
* @param oex another expression of the same type as this
* @return whether the expressions' properties imply: this ge oex
*/
public boolean propGreaterEqual (AbstractExpression oex) {
return propEquals(oex);
}
public boolean deepEquals (AbstractExpression oex) {
if (! equivalent(oex)) {
return false;
}
if (subs == oex.subs) {
return true;
}
if (subs == null || oex.subs == null) {
return false;
}
if (subs.length != oex.subs.length) {
return false;
}
for (int i = 0; i < subs.length; i++) {
if (! (subs[i].deepEquals(oex.subs[i]))) {
return false;
}
}
return true;
}
/**
* An expression is restrictive when any empty sub implies the expression is empty.
* In other words, restrictive expressions only return results when all of their
* subs are non-empty. Eg: and, intersect, predicate, path step.
* @return whether this expression is restrictive
*/
public boolean isRestrictive () {
return false;
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */