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)
61 return _expr.Evaluate (iter);
63 public XPathNodeIterator EvaluateNodeSet (BaseIterator iter)
65 return _expr.EvaluateNodeSet (iter);
71 /// Summary description for Expression.
73 internal abstract class Expression
78 public abstract XPathResultType ReturnType { get; }
79 public virtual XPathResultType GetReturnType (BaseIterator iter) { return ReturnType; }
80 public virtual object Evaluate (BaseIterator iter) { return null; }
82 public BaseIterator EvaluateNodeSet (BaseIterator iter)
84 if (GetReturnType (iter) == XPathResultType.NodeSet)
85 return (BaseIterator) Evaluate (iter);
86 throw new Exception ("expected nodeset: "+ToString ());
89 public double EvaluateNumber (BaseIterator iter)
92 XPathResultType type = GetReturnType (iter);
93 if (type == XPathResultType.NodeSet)
95 result = EvaluateString (iter);
96 type = XPathResultType.String;
99 result = Evaluate (iter);
103 case XPathResultType.Number:
104 return (double) result;
105 case XPathResultType.Boolean:
106 return Convert.ToDouble ((bool) result);
107 case XPathResultType.String:
108 return XmlConvert.ToDouble ((string) result); // TODO: spec? convert string to number
110 throw new Exception (); // TODO: handle other types
114 public string EvaluateString (BaseIterator iter)
116 object result = Evaluate (iter);
117 switch (GetReturnType (iter))
119 case XPathResultType.Number:
120 return (string) XmlConvert.ToString ((double) result); // TODO: spec? convert number to string
121 case XPathResultType.Boolean:
122 return ((bool) result) ? "true" : "false";
123 case XPathResultType.String:
124 return (string) result;
125 case XPathResultType.NodeSet:
127 BaseIterator iterResult = (BaseIterator) result;
128 if (iterResult == null || !iterResult.MoveNext ())
130 return iterResult.Current.Value;
133 throw new Exception (); // TODO: handle other types
137 public bool EvaluateBoolean (BaseIterator iter)
139 object result = Evaluate (iter);
140 switch (GetReturnType (iter))
142 case XPathResultType.Number:
144 double num = (double) result;
145 return (num != 0.0 && num != -0.0 && num != Double.NaN);
147 case XPathResultType.Boolean:
148 return (bool) result;
149 case XPathResultType.String:
150 return ((string) result).Length != 0;
151 case XPathResultType.NodeSet:
153 BaseIterator iterResult = (BaseIterator) result;
154 return (iterResult != null && iterResult.MoveNext ());
157 throw new Exception (); // TODO: handle other types
162 internal abstract class ExprBinary : Expression
164 protected Expression _left, _right;
166 public ExprBinary (Expression left, Expression right)
171 public override String ToString ()
173 return _left.ToString () + ' ' + Operator + ' ' + _right.ToString ();
175 protected abstract String Operator { get; }
178 internal abstract class ExprBoolean : ExprBinary
180 public ExprBoolean (Expression left, Expression right) : base (left, right) {}
181 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
184 internal class ExprOR : ExprBoolean
186 public ExprOR (Expression left, Expression right) : base (left, right) {}
187 protected override String Operator { get { return "or"; }}
188 public override object Evaluate (BaseIterator iter)
190 if (_left.EvaluateBoolean (iter))
192 return _right.EvaluateBoolean (iter);
195 internal class ExprAND : ExprBoolean
197 public ExprAND (Expression left, Expression right) : base (left, right) {}
198 protected override String Operator { get { return "and"; }}
199 public override object Evaluate (BaseIterator iter)
201 if (!_left.EvaluateBoolean (iter))
203 return _right.EvaluateBoolean (iter);
207 internal abstract class EqualityExpr : ExprBoolean
209 public EqualityExpr (Expression left, Expression right) : base (left, right) {}
211 public override object Evaluate (BaseIterator iter)
213 XPathResultType typeL = _left.GetReturnType (iter);
214 XPathResultType typeR = _right.GetReturnType (iter);
215 if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
217 Expression left, right;
218 if (typeL != XPathResultType.NodeSet)
222 XPathResultType typeTmp = typeL;
231 if (typeR == XPathResultType.Boolean)
233 bool fL = left.EvaluateBoolean (iter);
234 bool fR = right.EvaluateBoolean (iter);
235 return Compare (Convert.ToDouble (fL), Convert.ToDouble (fR));
239 BaseIterator iterL = left.EvaluateNodeSet (iter);
240 if (typeR == XPathResultType.Number)
242 double dR = right.EvaluateNumber (iter);
243 while (iterL.MoveNext ())
244 if (Compare (XPathFunctions.ToNumber (iterL.Current.Value), dR))
247 else if (typeR == XPathResultType.String)
249 string strR = right.EvaluateString (iter);
250 while (iterL.MoveNext ())
251 if (Compare (iterL.Current.Value, strR))
254 else if (typeR == XPathResultType.NodeSet)
256 BaseIterator iterR = right.EvaluateNodeSet (iter);
257 ArrayList rgNodesL = new ArrayList ();
258 while (iterL.MoveNext ())
259 rgNodesL.Add (XPathFunctions.ToString (iterL.Current.Value));
260 while (iterR.MoveNext ())
262 string strR = XPathFunctions.ToString (iterR.Current.Value);
263 foreach (string strL in rgNodesL)
264 if (Compare (strL, strR))
271 else if (typeL == XPathResultType.Boolean || typeR == XPathResultType.Boolean)
272 return Compare (_left.EvaluateBoolean (iter), _right.EvaluateBoolean (iter));
273 else if (typeL == XPathResultType.Number || typeR == XPathResultType.Number)
274 return Compare (_left.EvaluateNumber (iter), _right.EvaluateNumber (iter));
276 return Compare (_left.EvaluateString (iter), _right.EvaluateString (iter));
279 public abstract bool Compare (object arg1, object arg2); // TODO: should probably have type-safe methods here
282 internal class ExprEQ : EqualityExpr
284 public ExprEQ (Expression left, Expression right) : base (left, right) {}
285 protected override String Operator { get { return "="; }}
286 public override bool Compare (object arg1, object arg2)
288 return arg1.Equals (arg2);
291 internal class ExprNE : EqualityExpr
293 public ExprNE (Expression left, Expression right) : base (left, right) {}
294 protected override String Operator { get { return "!="; }}
295 public override bool Compare (object arg1, object arg2)
297 return !arg1.Equals (arg2);
301 internal abstract class RelationalExpr : ExprBoolean
303 public RelationalExpr (Expression left, Expression right) : base (left, right) {}
305 public override object Evaluate (BaseIterator iter)
307 XPathResultType typeL = _left.GetReturnType (iter);
308 XPathResultType typeR = _right.GetReturnType (iter);
309 if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
311 bool fReverse = false;
312 Expression left, right;
313 if (typeL != XPathResultType.NodeSet)
318 XPathResultType typeTmp = typeL;
327 if (typeR == XPathResultType.Boolean)
329 bool fL = left.EvaluateBoolean (iter);
330 bool fR = right.EvaluateBoolean (iter);
331 return Compare (Convert.ToDouble (fL), Convert.ToDouble (fR), fReverse);
335 BaseIterator iterL = left.EvaluateNodeSet (iter);
336 if (typeR == XPathResultType.Number || typeR == XPathResultType.String)
338 double dR = right.EvaluateNumber (iter);
339 while (iterL.MoveNext ())
340 if (Compare (XPathFunctions.ToNumber (iterL.Current.Value), dR, fReverse))
343 else if (typeR == XPathResultType.NodeSet)
345 BaseIterator iterR = right.EvaluateNodeSet (iter);
346 ArrayList rgNodesL = new ArrayList ();
347 while (iterL.MoveNext ())
348 rgNodesL.Add (XPathFunctions.ToNumber (iterL.Current.Value));
349 while (iterR.MoveNext ())
351 double numR = XPathFunctions.ToNumber (iterR.Current.Value);
352 foreach (double numL in rgNodesL)
353 if (Compare (numL, numR))
361 return Compare (_left.EvaluateNumber (iter), _right.EvaluateNumber (iter));
363 public abstract bool Compare (double arg1, double arg2);
364 public bool Compare (double arg1, double arg2, bool fReverse)
367 return Compare (arg2, arg1);
369 return Compare (arg1, arg2);
372 internal class ExprGT : RelationalExpr
374 public ExprGT (Expression left, Expression right) : base (left, right) {}
375 protected override String Operator { get { return ">"; }}
376 public override bool Compare (double arg1, double arg2)
381 internal class ExprGE : RelationalExpr
383 public ExprGE (Expression left, Expression right) : base (left, right) {}
384 protected override String Operator { get { return ">="; }}
385 public override bool Compare (double arg1, double arg2)
390 internal class ExprLT : RelationalExpr
392 public ExprLT (Expression left, Expression right) : base (left, right) {}
393 protected override String Operator { get { return "<"; }}
394 public override bool Compare (double arg1, double arg2)
399 internal class ExprLE : RelationalExpr
401 public ExprLE (Expression left, Expression right) : base (left, right) {}
402 protected override String Operator { get { return "<="; }}
403 public override bool Compare (double arg1, double arg2)
410 internal abstract class ExprNumeric : ExprBinary
412 public ExprNumeric (Expression left, Expression right) : base (left, right) {}
413 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
416 internal class ExprPLUS : ExprNumeric
418 public ExprPLUS (Expression left, Expression right) : base (left, right) {}
419 protected override String Operator { get { return "+"; }}
420 public override object Evaluate (BaseIterator iter)
422 return _left.EvaluateNumber (iter) + _right.EvaluateNumber (iter);
425 internal class ExprMINUS : ExprNumeric
427 public ExprMINUS (Expression left, Expression right) : base (left, right) {}
428 protected override String Operator { get { return "-"; }}
429 public override object Evaluate (BaseIterator iter)
431 return _left.EvaluateNumber (iter) - _right.EvaluateNumber (iter);
434 internal class ExprMULT : ExprNumeric
436 public ExprMULT (Expression left, Expression right) : base (left, right) {}
437 protected override String Operator { get { return "*"; }}
438 public override object Evaluate (BaseIterator iter)
440 return _left.EvaluateNumber (iter) * _right.EvaluateNumber (iter);
443 internal class ExprDIV : ExprNumeric
445 public ExprDIV (Expression left, Expression right) : base (left, right) {}
446 protected override String Operator { get { return "/"; }}
447 public override object Evaluate (BaseIterator iter)
449 return _left.EvaluateNumber (iter) / _right.EvaluateNumber (iter);
452 internal class ExprMOD : ExprNumeric
454 public ExprMOD (Expression left, Expression right) : base (left, right) {}
455 protected override String Operator { get { return "%"; }}
457 public override object Evaluate (BaseIterator iter)
459 return _left.EvaluateNumber (iter) % _right.EvaluateNumber (iter); // TODO: spec?
462 internal class ExprNEG : Expression
465 public ExprNEG (Expression expr)
469 public override String ToString () { return "- " + _expr.ToString (); }
470 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
471 public override object Evaluate (BaseIterator iter)
473 return - _expr.EvaluateNumber (iter);
478 internal abstract class NodeSet : Expression
480 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
482 internal class ExprUNION : NodeSet
484 protected Expression _left, _right;
485 public ExprUNION (NodeSet left, NodeSet right)
490 public override String ToString () { return _left.ToString ()+ " | " + _right.ToString (); }
491 public override object Evaluate (BaseIterator iter)
493 BaseIterator iterLeft = _left.EvaluateNodeSet (iter);
494 BaseIterator iterRight = _right.EvaluateNodeSet (iter);
495 return new UnionIterator (iter, iterLeft, iterRight);
498 internal class ExprSLASH : NodeSet
500 protected Expression _left, _right;
501 public ExprSLASH (NodeSet left, NodeSet right)
506 public override String ToString () { return _left.ToString ()+ "/" + _right.ToString (); }
507 public override object Evaluate (BaseIterator iter)
509 BaseIterator iterLeft = _left.EvaluateNodeSet (iter);
510 return new SlashIterator (iterLeft, _right);
513 internal class ExprRoot : NodeSet
515 public override String ToString () { return ""; }
516 public override object Evaluate (BaseIterator iter)
518 XPathNavigator navRoot = iter.Current.Clone ();
519 navRoot.MoveToRoot ();
520 return new SelfIterator (navRoot, iter.Context);
541 internal class AxisSpecifier
543 protected Axes _axis;
544 public AxisSpecifier (Axes axis)
548 public XPathNodeType NodeType
555 return XPathNodeType.Namespace;
557 return XPathNodeType.Attribute;
559 return XPathNodeType.Element;
563 public override string ToString ()
569 case Axes.AncestorOrSelf:
570 return "ancestor-or-self";
575 case Axes.Descendant:
577 case Axes.DescendantOrSelf:
578 return "descendant-or-self";
581 case Axes.FollowingSibling:
582 return "following-sibling";
589 case Axes.PrecedingSibling:
590 return "preceeding-sibling";
594 throw new IndexOutOfRangeException ();
597 public Axes Axis { get { return _axis; }}
598 public virtual BaseIterator Evaluate (BaseIterator iter)
603 return new AncestorIterator (iter);
604 case Axes.AncestorOrSelf:
605 return new AncestorOrSelfIterator (iter);
607 return new AttributeIterator (iter);
609 return new ChildIterator (iter);
610 case Axes.Descendant:
611 return new DescendantIterator (iter);
612 case Axes.DescendantOrSelf:
613 return new DescendantOrSelfIterator (iter);
615 return new FollowingIterator (iter);
616 case Axes.FollowingSibling:
617 return new FollowingSiblingIterator (iter);
619 return new NamespaceIterator (iter);
621 return new ParentIterator (iter);
623 return new PrecedingIterator (iter);
624 case Axes.PrecedingSibling:
625 return new PrecedingSiblingIterator (iter);
627 return new SelfIterator (iter);
629 throw new IndexOutOfRangeException ();
634 internal abstract class NodeTest
636 protected AxisSpecifier _axis;
637 public NodeTest (AxisSpecifier axis)
641 public NodeTest (Axes axis)
643 _axis = new AxisSpecifier (axis);
645 public abstract bool Match (XsltContext context, XPathNavigator nav);
646 public AxisSpecifier Axis { get { return _axis; }}
647 public virtual BaseIterator Evaluate (BaseIterator iter)
649 BaseIterator iterAxis = _axis.Evaluate (iter);
650 return new AxisIterator (iterAxis, this);
654 internal class NodeTypeTest : NodeTest
656 protected XPathNodeType _type;
657 protected String _param;
658 public NodeTypeTest (Axes axis) : base (axis)
660 _type = _axis.NodeType;
662 public NodeTypeTest (Axes axis, XPathNodeType type) : base (axis)
667 public NodeTypeTest (Axes axis, XPathNodeType type, String param) : base (axis)
671 if (param != null && type != XPathNodeType.ProcessingInstruction)
672 throw new Exception ("No argument allowed for "+ToString (_type)+"() test"); // TODO: better description
674 public override String ToString ()
676 String strType = ToString (_type);
677 if (_type == XPathNodeType.ProcessingInstruction && _param != null)
678 strType += "('" + _param + "')";
682 return _axis.ToString () + "::" + strType;
684 private static String ToString (XPathNodeType type)
688 case XPathNodeType.Comment:
690 case XPathNodeType.Text:
692 case XPathNodeType.ProcessingInstruction:
693 return "processing-instruction";
694 case XPathNodeType.All:
695 case XPathNodeType.Attribute:
696 case XPathNodeType.Element:
699 throw new NotImplementedException ();
702 public override bool Match (XsltContext context, XPathNavigator nav)
704 XPathNodeType nodeType = nav.NodeType;
707 case XPathNodeType.All:
710 case XPathNodeType.ProcessingInstruction:
711 if (nodeType != XPathNodeType.ProcessingInstruction)
713 if (_param != null && nav.Name != _param)
718 return _type == nodeType;
722 internal class NodeNameTest : NodeTest
724 protected QName _name;
725 public NodeNameTest (Axes axis, QName name) : base (axis)
729 public override String ToString () { return _axis.ToString () + "::" + _name.ToString (); }
731 public override bool Match (XsltContext context, XPathNavigator nav)
733 // must be the correct node type
734 if (nav.NodeType != _axis.NodeType)
737 if (_name.Local != null && _name.Local != "")
739 // test the local part of the name first
740 if (_name.Local != nav.LocalName)
744 // get the prefix for the given name
746 if (_name.Prefix != null)
748 strURI1 = context.LookupNamespace (_name.Prefix); // TODO: check to see if this returns null or ""
750 throw new Exception ("Invalid namespace prefix: "+_name.Prefix);
753 string strURI = nav.NamespaceURI;
754 if (strURI == null && strURI1 == "") // TODO: remove when bug #26855 fixed
758 return strURI1 == nav.NamespaceURI;
762 internal class ExprStep : NodeSet
764 protected NodeTest _test;
765 protected Expression [] _preds;
766 public ExprStep (NodeTest test, ExprPredicates preds)
770 _preds = preds.GetPredicates ();
772 public ExprStep (NodeTest test)
776 public override String ToString ()
778 String strExpr = _test.ToString ();
781 foreach (Expression pred in _preds)
783 strExpr += '[' + pred.ToString () + ']';
788 public override object Evaluate (BaseIterator iter)
790 BaseIterator iterStep = _test.Evaluate (iter);
794 return new PredicateIterator (iterStep, _preds);
799 internal class ExprPredicates
801 protected Expression _pred;
802 protected ExprPredicates _tail;
803 public ExprPredicates (Expression pred, ExprPredicates tail)
808 public ExprPredicates (Expression pred)
812 public Expression [] GetPredicates ()
814 ArrayList lstPreds = new ArrayList ();
815 ExprPredicates curr = this;
818 lstPreds.Add (curr._pred);
821 return (Expression []) lstPreds.ToArray (typeof (Expression));
825 internal class ExprFilter : Expression
827 protected Expression _expr;
828 protected Expression _pred;
829 public ExprFilter (Expression expr, Expression pred)
834 public override String ToString () { return "(" + _expr.ToString () + ")[" + _pred.ToString () + "]"; }
835 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
840 protected String _prefix;
841 protected String _local;
842 public QName (String prefix, String local)
847 public override String ToString ()
849 String strLocal = (_local != null) ? _local : "*";
851 return _prefix + ':' + strLocal;
854 public String Prefix { get { return _prefix; } }
855 public String Local { get { return _local; } }
857 internal class NCName : QName
859 public NCName (String local) : base (null, local) {}
862 internal class ExprNumber : Expression
864 protected double _value;
865 public ExprNumber (double value)
869 public override String ToString () { return _value.ToString (); }
870 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
871 public override object Evaluate (BaseIterator iter)
876 internal class ExprLiteral : Expression
878 protected String _value;
879 public ExprLiteral (String value)
883 public override String ToString () { return "'" + _value + "'"; }
884 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
885 public override object Evaluate (BaseIterator iter)
891 internal class ExprVariable : Expression
893 protected QName _name;
894 public ExprVariable (QName name)
898 public override String ToString () { return "$" + _name.ToString (); }
899 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
900 public override XPathResultType GetReturnType (BaseIterator iter)
902 IXsltContextVariable var = iter.Context.ResolveVariable (_name.Prefix, _name.Local);
903 return var.VariableType;
907 internal class FunctionArguments
909 protected Expression _arg;
910 protected FunctionArguments _tail;
911 public FunctionArguments (Expression arg, FunctionArguments tail)
916 public Expression Arg
920 public FunctionArguments Tail
922 get { return _tail; }
925 internal class ExprFunctionCall : Expression
927 protected QName _name;
928 protected ArrayList _args = new ArrayList ();
929 public ExprFunctionCall (String name, FunctionArguments args)
931 _name = new NCName (name);
934 _args.Add (args.Arg);
938 public override String ToString ()
941 foreach (Expression arg in _args)
945 strArgs += arg.ToString ();
947 return _name.ToString () + '(' + strArgs + ')';
949 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
950 public override XPathResultType GetReturnType (BaseIterator iter)
952 IXsltContextFunction func = iter.Context.ResolveFunction (_name.Prefix, _name.Local, GetArgTypes (iter));
953 return func.ReturnType;
955 private XPathResultType [] GetArgTypes (BaseIterator iter)
957 // TODO: can we cache these? what if the types depend on the context?
958 XPathResultType [] rgArgs = new XPathResultType [_args.Count];
959 for (int iArg = 0; iArg < _args.Count; iArg++)
960 rgArgs [iArg] = ((Expression) _args [iArg]).GetReturnType (iter);
963 public override object Evaluate (BaseIterator iter)
965 //special-case the 'last' and 'position' functions
966 if (_args.Count == 0 && _name.Prefix == null)
968 if (_name.Local == "last")
970 return (double) iter.Count;
972 else if (_name.Local == "position")
974 return (double) iter.CurrentPosition;
978 XPathResultType [] rgTypes = GetArgTypes (iter);
979 IXsltContextFunction func = iter.Context.ResolveFunction (_name.Prefix, _name.Local, rgTypes);
980 object [] rgArgs = new object [_args.Count];
981 for (int iArg = 0; iArg < _args.Count; iArg ++)
982 rgArgs [iArg] = ((Expression) _args [iArg]).Evaluate (iter);
983 return func.Invoke (iter.Context, rgArgs, iter.Current);