2 // System.Xml.XPath.XPathExpression support classes
5 // Piers Haken (piersh@friskit.com)
7 // (C) 2002 Piers Haken
11 using System.Collections;
13 using System.Xml.XPath;
16 namespace System.Xml.XPath
19 internal class CompiledExpression : Test.Xml.XPath.XPathExpression
21 internal class CompiledExpression : XPathExpression
24 protected XmlNamespaceManager _nsm;
25 protected Expression _expr;
27 public CompiledExpression (Expression expr)
31 private CompiledExpression (CompiledExpression other)
37 public override Test.Xml.XPath.XPathExpression Clone () { return new CompiledExpression (this); }
39 public override XPathExpression Clone () { return new CompiledExpression (this); }
42 public override void SetContext (XmlNamespaceManager nsManager)
46 public override String Expression { get { return _expr.ToString (); }}
47 public override XPathResultType ReturnType { get { return _expr.ReturnType; }}
49 public override void AddSort (Object obj, IComparer cmp)
51 throw new NotImplementedException ();
54 public override void AddSort(object obj, XmlSortOrder sortOrder, XmlCaseOrder caseOrder, string str, XmlDataType type)
56 throw new NotImplementedException ();
59 public object Evaluate (BaseIterator iter)
63 return _expr.Evaluate (iter);
65 catch (XPathException e)
71 throw new XPathException ("Error during evaluation", e);
74 public XPathNodeIterator EvaluateNodeSet (BaseIterator iter)
78 return _expr.EvaluateNodeSet (iter);
80 catch (XPathException e)
86 throw new XPathException ("Error during evaluation", e);
93 /// Summary description for Expression.
95 internal abstract class Expression
100 public abstract XPathResultType ReturnType { get; }
101 public virtual XPathResultType GetReturnType (BaseIterator iter) { return ReturnType; }
102 public virtual object Evaluate (BaseIterator iter) { return null; }
104 public BaseIterator EvaluateNodeSet (BaseIterator iter)
106 if (GetReturnType (iter) == XPathResultType.NodeSet)
107 return (BaseIterator) Evaluate (iter);
108 throw new XPathException ("expected nodeset: "+ToString ());
111 public double EvaluateNumber (BaseIterator iter)
114 XPathResultType type = GetReturnType (iter);
115 if (type == XPathResultType.NodeSet)
117 result = EvaluateString (iter);
118 type = XPathResultType.String;
121 result = Evaluate (iter);
125 case XPathResultType.Number:
126 return (double) result;
127 case XPathResultType.Boolean:
128 return Convert.ToDouble ((bool) result);
129 case XPathResultType.String:
130 return XmlConvert.ToDouble ((string) result); // TODO: spec? convert string to number
132 throw new XPathException ("invalid node type"); // TODO: handle other types
136 public string EvaluateString (BaseIterator iter)
138 object result = Evaluate (iter);
139 switch (GetReturnType (iter))
141 case XPathResultType.Number:
142 return (string) XmlConvert.ToString ((double) result); // TODO: spec? convert number to string
143 case XPathResultType.Boolean:
144 return ((bool) result) ? "true" : "false";
145 case XPathResultType.String:
146 return (string) result;
147 case XPathResultType.NodeSet:
149 BaseIterator iterResult = (BaseIterator) result;
150 if (iterResult == null || !iterResult.MoveNext ())
152 return iterResult.Current.Value;
155 throw new XPathException ("invalid node type"); // TODO: handle other types
159 public bool EvaluateBoolean (BaseIterator iter)
161 object result = Evaluate (iter);
162 switch (GetReturnType (iter))
164 case XPathResultType.Number:
166 double num = (double) result;
167 return (num != 0.0 && num != -0.0 && num != Double.NaN);
169 case XPathResultType.Boolean:
170 return (bool) result;
171 case XPathResultType.String:
172 return ((string) result).Length != 0;
173 case XPathResultType.NodeSet:
175 BaseIterator iterResult = (BaseIterator) result;
176 return (iterResult != null && iterResult.MoveNext ());
179 throw new XPathException ("invalid node type"); // TODO: handle other types
184 internal abstract class ExprBinary : Expression
186 protected Expression _left, _right;
188 public ExprBinary (Expression left, Expression right)
193 public override String ToString ()
195 return _left.ToString () + ' ' + Operator + ' ' + _right.ToString ();
197 protected abstract String Operator { get; }
200 internal abstract class ExprBoolean : ExprBinary
202 public ExprBoolean (Expression left, Expression right) : base (left, right) {}
203 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
206 internal class ExprOR : ExprBoolean
208 public ExprOR (Expression left, Expression right) : base (left, right) {}
209 protected override String Operator { get { return "or"; }}
210 public override object Evaluate (BaseIterator iter)
212 if (_left.EvaluateBoolean (iter))
214 return _right.EvaluateBoolean (iter);
217 internal class ExprAND : ExprBoolean
219 public ExprAND (Expression left, Expression right) : base (left, right) {}
220 protected override String Operator { get { return "and"; }}
221 public override object Evaluate (BaseIterator iter)
223 if (!_left.EvaluateBoolean (iter))
225 return _right.EvaluateBoolean (iter);
229 internal abstract class EqualityExpr : ExprBoolean
231 public EqualityExpr (Expression left, Expression right) : base (left, right) {}
233 public override object Evaluate (BaseIterator iter)
235 XPathResultType typeL = _left.GetReturnType (iter);
236 XPathResultType typeR = _right.GetReturnType (iter);
237 if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
239 Expression left, right;
240 if (typeL != XPathResultType.NodeSet)
244 XPathResultType typeTmp = typeL;
253 if (typeR == XPathResultType.Boolean)
255 bool fL = left.EvaluateBoolean (iter);
256 bool fR = right.EvaluateBoolean (iter);
257 return Compare (Convert.ToDouble (fL), Convert.ToDouble (fR));
261 BaseIterator iterL = left.EvaluateNodeSet (iter);
262 if (typeR == XPathResultType.Number)
264 double dR = right.EvaluateNumber (iter);
265 while (iterL.MoveNext ())
266 if (Compare (XPathFunctions.ToNumber (iterL.Current.Value), dR))
269 else if (typeR == XPathResultType.String)
271 string strR = right.EvaluateString (iter);
272 while (iterL.MoveNext ())
273 if (Compare (iterL.Current.Value, strR))
276 else if (typeR == XPathResultType.NodeSet)
278 BaseIterator iterR = right.EvaluateNodeSet (iter);
279 ArrayList rgNodesL = new ArrayList ();
280 while (iterL.MoveNext ())
281 rgNodesL.Add (XPathFunctions.ToString (iterL.Current.Value));
282 while (iterR.MoveNext ())
284 string strR = XPathFunctions.ToString (iterR.Current.Value);
285 foreach (string strL in rgNodesL)
286 if (Compare (strL, strR))
293 else if (typeL == XPathResultType.Boolean || typeR == XPathResultType.Boolean)
294 return Compare (_left.EvaluateBoolean (iter), _right.EvaluateBoolean (iter));
295 else if (typeL == XPathResultType.Number || typeR == XPathResultType.Number)
296 return Compare (_left.EvaluateNumber (iter), _right.EvaluateNumber (iter));
298 return Compare (_left.EvaluateString (iter), _right.EvaluateString (iter));
301 public abstract bool Compare (object arg1, object arg2); // TODO: should probably have type-safe methods here
304 internal class ExprEQ : EqualityExpr
306 public ExprEQ (Expression left, Expression right) : base (left, right) {}
307 protected override String Operator { get { return "="; }}
308 public override bool Compare (object arg1, object arg2)
310 return arg1.Equals (arg2);
313 internal class ExprNE : EqualityExpr
315 public ExprNE (Expression left, Expression right) : base (left, right) {}
316 protected override String Operator { get { return "!="; }}
317 public override bool Compare (object arg1, object arg2)
319 return !arg1.Equals (arg2);
323 internal abstract class RelationalExpr : ExprBoolean
325 public RelationalExpr (Expression left, Expression right) : base (left, right) {}
327 public override object Evaluate (BaseIterator iter)
329 XPathResultType typeL = _left.GetReturnType (iter);
330 XPathResultType typeR = _right.GetReturnType (iter);
331 if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
333 bool fReverse = false;
334 Expression left, right;
335 if (typeL != XPathResultType.NodeSet)
340 XPathResultType typeTmp = typeL;
349 if (typeR == XPathResultType.Boolean)
351 bool fL = left.EvaluateBoolean (iter);
352 bool fR = right.EvaluateBoolean (iter);
353 return Compare (Convert.ToDouble (fL), Convert.ToDouble (fR), fReverse);
357 BaseIterator iterL = left.EvaluateNodeSet (iter);
358 if (typeR == XPathResultType.Number || typeR == XPathResultType.String)
360 double dR = right.EvaluateNumber (iter);
361 while (iterL.MoveNext ())
362 if (Compare (XPathFunctions.ToNumber (iterL.Current.Value), dR, fReverse))
365 else if (typeR == XPathResultType.NodeSet)
367 BaseIterator iterR = right.EvaluateNodeSet (iter);
368 ArrayList rgNodesL = new ArrayList ();
369 while (iterL.MoveNext ())
370 rgNodesL.Add (XPathFunctions.ToNumber (iterL.Current.Value));
371 while (iterR.MoveNext ())
373 double numR = XPathFunctions.ToNumber (iterR.Current.Value);
374 foreach (double numL in rgNodesL)
375 if (Compare (numL, numR))
383 return Compare (_left.EvaluateNumber (iter), _right.EvaluateNumber (iter));
385 public abstract bool Compare (double arg1, double arg2);
386 public bool Compare (double arg1, double arg2, bool fReverse)
389 return Compare (arg2, arg1);
391 return Compare (arg1, arg2);
394 internal class ExprGT : RelationalExpr
396 public ExprGT (Expression left, Expression right) : base (left, right) {}
397 protected override String Operator { get { return ">"; }}
398 public override bool Compare (double arg1, double arg2)
403 internal class ExprGE : RelationalExpr
405 public ExprGE (Expression left, Expression right) : base (left, right) {}
406 protected override String Operator { get { return ">="; }}
407 public override bool Compare (double arg1, double arg2)
412 internal class ExprLT : RelationalExpr
414 public ExprLT (Expression left, Expression right) : base (left, right) {}
415 protected override String Operator { get { return "<"; }}
416 public override bool Compare (double arg1, double arg2)
421 internal class ExprLE : RelationalExpr
423 public ExprLE (Expression left, Expression right) : base (left, right) {}
424 protected override String Operator { get { return "<="; }}
425 public override bool Compare (double arg1, double arg2)
432 internal abstract class ExprNumeric : ExprBinary
434 public ExprNumeric (Expression left, Expression right) : base (left, right) {}
435 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
438 internal class ExprPLUS : ExprNumeric
440 public ExprPLUS (Expression left, Expression right) : base (left, right) {}
441 protected override String Operator { get { return "+"; }}
442 public override object Evaluate (BaseIterator iter)
444 return _left.EvaluateNumber (iter) + _right.EvaluateNumber (iter);
447 internal class ExprMINUS : ExprNumeric
449 public ExprMINUS (Expression left, Expression right) : base (left, right) {}
450 protected override String Operator { get { return "-"; }}
451 public override object Evaluate (BaseIterator iter)
453 return _left.EvaluateNumber (iter) - _right.EvaluateNumber (iter);
456 internal class ExprMULT : ExprNumeric
458 public ExprMULT (Expression left, Expression right) : base (left, right) {}
459 protected override String Operator { get { return "*"; }}
460 public override object Evaluate (BaseIterator iter)
462 return _left.EvaluateNumber (iter) * _right.EvaluateNumber (iter);
465 internal class ExprDIV : ExprNumeric
467 public ExprDIV (Expression left, Expression right) : base (left, right) {}
468 protected override String Operator { get { return "/"; }}
469 public override object Evaluate (BaseIterator iter)
471 return _left.EvaluateNumber (iter) / _right.EvaluateNumber (iter);
474 internal class ExprMOD : ExprNumeric
476 public ExprMOD (Expression left, Expression right) : base (left, right) {}
477 protected override String Operator { get { return "%"; }}
479 public override object Evaluate (BaseIterator iter)
481 return _left.EvaluateNumber (iter) % _right.EvaluateNumber (iter); // TODO: spec?
484 internal class ExprNEG : Expression
487 public ExprNEG (Expression expr)
491 public override String ToString () { return "- " + _expr.ToString (); }
492 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
493 public override object Evaluate (BaseIterator iter)
495 return - _expr.EvaluateNumber (iter);
500 internal abstract class NodeSet : Expression
502 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
504 internal class ExprUNION : NodeSet
506 protected Expression _left, _right;
507 public ExprUNION (NodeSet left, NodeSet right)
512 public override String ToString () { return _left.ToString ()+ " | " + _right.ToString (); }
513 public override object Evaluate (BaseIterator iter)
515 BaseIterator iterLeft = _left.EvaluateNodeSet (iter);
516 BaseIterator iterRight = _right.EvaluateNodeSet (iter);
517 return new UnionIterator (iter, iterLeft, iterRight);
520 internal class ExprSLASH : NodeSet
522 protected Expression _left, _right;
523 public ExprSLASH (Expression left, NodeSet right)
528 public override String ToString () { return _left.ToString ()+ "/" + _right.ToString (); }
529 public override object Evaluate (BaseIterator iter)
531 BaseIterator iterLeft = _left.EvaluateNodeSet (iter);
532 return new SlashIterator (iterLeft, _right);
535 internal class ExprRoot : NodeSet
537 public override String ToString () { return ""; }
538 public override object Evaluate (BaseIterator iter)
540 XPathNavigator navRoot = iter.Current.Clone ();
541 navRoot.MoveToRoot ();
542 return new SelfIterator (navRoot, iter.Context);
563 internal class AxisSpecifier
565 protected Axes _axis;
566 public AxisSpecifier (Axes axis)
570 public XPathNodeType NodeType
577 return XPathNodeType.Namespace;
579 return XPathNodeType.Attribute;
581 return XPathNodeType.Element;
585 public override string ToString ()
591 case Axes.AncestorOrSelf:
592 return "ancestor-or-self";
597 case Axes.Descendant:
599 case Axes.DescendantOrSelf:
600 return "descendant-or-self";
603 case Axes.FollowingSibling:
604 return "following-sibling";
611 case Axes.PrecedingSibling:
612 return "preceeding-sibling";
616 throw new IndexOutOfRangeException ();
619 public Axes Axis { get { return _axis; }}
620 public virtual BaseIterator Evaluate (BaseIterator iter)
625 return new AncestorIterator (iter);
626 case Axes.AncestorOrSelf:
627 return new AncestorOrSelfIterator (iter);
629 return new AttributeIterator (iter);
631 return new ChildIterator (iter);
632 case Axes.Descendant:
633 return new DescendantIterator (iter);
634 case Axes.DescendantOrSelf:
635 return new DescendantOrSelfIterator (iter);
637 return new FollowingIterator (iter);
638 case Axes.FollowingSibling:
639 return new FollowingSiblingIterator (iter);
641 return new NamespaceIterator (iter);
643 return new ParentIterator (iter);
645 return new PrecedingIterator (iter);
646 case Axes.PrecedingSibling:
647 return new PrecedingSiblingIterator (iter);
649 return new SelfIterator (iter);
651 throw new IndexOutOfRangeException ();
656 internal abstract class NodeTest
658 protected AxisSpecifier _axis;
659 public NodeTest (AxisSpecifier axis)
663 public NodeTest (Axes axis)
665 _axis = new AxisSpecifier (axis);
667 public abstract bool Match (XsltContext context, XPathNavigator nav);
668 public AxisSpecifier Axis { get { return _axis; }}
669 public virtual BaseIterator Evaluate (BaseIterator iter)
671 BaseIterator iterAxis = _axis.Evaluate (iter);
672 return new AxisIterator (iterAxis, this);
676 internal class NodeTypeTest : NodeTest
678 protected XPathNodeType _type;
679 protected String _param;
680 public NodeTypeTest (Axes axis) : base (axis)
682 _type = _axis.NodeType;
684 public NodeTypeTest (Axes axis, XPathNodeType type) : base (axis)
689 public NodeTypeTest (Axes axis, XPathNodeType type, String param) : base (axis)
693 if (param != null && type != XPathNodeType.ProcessingInstruction)
694 throw new XPathException ("No argument allowed for "+ToString (_type)+"() test"); // TODO: better description
696 public override String ToString ()
698 String strType = ToString (_type);
699 if (_type == XPathNodeType.ProcessingInstruction && _param != null)
700 strType += "('" + _param + "')";
704 return _axis.ToString () + "::" + strType;
706 private static String ToString (XPathNodeType type)
710 case XPathNodeType.Comment:
712 case XPathNodeType.Text:
714 case XPathNodeType.ProcessingInstruction:
715 return "processing-instruction";
716 case XPathNodeType.All:
717 case XPathNodeType.Attribute:
718 case XPathNodeType.Element:
721 throw new NotImplementedException ();
724 public override bool Match (XsltContext context, XPathNavigator nav)
726 XPathNodeType nodeType = nav.NodeType;
729 case XPathNodeType.All:
732 case XPathNodeType.ProcessingInstruction:
733 if (nodeType != XPathNodeType.ProcessingInstruction)
735 if (_param != null && nav.Name != _param)
740 return _type == nodeType;
744 internal class NodeNameTest : NodeTest
746 protected QName _name;
747 public NodeNameTest (Axes axis, QName name) : base (axis)
751 public override String ToString () { return _axis.ToString () + "::" + _name.ToString (); }
753 public override bool Match (XsltContext context, XPathNavigator nav)
755 // must be the correct node type
756 if (nav.NodeType != _axis.NodeType)
759 if (_name.Local != null && _name.Local != "")
761 // test the local part of the name first
762 if (_name.Local != nav.LocalName)
766 // get the prefix for the given name
768 if (_name.Prefix != null)
770 strURI1 = context.LookupNamespace (_name.Prefix); // TODO: check to see if this returns null or ""
772 throw new XPathException ("Invalid namespace prefix: "+_name.Prefix);
775 string strURI = nav.NamespaceURI;
776 if (strURI == null && strURI1 == "") // TODO: remove when bug #26855 fixed
780 return strURI1 == nav.NamespaceURI;
784 internal class ExprStep : NodeSet
786 protected NodeTest _test;
787 protected Expression [] _preds;
788 public ExprStep (NodeTest test, ExprPredicates preds)
792 _preds = preds.GetPredicates ();
794 public ExprStep (NodeTest test)
798 public override String ToString ()
800 String strExpr = _test.ToString ();
803 foreach (Expression pred in _preds)
805 strExpr += '[' + pred.ToString () + ']';
810 public override object Evaluate (BaseIterator iter)
812 BaseIterator iterStep = _test.Evaluate (iter);
816 return new PredicateIterator (iterStep, _preds);
821 internal class ExprPredicates
823 protected Expression _pred;
824 protected ExprPredicates _tail;
825 public ExprPredicates (Expression pred, ExprPredicates tail)
830 public ExprPredicates (Expression pred)
834 public Expression [] GetPredicates ()
836 ArrayList lstPreds = new ArrayList ();
837 ExprPredicates curr = this;
840 lstPreds.Add (curr._pred);
843 return (Expression []) lstPreds.ToArray (typeof (Expression));
847 internal class ExprFilter : Expression
849 protected Expression _expr;
850 protected Expression _pred;
851 public ExprFilter (Expression expr, Expression pred)
856 public override String ToString () { return "(" + _expr.ToString () + ")[" + _pred.ToString () + "]"; }
857 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
862 protected String _prefix;
863 protected String _local;
864 public QName (String prefix, String local)
869 public override String ToString ()
871 String strLocal = (_local != null) ? _local : "*";
873 return _prefix + ':' + strLocal;
876 public String Prefix { get { return _prefix; } }
877 public String Local { get { return _local; } }
879 internal class NCName : QName
881 public NCName (String local) : base (null, local) {}
884 internal class ExprNumber : Expression
886 protected double _value;
887 public ExprNumber (double value)
891 public override String ToString () { return _value.ToString (); }
892 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
893 public override object Evaluate (BaseIterator iter)
898 internal class ExprLiteral : Expression
900 protected String _value;
901 public ExprLiteral (String value)
905 public override String ToString () { return "'" + _value + "'"; }
906 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
907 public override object Evaluate (BaseIterator iter)
913 internal class ExprVariable : Expression
915 protected QName _name;
916 public ExprVariable (QName name)
920 public override String ToString () { return "$" + _name.ToString (); }
921 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
922 public override XPathResultType GetReturnType (BaseIterator iter)
924 IXsltContextVariable var = iter.Context.ResolveVariable (_name.Prefix, _name.Local);
925 return var.VariableType;
929 internal class FunctionArguments
931 protected Expression _arg;
932 protected FunctionArguments _tail;
933 public FunctionArguments (Expression arg, FunctionArguments tail)
938 public Expression Arg
942 public FunctionArguments Tail
944 get { return _tail; }
947 internal class ExprFunctionCall : Expression
949 protected QName _name;
950 protected ArrayList _args = new ArrayList ();
951 public ExprFunctionCall (String name, FunctionArguments args)
953 _name = new NCName (name);
956 _args.Add (args.Arg);
960 public override String ToString ()
963 foreach (Expression arg in _args)
967 strArgs += arg.ToString ();
969 return _name.ToString () + '(' + strArgs + ')';
971 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
972 public override XPathResultType GetReturnType (BaseIterator iter)
974 IXsltContextFunction func = iter.Context.ResolveFunction (_name.Prefix, _name.Local, GetArgTypes (iter));
975 return func.ReturnType;
977 private XPathResultType [] GetArgTypes (BaseIterator iter)
979 // TODO: can we cache these? what if the types depend on the context?
980 XPathResultType [] rgArgs = new XPathResultType [_args.Count];
981 for (int iArg = 0; iArg < _args.Count; iArg++)
982 rgArgs [iArg] = ((Expression) _args [iArg]).GetReturnType (iter);
985 public override object Evaluate (BaseIterator iter)
987 //special-case the 'last' and 'position' functions
988 if (_args.Count == 0 && _name.Prefix == null)
990 if (_name.Local == "last")
992 return (double) iter.Count;
994 else if (_name.Local == "position")
996 return (double) iter.CurrentPosition;
1000 XPathResultType [] rgTypes = GetArgTypes (iter);
1001 //FIXME: what if func == null after next line?
1002 IXsltContextFunction func = iter.Context.ResolveFunction (_name.Prefix, _name.Local, rgTypes);
1003 object [] rgArgs = new object [_args.Count];
1004 for (int iArg = 0; iArg < _args.Count; iArg ++)
1005 rgArgs [iArg] = ((Expression) _args [iArg]).Evaluate (iter);
1006 return func.Invoke (iter.Context, rgArgs, iter.Current);