blob: 14bfa748159708f4fc6c663a0877cfb42733eff1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 Andrea Bittau, University College London, and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* Mukul Gandhi - bug 274805 - improvements to xs:integer data type
* Jesper Steen Moeller - bug 285145 - check arguments to op:to
* Jesper Steen Moeller - bug 262765 - fixed node state iteration
* Jesper Steen Moller - bug 275610 - Avoid big time and memory overhead for externals
* Jesper Steen Moller - bug 280555 - Add pluggable collation support
* Jesper Steen Moller - bug 281938 - undefined context should raise error
* Jesper Steen Moller - bug 262765 - use correct 'effective boolean value'
* Jesper Steen Moller - bug 312191 - instance of test fails with partial matches
* Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4
* Mukul Gandhi - bug 325262 - providing ability to store an XPath2 sequence
* into an user-defined variable.
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor;
import org.apache.xerces.xs.ItemPSVI;
import org.apache.xerces.xs.XSTypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.ast.XPath;
import org.eclipse.wst.xml.xpath2.processor.internal.Axis;
import org.eclipse.wst.xml.xpath2.processor.internal.DescendantOrSelfAxis;
import org.eclipse.wst.xml.xpath2.processor.internal.Focus;
import org.eclipse.wst.xml.xpath2.processor.internal.ForwardAxis;
import org.eclipse.wst.xml.xpath2.processor.internal.ParentAxis;
import org.eclipse.wst.xml.xpath2.processor.internal.ReverseAxis;
import org.eclipse.wst.xml.xpath2.processor.internal.SelfAxis;
import org.eclipse.wst.xml.xpath2.processor.internal.SeqType;
import org.eclipse.wst.xml.xpath2.processor.internal.StaticNameError;
import org.eclipse.wst.xml.xpath2.processor.internal.StaticTypeNameError;
import org.eclipse.wst.xml.xpath2.processor.internal.TypeError;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.AddExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.AndExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.AnyKindTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.AttributeTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.AxisStep;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.BinExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.CastExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.CastableExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.CmpExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.CntxItemExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.CommentTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.DecimalLiteral;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.DivExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.DocumentTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.DoubleLiteral;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ElementTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ExceptExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.Expr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.FilterExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ForExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ForwardStep;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.FunctionCall;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.IDivExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.IfExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.InstOfExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.IntegerLiteral;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.IntersectExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ItemType;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.MinusExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ModExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.MulExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.NameTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.OrExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.PITest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ParExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.PipeExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.PlusExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.QuantifiedExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.RangeExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.ReverseStep;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.SchemaAttrTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.SchemaElemTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.SequenceType;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.SingleType;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.StepExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.StringLiteral;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.SubExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.TextTest;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.TreatAsExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.UnionExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.VarExprPair;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.VarRef;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.XPathExpr;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.XPathNode;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.XPathVisitor;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FnBoolean;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FnData;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FnRoot;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsDiv;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsEq;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsGe;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsGt;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsIDiv;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsLe;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsLt;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsMinus;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsMod;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsNe;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsPlus;
import org.eclipse.wst.xml.xpath2.processor.internal.function.FsTimes;
import org.eclipse.wst.xml.xpath2.processor.internal.function.OpExcept;
import org.eclipse.wst.xml.xpath2.processor.internal.function.OpIntersect;
import org.eclipse.wst.xml.xpath2.processor.internal.function.OpTo;
import org.eclipse.wst.xml.xpath2.processor.internal.function.OpUnion;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyAtomicType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AttrType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.CommentType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.DocType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.ElementType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.NodeType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.NumericType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.PIType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.QName;
import org.eclipse.wst.xml.xpath2.processor.internal.types.TextType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSBoolean;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSInteger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* Default evaluator interface
*/
public class DefaultEvaluator implements XPathVisitor, Evaluator {
private static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
private static final QName ANY_ATOMIC_TYPE = new QName("xs",
"anyAtomicType", XML_SCHEMA_NS);
static class DummyError extends Error {
/**
*
*/
private static final long serialVersionUID = 3161644790881405403L;
// 0: dynamic error
// 1: type error
private int _type;
public DummyError(int type) {
_type = type;
}
public int type() {
return _type;
}
}
private DynamicContext _dc;
private XPathException _err;
// stuff anyone may use
private Collection _g_coll;
private XSInteger _g_xsint;
// this is a parameter that may be set on a call...
// the parameter may become invalid on the next call... i.e. the
// previous parameter is not saved... so use with care! [remember...
// this thing is highly recursive]
private Object _param;
static class Pair {
public Object _one;
public Object _two;
public Pair(Object o, Object t) {
_one = o;
_two = t;
}
}
/**
* set parameters
*
* @param dc
* is the dynamic context.
* @param doc
* is the document.
*/
public DefaultEvaluator(DynamicContext dc, Document doc) {
_dc = dc;
_err = null;
// initialize context item with root of document
ResultSequence rs = ResultSequenceFactory.create_new();
if (doc != null) rs.add(new DocType(doc));
_dc.set_focus(new Focus(rs));
_param = null;
_g_coll = new ArrayList();
_g_xsint = new XSInteger();
}
// XXX this kinda sux
// the problem is that visistor interface does not throw exceptions...
// so we get around it ;D
private void report_error(DynamicError err) {
_err = err;
throw new DummyError(0);
}
private void report_error(TypeError err) {
_err = err;
throw new DummyError(1);
}
private void report_error(StaticNameError err) {
throw new DummyError(666);
}
/**
* evaluate the xpath node
*
* @param node
* is the xpath node.
* @throws dynamic
* error.
* @return result sequence.
*/
public ResultSequence evaluate(XPathNode node) throws DynamicError {
try {
return (ResultSequence) node.accept(this);
} catch (DummyError e) {
switch (e.type()) {
case 0:
throw (DynamicError) _err;
case 1:
throw new DynamicError((TypeError) _err);
default:
assert false;
}
}
return null; // unreach
}
// basically the comma operator...
private ResultSequence do_expr(Iterator i) {
ResultSequence rs = null;
while (i.hasNext()) {
Expr e = (Expr) i.next();
ResultSequence result = (ResultSequence) e.accept(this);
if (rs == null)
rs = result;
else
rs.concat(result);
}
if (rs == null)
rs = ResultSequenceFactory.create_new();
return rs;
}
/**
* iterate through xpath expression
*
* @param xp
* is the xpath.
* @return result sequence.
*/
public Object visit(XPath xp) {
ResultSequence rs = do_expr(xp.iterator());
return rs;
}
// XXX ugly
// type: 0 = for [return == "correct"]
// 1 = for all [return false, return empty on true]
// 2 = there exists [return true, return empty on false]
private ResultSequence do_for_quantified_expr(ListIterator iter,
Expr finalexpr, int type) {
// we have more vars to bind...
if (iter.hasNext()) {
boolean allocated_var = false;
ResultSequence result = ResultSequenceFactory.create_new();
VarExprPair ve = (VarExprPair) iter.next();
// evaluate binding sequence
ResultSequence rs = (ResultSequence) ve.expr().accept(this);
// XXX
if (rs.empty()) {
iter.previous();
return result;
}
QName varname = ve.varname();
// for each item of binding sequence, bind the range
// variable and do the expression, concatenating the
// result
for (Iterator i = rs.iterator(); i.hasNext();) {
AnyType item = (AnyType) i.next();
_dc.set_variable(varname, item);
allocated_var = true;
_dc.new_scope();
ResultSequence res = do_for_quantified_expr(iter, finalexpr,
type);
_dc.destroy_scope();
assert res != null;
// ok here we got a "real" result, now figure
// out what to do with it
XSBoolean effbool = null;
switch (type) {
// for expression
case 0:
result.concat(res);
break;
// we need the effective boolean value
// of the expression
case 1:
case 2:
effbool = effective_boolean_value(res);
break;
default:
assert false;
}
// we got a quantified expression
if (effbool != null) {
// for all
if (type == 1) {
result = ResultSequenceFactory.create_new(effbool);
if (!effbool.value())
break;
}
// there exists
else if (type == 2) {
result = ResultSequenceFactory.create_new(effbool);
if (effbool.value())
break;
} else
assert false;
}
}
if (allocated_var) {
boolean del = _dc.del_variable(varname);
assert del == true;
}
iter.previous();
return result;
}
// we finally got to do the "last expression"
else {
return (ResultSequence) finalexpr.accept(this);
}
}
/**
* visit for expression
*
* @param fex
* is the for expression.
* @return a new function.
*/
public Object visit(ForExpr fex) {
// XXX
List pairs = new ArrayList(fex.ve_pairs());
return do_for_quantified_expr(pairs.listIterator(), fex.expr(), 0);
}
/**
* visit quantified expression
*
* @param qex
* is the quantified expression.
* @return a new function or null.
*/
public Object visit(QuantifiedExpr qex) {
// XXX
List pairs = new ArrayList(qex.ve_pairs());
int hack = 0;
switch (qex.type()) {
case QuantifiedExpr.SOME:
hack = 2;
break;
case QuantifiedExpr.ALL:
hack = 1;
break;
default:
assert false;
return null; // unreach
}
ResultSequence rs = do_for_quantified_expr(pairs.listIterator(), qex
.expr(), hack);
// make sure we found answer
if (!rs.empty())
return rs;
// ok because all of this is a hack... here we go
switch (qex.type()) {
case QuantifiedExpr.SOME:
return ResultSequenceFactory.create_new(new XSBoolean(false));
case QuantifiedExpr.ALL:
return ResultSequenceFactory.create_new(new XSBoolean(true));
default:
assert false;
return null; // unreach
}
}
/**
* visit if expression
*
* @param ifex
* is the if expression.
* @return a ifex.then_clause().accept(this).
*/
public Object visit(IfExpr ifex) {
ResultSequence test_res = do_expr(ifex.iterator());
XSBoolean res = effective_boolean_value(test_res);
if (res.value())
return ifex.then_clause().accept(this);
else
return ifex.else_clause().accept(this);
}
private boolean[] do_logic_exp(BinExpr e) {
Collection args = do_bin_args(e);
Iterator argiter = args.iterator();
ResultSequence one = (ResultSequence) argiter.next();
ResultSequence two = (ResultSequence) argiter.next();
boolean oneb = effective_boolean_value(one).value();
boolean twob = effective_boolean_value(two).value();
boolean res[] = { oneb, twob };
return res;
}
/**
* visit or expression
*
* @param orex
* is the or expression.
* @return a new function
*/
public Object visit(OrExpr orex) {
boolean res[] = do_logic_exp(orex);
return ResultSequenceFactory
.create_new(new XSBoolean(res[0] || res[1]));
}
/**
* visit and expression
*
* @param andex
* is the and expression.
* @return a new function
*/
public Object visit(AndExpr andex) {
boolean res[] = do_logic_exp(andex);
return ResultSequenceFactory
.create_new(new XSBoolean(res[0] && res[1]));
}
private ResultSequence node_cmp(int type, Collection args) {
ResultSequence rs = ResultSequenceFactory.create_new();
assert args.size() == 2;
Iterator argsiter = args.iterator();
ResultSequence one = (ResultSequence) argsiter.next();
ResultSequence two = (ResultSequence) argsiter.next();
int size_one = one.size();
int size_two = two.size();
if (size_one > 1 || size_two > 1)
report_error(TypeError.invalid_type(null));
if (size_one == 0 || size_two == 0)
return rs;
AnyType at_one = one.first();
AnyType at_two = two.first();
if (!(at_one instanceof NodeType) || !(at_two instanceof NodeType))
report_error(TypeError.invalid_type(null));
// ok we got the args finally
NodeType nt_one = (NodeType) at_one;
NodeType nt_two = (NodeType) at_two;
boolean answer = false; // we are pessimistic as usual
// do comparison
switch (type) {
case CmpExpr.IS:
answer = nt_one.node_value() == nt_two.node_value();
break;
case CmpExpr.LESS_LESS:
answer = nt_one.before(nt_two);
break;
case CmpExpr.GREATER_GREATER:
answer = nt_one.after(nt_two);
break;
default:
assert false;
}
rs.add(new XSBoolean(answer));
return rs;
}
/**
* visit compare expression
*
* @param cmpex
* is the compare expression.
* @return a new function or null
*/
public Object visit(CmpExpr cmpex) {
try {
Collection args = do_bin_args(cmpex);
switch (cmpex.type()) {
case CmpExpr.EQ:
return FsEq.fs_eq_value(args, _dc);
case CmpExpr.NE:
return FsNe.fs_ne_value(args, _dc);
case CmpExpr.GT:
return FsGt.fs_gt_value(args, _dc);
case CmpExpr.LT:
return FsLt.fs_lt_value(args, _dc);
case CmpExpr.GE:
return FsGe.fs_ge_value(args, _dc);
case CmpExpr.LE:
return FsLe.fs_le_value(args, _dc);
case CmpExpr.EQUALS:
return FsEq.fs_eq_general(args, _dc);
case CmpExpr.NOTEQUALS:
return FsNe.fs_ne_general(args, _dc);
case CmpExpr.GREATER:
return FsGt.fs_gt_general(args, _dc);
case CmpExpr.LESSTHAN:
return FsLt.fs_lt_general(args, _dc);
case CmpExpr.GREATEREQUAL:
return FsGe.fs_ge_general(args, _dc);
case CmpExpr.LESSEQUAL:
return FsLe.fs_le_general(args, _dc);
case CmpExpr.IS:
case CmpExpr.LESS_LESS:
case CmpExpr.GREATER_GREATER:
return node_cmp(cmpex.type(), args);
default:
assert false;
}
} catch (DynamicError err) {
report_error(err);
}
return null; // unreach
}
/**
* visit range expression
*
* @param rex
* is the range expression.
* @return a new function
*/
public Object visit(RangeExpr rex) {
ResultSequence one = (ResultSequence) rex.left().accept(this);
ResultSequence two = (ResultSequence) rex.right().accept(this);
if (one.empty() || two.empty()) return ResultSequenceFactory.create_new();
Collection args = new ArrayList();
args.add(one);
args.add(two);
try {
return OpTo.op_to(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
private XSBoolean effective_boolean_value(ResultSequence rs) {
try {
return FnBoolean.fn_boolean(rs);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit and expression
*
* @param addex
* is the and expression.
* @return a new function
*/
public Object visit(AddExpr addex) {
try {
Collection args = do_bin_args(addex);
return FsPlus.fs_plus(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit sub expression
*
* @param subex
* is the sub expression.
* @return a new function
*/
public Object visit(SubExpr subex) {
try {
Collection args = do_bin_args(subex);
return FsMinus.fs_minus(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit multiply expression
*
* @param mulex
* is the mul expression.
* @return a new function
*/
public Object visit(MulExpr mulex) {
try {
Collection args = do_bin_args(mulex);
return FsTimes.fs_times(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit division expression
*
* @param mulex
* is the division expression.
* @return a new function
*/
public Object visit(DivExpr mulex) {
try {
Collection args = do_bin_args(mulex);
return FsDiv.fs_div(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit integer division expression
*
* @param mulex
* is the integer division expression.
* @return a new function
*/
public Object visit(IDivExpr mulex) {
try {
Collection args = do_bin_args(mulex);
return FsIDiv.fs_idiv(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit modular expression
*
* @param mulex
* is the modular expression.
* @return a new function
*/
public Object visit(ModExpr mulex) {
try {
Collection args = do_bin_args(mulex);
return FsMod.fs_mod(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
private Collection do_bin_args(BinExpr e) {
ResultSequence one = (ResultSequence) e.left().accept(this);
ResultSequence two = (ResultSequence) e.right().accept(this);
Collection args = new ArrayList();
args.add(one);
args.add(two);
return args;
}
/**
* visit union expression
*
* @param unex
* is the union expression.
* @return a new function
*/
public Object visit(UnionExpr unex) {
try {
Collection args = do_bin_args(unex);
return OpUnion.op_union(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit pipe expression
*
* @param pipex
* is the pipe expression.
* @return a new function
*/
// XXX same as above
public Object visit(PipeExpr pipex) {
try {
Collection args = do_bin_args(pipex);
return OpUnion.op_union(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit intersect expression
*
* @param iexpr
* is the intersect expression.
* @return a new function
*/
public Object visit(IntersectExpr iexpr) {
try {
Collection args = do_bin_args(iexpr);
return OpIntersect.op_intersect(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit except expression
*
* @param eexpr
* is the except expression.
* @return a new function
*/
public Object visit(ExceptExpr eexpr) {
try {
Collection args = do_bin_args(eexpr);
return OpExcept.op_except(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit instance of expression
*
* @param ioexp
* is the instance of expression.
* @return a new function
*/
public Object visit(InstOfExpr ioexp) {
// get the value
ResultSequence rs = (ResultSequence) ioexp.left().accept(this);
// get the sequence type
SequenceType seqt = (SequenceType) ioexp.right();
return ResultSequenceFactory.create_new(new XSBoolean(isInstanceOf(rs, seqt)));
}
private boolean isInstanceOf(ResultSequence rs, SequenceType seqt) {
Object oldParam = this._param;
try {
this._param = new Pair(null, rs);
int sequenceLength = rs.size();
// Run the matcher
seqt.accept(this);
int lengthAfter = rs.size();
if (sequenceLength != lengthAfter)
return false; // Something didn't match, so it's not an instance of it
return seqt.isLengthValid(sequenceLength);
} finally {
this._param = oldParam;
}
}
/**
* visit treat-as expression
*
* @param taexp
* is the treat-as expression.
* @return a new function
*/
public Object visit(TreatAsExpr taexp) {
ResultSequence rs = (ResultSequence) taexp.left().accept(this);
SequenceType seqt = (SequenceType) taexp.right();
SeqType st = new SeqType(seqt, _dc, rs);
try {
st.match(rs);
} catch (DynamicError err) {
report_error(err);
}
return rs;
}
/**
* visit castable expression
*
* @param cexp
* is the castable expression.
* @return a new function
*/
public Object visit(CastableExpr cexp) {
boolean castable = false;
try {
CastExpr ce = new CastExpr((Expr) cexp.left(), (SingleType) cexp
.right());
visit(ce);
castable = true;
} catch (Throwable t) {
castable = false;
}
return ResultSequenceFactory.create_new(new XSBoolean(castable));
}
/**
* visit cast expression
*
* @param cexp
* is the cast expression.
* @return a new function
*/
public Object visit(CastExpr cexp) {
ResultSequence rs = (ResultSequence) cexp.left().accept(this);
SingleType st = (SingleType) cexp.right();
rs = FnData.atomize(rs);
if (rs.size() > 1)
report_error(TypeError.invalid_type(null));
if (rs.empty()) {
if (st.qmark())
return rs;
else
report_error(TypeError.invalid_type(null));
}
AnyType at = rs.first();
if (!(at instanceof AnyAtomicType))
report_error(TypeError.invalid_type(null));
AnyAtomicType aat = (AnyAtomicType) at;
QName type = st.type();
// check if constructor exists
// try {
if (!_dc.function_exists(type, 1))
report_error(TypeError.invalid_type(null));
/*
* } catch(StaticNsNameError err) {
* report_error(TypeError.invalid_type(null)); }
*/
// prepare args from function
Collection args = new ArrayList();
args.add(ResultSequenceFactory.create_new(aat));
try {
return _dc.evaluate_function(type, args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit minus expression
*
* @param e
* is the minus expression.
* @return a new function
*/
public Object visit(MinusExpr e) {
ResultSequence rs = (ResultSequence) e.arg().accept(this);
Collection args = new ArrayList();
args.add(rs);
try {
return FsMinus.fs_minus_unary(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit plus expression
*
* @param e
* is the plus expression.
* @return a new function
*/
public Object visit(PlusExpr e) {
ResultSequence rs = (ResultSequence) e.arg().accept(this);
Collection args = new ArrayList();
args.add(rs);
try {
return FsPlus.fs_plus_unary(args);
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
// this will evaluate the step expression for the whole focus and return
// the result.
//
// i.e. It will execute the step expression for each item in the focus
// [each time changing the context item].
private ResultSequence do_step(StepExpr se) {
ResultSequence rs = ResultSequenceFactory.create_new();
ArrayList results = new ArrayList();
int type = 0; // 0: don't know yet
// 1: atomic
// 2: node
Focus focus = _dc.focus();
int original_pos = focus.position();
// execute step for all items in focus
while (true) {
results.add(se.accept(this));
// go to next
if (!focus.advance_cp())
break;
}
// make sure we didn't change focus
focus.set_position(original_pos);
boolean node_types = false;
// check the results
for (Iterator i = results.iterator(); i.hasNext();) {
ResultSequence result = (ResultSequence) i.next();
// make sure results are of same type, and add them in
for (Iterator j = result.iterator(); j.hasNext();) {
AnyType item = (AnyType) j.next();
// first item
if (type == 0) {
if (item instanceof AnyAtomicType)
type = 1;
else if (item instanceof NodeType)
type = 2;
else
assert false;
}
// make sure we got coherent types
switch (type) {
// atomic... just concat
case 1:
if (!(item instanceof AnyAtomicType))
report_error(TypeError.mixed_vals(null));
rs.add(item);
break;
case 2:
node_types = true;
if (!(item instanceof NodeType))
report_error(TypeError.mixed_vals(null));
rs.add(item);
break;
default:
assert false;
}
}
}
// XXX lame
if (node_types) {
rs = NodeType.eliminate_dups(rs);
rs = NodeType.sort_document_order(rs);
}
return rs;
}
private ResultSequence root_self_node() {
Axis axis = new SelfAxis();
ResultSequence rs;
// XXX the cast!!!
rs = axis.iterate((NodeType) _dc.context_item(), _dc);
rs = kind_test(rs, NodeType.class);
try {
List records = new ArrayList();
records.add(rs);
rs = FnRoot.fn_root(records, _dc);
} catch (DynamicError err) {
report_error(err);
}
return rs;
}
private ResultSequence descendant_or_self_node(ResultSequence rs) {
ResultSequence res = ResultSequenceFactory.create_new();
Axis axis = new DescendantOrSelfAxis();
// for all nodes, get descendant or self nodes
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType item = (NodeType) i.next();
ResultSequence nodes = axis.iterate(item, _dc);
nodes = kind_test(nodes, NodeType.class);
res.concat(nodes);
}
return res;
}
/**
* visit XPath expression
*
* @param e
* is the XPath expression.
* @return a new function
*/
public Object visit(XPathExpr e) {
XPathExpr xp = e;
ResultSequence rs = null;
Focus original_focus = _dc.focus();
// do all the steps
while (xp != null) {
StepExpr se = xp.expr();
if (se != null) {
// this is not the first step
if (rs != null) {
// XXX ?
// the expression didn't return any
// results...
if (rs.size() == 0)
break;
// make sure result of previous step are
// nodes!
for (Iterator i = rs.iterator(); i.hasNext();) {
AnyType item = (AnyType) i.next();
if (!(item instanceof NodeType)) {
report_error(TypeError.step_conatins_atoms(null));
return null; // unreach
}
}
// check if we got a //
if (xp.slashes() == 2) {
rs = descendant_or_self_node(rs);
if (rs.size() == 0)
break;
}
// make result of previous step the new
// focus
_dc.set_focus(new Focus(rs));
// do the step for all item in context
rs = do_step(se);
}
// this is first step...
// note... we may be called from upstream...
// like in the expression sorbo/*[2] ... we may
// be called to evaluate the 2... the caller
// will iterate through the whole outer focus
// for us
else {
// XXX ???
if (xp.slashes() == 1) {
rs = root_self_node();
_dc.set_focus(new Focus(rs));
rs = do_step(se);
} else if (xp.slashes() == 2) {
ResultSequence res = ResultSequenceFactory.create_new();
rs = root_self_node();
rs = descendant_or_self_node(rs);
_dc.set_focus(new Focus(rs));
rs = do_step(se);
} else
rs = (ResultSequence) se.accept(this);
}
}
// the expression is "/"
else {
assert xp.slashes() == 1;
rs = root_self_node();
}
xp = xp.next();
}
// restore focus
_dc.set_focus(original_focus);
return rs;
}
/**
* visit a forward step expression
*
* @param e
* is the forward step.
* @return a new function
*/
public Object visit(ForwardStep e) {
// get context node
AnyType ci = _dc.context_item();
if (ci == null)
report_error(DynamicError.contextUndefined());
if (!(ci instanceof NodeType))
report_error(TypeError.ci_not_node(ci.string_type()));
NodeType cn = (NodeType) ci;
// get the nodes on the axis
ForwardAxis axis = e.iterator();
ResultSequence nodes = axis.iterate(cn, _dc);
// get all nodes in the axis, and principal node
Pair arg = new Pair(axis.principal_node_kind().string_type(), nodes);
// do the name test
_param = arg;
ResultSequence rs = (ResultSequence) e.node_test().accept(this);
return rs;
}
/**
* visit a reverse step expression
*
* @param e
* is the reverse step.
* @return a new function
*/
// XXX unify with top
public Object visit(ReverseStep e) {
// get context node
AnyType ci = _dc.context_item();
if (!(ci instanceof NodeType))
report_error(TypeError.ci_not_node(ci.string_type()));
NodeType cn = (NodeType) ci;
// get the nodes on the axis
ReverseAxis axis = e.iterator();
// short for "gimme da parent"
if (e.axis() == ReverseStep.DOTDOT) {
axis = new ParentAxis();
return kind_test(axis.iterate(cn, _dc), NodeType.class);
}
assert axis != null;
ResultSequence nodes = axis.iterate(cn, _dc);
// get all nodes in the axis, and principal node
Pair arg = new Pair(axis.principal_node_kind().string_type(), nodes);
// do the name test
_param = arg;
ResultSequence rs = (ResultSequence) e.node_test().accept(this);
return rs;
}
// XXX this routine sux
private boolean name_test(NodeType node, QName name, String type) {
// make sure principal node kind is the same
if (node == null) {
return false;
}
if (!type.equals(node.string_type())) {
return false;
}
String test_prefix = name.prefix();
// if unprefixed and principal node kind is element, set default
// element namespace
if (test_prefix == null && type.equals("element")) {
// XXX make a new copy
name = new QName(null, name.local());
name.set_namespace(_dc.default_namespace());
// if we actually have a namespace, pretend we do =D
if (name.namespace() != null)
test_prefix = "";
}
QName node_name = node.node_name();
assert node_name != null;
// make sure namespace matches
String node_namespace = node_name.namespace();
String test_namespace = null;
if (name.expanded())
test_namespace = name.namespace();
// name test has no prefix
if (test_prefix == null) {
// ok no namespace... match
if (node_namespace == null) {
} else {
return false;
}
}
// name test has a prefix and is not wildcard
// XXX AT THIS POINT ALL PREFIXES NEED TO BE RESOLVED!
else if (!test_namespace.equals("*")) {
// the node doesn't have a namespace... no match
if (node_namespace == null) {
return false;
}
// check namespaces
else {
if (node_namespace.equals(test_namespace)) {
// namespace matches
} else {
return false;
}
}
}
// make sure local part matches
// check for wildcard in localpart
if (name.local().equals("*"))
return true;
// check if local part matches
if (!name.local().equals(node_name.local())) {
return false;
}
return true;
}
/**
* visit a name test expression
*
* @param e
* is thename test.
* @return a result sequence
*/
public Object visit(NameTest e) {
QName name = e.name();
// get the arguments
Pair arg = (Pair) _param;
String type = (String) arg._one;
ResultSequence rs = (ResultSequence) arg._two;
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType nt = (NodeType) i.next();
// check if node passes name test
if (!name_test(nt, name, type))
i.remove();
}
return rs;
}
/**
* visit variable reference
*
* @param e
* is the variable reference.
* @return a result sequence
*/
public Object visit(VarRef e) {
ResultSequence rs = ResultSequenceFactory.create_new();
Object var = _dc.get_variable(e.name());
assert var != null;
if (var instanceof AnyType) {
rs.add((AnyType) var);
}
else if (var instanceof ResultSequence) {
rs.concat((ResultSequence) var);
}
return rs;
}
/**
* visit string literal.
*
* @param e
* is the string literal.
* @return a result sequence
*/
public Object visit(StringLiteral e) {
ResultSequence rs = ResultSequenceFactory.create_new();
rs.add(e.value());
return rs;
}
/**
* visit integer literal.
*
* @param e
* is the integer literal.
* @return a result sequence
*/
public Object visit(IntegerLiteral e) {
ResultSequence rs = ResultSequenceFactory.create_new();
rs.add(e.value());
return rs;
}
/**
* visit double literal.
*
* @param e
* is the double literal.
* @return a result sequence
*/
public Object visit(DoubleLiteral e) {
ResultSequence rs = ResultSequenceFactory.create_new();
rs.add(e.value());
return rs;
}
/**
* visit decimal literal.
*
* @param e
* is the decimal literal.
* @return a result sequence
*/
public Object visit(DecimalLiteral e) {
ResultSequence rs = ResultSequenceFactory.create_new();
rs.add(e.value());
return rs;
}
/**
* visit parent expression.
*
* @param e
* is the parent expression.
* @return a new function
*/
public Object visit(ParExpr e) {
return do_expr(e.iterator());
}
/**
* visit context item expression.
*
* @param e
* is the context item expression.
* @return a result sequence
*/
public Object visit(CntxItemExpr e) {
ResultSequence rs = ResultSequenceFactory.create_new();
AnyType contextItem = _dc.context_item();
if (contextItem == null) {
report_error(DynamicError.contextUndefined());
}
rs.add(contextItem);
return rs;
}
/**
* visit function call.
*
* @param e
* is the function call.
* @return a new function or null
*/
public Object visit(FunctionCall e) {
ArrayList args = new ArrayList();
for (Iterator i = e.iterator(); i.hasNext();) {
Expr arg = (Expr) i.next();
// each argument will produce a result sequence
args.add(arg.accept(this));
}
try {
ResultSequence rs = _dc.evaluate_function(e.name(), args);
return rs;
} catch (DynamicError err) {
report_error(err);
return null; // unreach
}
}
/**
* visit single type.
*
* @param e
* is the single type.
* @return null
*/
public Object visit(SingleType e) {
return null;
}
/**
* visit sequence type.
*
* @param e
* is the sequence type.
* @return null
*/
public Object visit(SequenceType e) {
ItemType it = e.item_type();
if (it != null)
it.accept(this);
return null;
}
/**
* visit item type.
*
* @param e
* is the item type.
* @return null
*/
public Object visit(ItemType e) {
switch (e.type()) {
case ItemType.ITEM:
break;
case ItemType.QNAME:
// try {
if (!_dc.type_defined(e.qname()))
report_error(new StaticTypeNameError("Type not defined: "
+ e.qname().string()));
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
item_test(arg, e.qname());
break;
case ItemType.KINDTEST:
e.kind_test().accept(this);
break;
}
return null;
}
private ResultSequence item_test(ResultSequence rs, QName qname) {
for (Iterator i = rs.iterator(); i.hasNext();) {
AnyType item = (AnyType) i.next();
if (item instanceof NodeType) {
NodeType node = ((NodeType)item);
if (node.node_value() instanceof ItemPSVI) {
if (_dc.derives_from(node , qname)) continue;
}
// fall through => non-match
} else {
// atomic of some sort
if (qname.equals(ANY_ATOMIC_TYPE)) continue; // match !
final AnyAtomicType aat = _dc.make_atomic(qname);
if (aat.getClass().isInstance(item)) continue;
// fall through => non-match
}
i.remove();
}
return rs;
}
private ResultSequence kind_test(ResultSequence rs, Class kind) {
for (Iterator i = rs.iterator(); i.hasNext();) {
if (!kind.isInstance(i.next()))
i.remove();
}
return rs;
}
/**
* visit any kind test.
*
* @param e
* is the any kind test.
* @return a new function
*/
public Object visit(AnyKindTest e) {
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
return kind_test(arg, NodeType.class);
}
/**
* visit document test.
*
* @param e
* is the document test.
* @return result sequence
*/
public Object visit(DocumentTest e) {
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
int type = e.type();
// filter doc nodes
ResultSequence rs = kind_test(arg, DocType.class);
if (type == DocumentTest.NONE)
return rs;
// for all docs, find the ones with exactly one element, and do
// the element test
for (Iterator i = rs.iterator(); i.hasNext();) {
DocType doc = (DocType) i.next();
int elem_count = 0;
ElementType elem = null;
// make sure doc has only 1 element
NodeList children = doc.node_value().getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
// bingo
if (child.getNodeType() == Node.ELEMENT_NODE) {
elem_count++;
if (elem_count > 1)
break;
elem = new ElementType((Element) child);
}
}
// this doc is no good... send him to hell
if (elem_count != 1) {
i.remove();
continue;
}
assert elem != null;
// setup parameter for element test
ResultSequence res = ResultSequenceFactory.create_new();
res.add(elem);
_param = new Pair("element", res);
// do name test
res = null;
if (type == DocumentTest.ELEMENT)
res = (ResultSequence) e.elem_test().accept(this);
else if (type == DocumentTest.SCHEMA_ELEMENT)
res = (ResultSequence) e.schema_elem_test().accept(this);
else
assert false;
// check if element survived nametest
if (res.size() != 1)
i.remove();
}
return rs;
}
/**
* visit text test.
*
* @param e
* is the text test.
* @return a new function
*/
public Object visit(TextTest e) {
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
return kind_test(arg, TextType.class);
}
/**
* visit comment test.
*
* @param e
* is the text test.
* @return a new function
*/
public Object visit(CommentTest e) {
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
return kind_test(arg, CommentType.class);
}
/**
* visit PI test.
*
* @param e
* is the PI test.
* @return a argument
*/
public Object visit(PITest e) {
ResultSequence arg = (ResultSequence) ((Pair) _param)._two;
String pit_arg = e.arg();
// match any pi
if (pit_arg == null)
return kind_test(arg, PIType.class);
for (Iterator i = arg.iterator(); i.hasNext();) {
AnyType item = (AnyType) i.next();
// match PI
if (item instanceof PIType) {
PIType pi = (PIType) item;
// match target
if (!pit_arg.equals(pi.value().getTarget()))
i.remove();
} else
i.remove();
}
return arg;
}
/**
* visit attribute test.
*
* @param e
* is the attribute test.
* @return a result sequence
*/
public Object visit(AttributeTest e) {
// filter out all attrs
ResultSequence rs = kind_test((ResultSequence) ((Pair) _param)._two,
AttrType.class);
// match the name if it's not a wild card
QName name = e.name();
if (name != null && !e.wild()) {
for (Iterator i = rs.iterator(); i.hasNext();) {
if (!name_test((NodeType) i.next(), name, "attribute"))
i.remove();
}
}
// match the type
QName type = e.type();
if (type != null) {
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
// check if element derives from
if (!_dc.derives_from(node, type))
i.remove();
}
}
return rs;
}
/**
* visit schema attribute test.
*
* @param e
* is the schema attribute test.
* @return a result sequence
*/
public Object visit(SchemaAttrTest e) {
// filter out all attrs
ResultSequence rs = kind_test((ResultSequence) ((Pair) _param)._two,
AttrType.class);
// match the name
QName name = e.arg();
for (Iterator i = rs.iterator(); i.hasNext();) {
if (!name_test((NodeType) i.next(), name, "attribute"))
i.remove();
}
// check the type
XSTypeDefinition et = _dc.attribute_type_definition(name);
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
if (!_dc.derives_from(node, et))
i.remove();
}
return rs;
}
/**
* visit element test.
*
* @param e
* is the element test.
* @return a result sequence
*/
public Object visit(ElementTest e) {
// filter out all elements
ResultSequence rs = kind_test((ResultSequence) ((Pair) _param)._two,
ElementType.class);
// match the name if it's not a wild card
QName name = e.name();
if (name != null && !e.wild()) {
for (Iterator i = rs.iterator(); i.hasNext();) {
if (!name_test((ElementType) i.next(), name, "element"))
i.remove();
}
}
// match the type
QName type = e.type();
if (type != null) {
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
// check if element derives from
if (_dc.derives_from(node, type)) {
// nilled may be true or false
if (e.qmark()) {
}
// nilled has to be false
else {
XSBoolean nilled = (XSBoolean) node.nilled().first();
if (nilled.value())
i.remove();
}
} else
i.remove();
}
}
return rs;
}
/**
* visit schema element test.
*
* @param e
* is the schema element test.
* @return a result sequence
*/
public Object visit(SchemaElemTest e) {
// filter out all elements
ResultSequence rs = kind_test((ResultSequence) ((Pair) _param)._two,
ElementType.class);
// match the name
// XXX substitution groups
QName name = e.name();
for (Iterator i = rs.iterator(); i.hasNext();) {
if (!name_test((ElementType) i.next(), name, "element"))
i.remove();
}
// check the type
XSTypeDefinition et = _dc.element_type_definition(name);
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
if (!_dc.derives_from(node, et)) {
i.remove();
continue;
}
XSBoolean nilled = (XSBoolean) node.nilled().first();
// XXX or, in schema it is nillable
if (nilled.value())
i.remove();
}
return rs;
}
private boolean predicate_truth(ResultSequence rs) {
// rule 1 of spec... if numeric type:
// if num eq position then true else false
if (rs.size() == 1) {
AnyType at = rs.get(0);
if (at instanceof NumericType) {
try {
_g_xsint.set_int(BigInteger.valueOf(_dc.context_position()));
return FsEq.fs_eq_fast(at, _g_xsint, _dc);
} catch (DynamicError err) {
report_error(err);
// unreach
assert false;
return false;
}
}
}
// rule 2
XSBoolean ret = effective_boolean_value(rs);
return ret.value();
}
// do the predicate for all items in focus
private ResultSequence do_predicate(Collection exprs) {
ResultSequence rs = ResultSequenceFactory.create_new();
Focus focus = _dc.focus();
int original_cp = focus.position();
// optimization
// check if predicate is single numeric constant
if (exprs.size() == 1) {
Expr expr = (Expr) exprs.iterator().next();
if (expr instanceof XPathExpr) {
XPathExpr xpe = (XPathExpr) expr;
if (xpe.next() == null && xpe.slashes() == 0
&& xpe.expr() instanceof FilterExpr) {
FilterExpr fex = (FilterExpr) xpe.expr();
if (fex.primary() instanceof IntegerLiteral) {
int pos = (((IntegerLiteral) fex.primary()).value()
.int_value()).intValue();
if (pos <= focus.last() && pos > 0) {
focus.set_position(pos);
rs.add(focus.context_item());
}
focus.set_position(original_cp);
return rs;
}
}
}
}
// go through all elements
while (true) {
// do the predicate
// XXX saxon doesn't allow for predicates to have
// commas... but XPath 2.0 spec seems to do
ResultSequence res = do_expr(exprs.iterator());
// if predicate is true, the context item is definitely
// in the sequence
if (predicate_truth(res))
rs.add(_dc.context_item());
res.release();
if (!focus.advance_cp())
break;
}
// restore
focus.set_position(original_cp);
return rs;
}
/**
* visit axis step.
*
* @param e
* is the axis step.
* @return a result sequence
*/
public Object visit(AxisStep e) {
ResultSequence rs = (ResultSequence) e.step().accept(this);
if (e.predicate_count() == 0)
return rs;
// I take it predicates are logical ANDS...
Focus original_focus = _dc.focus();
// go through all predicates
for (Iterator i = e.iterator(); i.hasNext();) {
// empty results... get out of here ? XXX
if (rs.size() == 0)
break;
_dc.set_focus(new Focus(rs));
rs = do_predicate((Collection) i.next());
}
// restore focus [context switching ;D ]
_dc.set_focus(original_focus);
return rs;
}
/**
* visit filter expression
*
* @param e
* is the filter expression.
* @return a result sequence
*/
// XXX unify with top ?
public Object visit(FilterExpr e) {
ResultSequence rs = (ResultSequence) e.primary().accept(this);
// if no predicates are present, then the result is the same as
// the primary expression
if (e.predicate_count() == 0)
return rs;
Focus original_focus = _dc.focus();
// go through all predicates
for (Iterator i = e.iterator(); i.hasNext();) {
if (rs.size() == 0)
break;
_dc.set_focus(new Focus(rs));
rs = do_predicate((Collection) i.next());
}
// restore focus [context switching ;D ]
_dc.set_focus(original_focus);
return rs;
}
}