2 // XQueryFunctionCliImple.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // Runtime-level (native) implementation of XQuery 1.0 and XPath 2.0
32 // Functions implementation. XQueryCliFunction
33 // See XQuery 1.0 and XPath 2.0 Functions and Operators.
37 using System.Collections;
38 using System.Globalization;
40 using System.Text.RegularExpressions;
42 using System.Xml.Query;
43 using System.Xml.Schema;
44 using System.Xml.XPath;
46 namespace Mono.Xml.XPath2
48 public class XQueryFunctionCliImpl
50 internal static XmlSchemaType XmlTypeFromCliType (Type cliType)
52 switch (Type.GetTypeCode (cliType)) {
54 return XmlSchemaSimpleType.XsInt;
55 case TypeCode.Decimal:
56 return XmlSchemaSimpleType.XsDecimal;
58 return XmlSchemaSimpleType.XsDouble;
60 return XmlSchemaSimpleType.XsFloat;
62 return XmlSchemaSimpleType.XsLong;
64 return XmlSchemaSimpleType.XsShort;
66 return XmlSchemaSimpleType.XsUnsignedShort;
68 return XmlSchemaSimpleType.XsUnsignedInt;
70 return XmlSchemaSimpleType.XsString;
71 case TypeCode.DateTime:
72 return XmlSchemaSimpleType.XsDateTime;
73 case TypeCode.Boolean:
74 return XmlSchemaSimpleType.XsBoolean;
76 if (cliType == typeof (XmlQualifiedName))
77 return XmlSchemaSimpleType.XsQName;
81 private static XPathItem ToItem (object arg)
85 XPathItem item = arg as XPathItem;
88 XPathSequence seq = arg as XPathSequence;
90 return seq.MoveNext () ? seq.Current : null;
91 return new XPathAtomicValue (arg, XmlTypeFromCliType (arg.GetType ()));
96 public static XmlQualifiedName FnNodeName (XPathNavigator arg)
101 return arg.LocalName == String.Empty ?
102 XmlQualifiedName.Empty :
103 new XmlQualifiedName (arg.LocalName, arg.NamespaceURI);
106 public static bool FnNilled (XPathNavigator arg)
109 throw new XmlQueryException ("Function nilled() does not allow empty sequence parameter.");
111 IXmlSchemaInfo info = arg.NodeType == XPathNodeType.Element ? arg.SchemaInfo : null;
112 return info != null && info.IsNil;
115 public static string FnString (XQueryContext context)
117 XPathItem item = context.CurrentItem;
119 throw new ArgumentException ("FONC0001: undefined context item");
120 return FnString (item);
124 public static string FnString (object arg)
128 XPathNavigator nav = arg as XPathNavigator;
131 // FIXME: it should be exactly the same as "arg cast as xs:string"
132 XPathItem item = ToItem (arg);
133 return item != null ? XQueryConvert.ItemToString (item) : null;
137 public static XPathAtomicValue FnData (object arg)
139 // FIXME: parameter should be object []
140 XPathNavigator nav = arg as XPathNavigator;
142 XmlSchemaType st = nav.SchemaInfo != null ? nav.SchemaInfo.SchemaType : null;
143 return new XPathAtomicValue (nav.TypedValue, st != null ? st : XmlSchemaComplexType.AnyType);
146 return (XPathAtomicValue) arg;
149 public static string FnBaseUri (XPathNavigator nav)
151 return nav != null ? nav.BaseURI : null;
154 public static string FnDocumentUri (XPathNavigator nav)
158 XPathNavigator root = nav.Clone ();
166 public static void FnError (object arg)
168 throw new NotImplementedException ();
174 public static object FnTrace (XQueryContext ctx, object value, string label)
177 return new XPathEmptySequence (ctx);
178 XPathSequence seq = value as XPathSequence;
180 XPathAtomicValue av = value as XPathAtomicValue;
182 av = new XPathAtomicValue (value,
183 XmlSchemaType.GetBuiltInType (
184 XPathAtomicValue.XmlTypeCodeFromRuntimeType (
185 value.GetType (), true)));
186 seq = new SingleItemIterator (av, ctx);
188 return new TracingIterator (seq, label);
194 public static object FnAbs (object arg)
197 return System.Math.Abs ((int) arg);
199 return System.Math.Abs ((long) arg);
200 else if (arg is decimal)
201 return System.Math.Abs ((decimal) arg);
202 else if (arg is double)
203 return System.Math.Abs ((double) arg);
204 else if (arg is float)
205 return System.Math.Abs ((float) arg);
206 else if (arg is short)
207 return System.Math.Abs ((short) arg);
208 else if (arg is uint || arg is ulong || arg is ushort)
214 public static object FnCeiling (object arg)
216 if (arg is decimal) {
217 decimal d = (decimal) arg;
218 decimal d2 = Decimal.Floor (d);
219 return d2 != d ? d2 + 1 : d2;
221 else if (arg is double || arg is float)
222 return System.Math.Ceiling ((double) arg);
223 else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
229 public static object FnFloor (object arg)
232 return Decimal.Floor ((decimal) arg);
233 else if (arg is double || arg is float)
234 return System.Math.Floor ((double) arg);
235 else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
241 public static object FnRound (object arg)
244 return Decimal.Round ((decimal) arg, 0);
245 else if (arg is double || arg is float)
246 return System.Math.Round ((double) arg);
247 else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
253 public static object FnRoundHalfToEven (object arg)
255 throw new NotImplementedException ();
259 public static string FnCodepointsToString (int [] arg)
261 throw new NotImplementedException ();
265 public static int [] FnStringToCodepoints (string arg)
267 throw new NotImplementedException ();
270 public static int FnCompare (XQueryContext ctx, string s1, string s2)
272 return FnCompare (s1, s2, ctx.DefaultCollation);
275 public static int FnCompare (XQueryContext ctx, string s1, string s2, string collation)
277 return FnCompare (s1, s2, ctx.GetCulture (collation));
280 private static int FnCompare (string s1, string s2, CultureInfo ci)
282 return ci.CompareInfo.Compare (s1, s2);
285 public static string FnConcat (object o1, object o2)
287 return String.Concat (o1, o2);
290 public static string FnStringJoin (string [] strings, string separator)
292 return String.Join (separator, strings);
295 public static string FnSubstring (string src, double loc)
297 return src.Substring ((int) loc);
300 public static string FnSubstring (string src, double loc, double length)
302 return src.Substring ((int) loc, (int) length);
305 public static int FnStringLength (XQueryContext ctx)
307 return FnString (ctx).Length;
310 public static int FnStringLength (string s)
315 public static string FnNormalizeSpace (XQueryContext ctx)
317 return FnNormalizeSpace (FnString (ctx));
321 public static string FnNormalizeSpace (string s)
323 throw new NotImplementedException ();
326 public static string FnNormalizeUnicode (string arg)
328 return FnNormalizeUnicode (arg, "NFC");
332 public static string FnNormalizeUnicode (string arg, string normalizationForm)
334 throw new NotImplementedException ();
337 public static string FnUpperCase (string arg)
339 // FIXME: supply culture
340 return arg.ToUpper ();
343 public static string FnLowerCase (string arg)
345 // FIXME: supply culture
346 return arg.ToLower ();
349 public static string FnTranslate (string arg, string mapString, string transString)
351 return arg == null ? null : arg.Replace (mapString, transString);
355 public static string FnEscapeUri (string uriPart, bool escapeReserved)
357 throw new NotImplementedException ();
360 public static bool FnContains (XQueryContext ctx, string arg1, string arg2)
362 return FnContains (arg1, arg2, ctx.DefaultCollation);
365 public static bool FnContains (XQueryContext ctx, string arg1, string arg2, string collation)
367 return FnContains (arg1, arg2, ctx.GetCulture (collation));
370 private static bool FnContains (string arg1, string arg2, CultureInfo ci)
376 if (arg2 == String.Empty)
378 return ci.CompareInfo.IndexOf (arg1, arg2) >= 0;
381 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2)
383 return FnStartsWith (arg1, arg2, ctx.DefaultCollation);
386 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2, string collation)
388 return FnStartsWith (arg1, arg2, ctx.GetCulture (collation));
391 private static bool FnStartsWith (string arg1, string arg2, CultureInfo ci)
393 return ci.CompareInfo.IsPrefix (arg1, arg2);
396 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2)
398 return FnEndsWith (arg1, arg2, ctx.DefaultCollation);
401 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2, string collation)
403 return FnEndsWith (arg1, arg2, ctx.GetCulture (collation));
406 private static bool FnEndsWith (string arg1, string arg2, CultureInfo ci)
408 return ci.CompareInfo.IsSuffix (arg1, arg2);
411 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2)
413 return FnSubstringBefore (arg1, arg2, ctx.DefaultCollation);
416 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2, string collation)
418 return FnSubstringBefore (arg1, arg2, ctx.GetCulture (collation));
421 private static string FnSubstringBefore (string arg1, string arg2, CultureInfo ci)
423 int index = ci.CompareInfo.IndexOf (arg1, arg2);
424 return arg1.Substring (0, index);
427 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2)
429 return FnSubstringAfter (arg1, arg2, ctx.DefaultCollation);
432 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2, string collation)
434 return FnSubstringAfter (arg1, arg2, ctx.GetCulture (collation));
437 private static string FnSubstringAfter (string arg1, string arg2, CultureInfo ci)
439 int index = ci.CompareInfo.IndexOf (arg1, arg2);
440 return arg1.Substring (index);
443 public static bool FnMatches (string input, string pattern)
445 return new Regex (pattern).IsMatch (input);
449 public static bool FnMatches (string input, string pattern, string flags)
451 throw new NotImplementedException ();
454 public static string FnReplace (string input, string pattern, string replace)
456 return new Regex (pattern).Replace (input, replace);
460 public static string FnReplace (string input, string pattern, string replace, string flags)
462 throw new NotImplementedException ();
465 public static string [] FnTokenize (string input, string pattern)
467 return new Regex (pattern).Split (input);
471 public static string [] FnTokenize (string input, string pattern, string flags)
473 throw new NotImplementedException ();
476 public static string FnResolveUri (XQueryContext ctx, string relUri)
478 return new Uri (new Uri (ctx.StaticContext.BaseUri), relUri).ToString ();
481 public static string FnResolveUri (string relUri, string baseUri)
483 return new Uri (new Uri (baseUri), relUri).ToString ();
486 public static object FnTrue ()
491 public static object FnFalse ()
496 public static object FnNot (bool value)
501 // FIXME: add a bunch of annoying datetime functions
503 public static object FnResolveQName (string qname, XPathNavigator element)
508 int index = qname.IndexOf (':');
509 string prefix = (index < 0) ? "" : qname.Substring (index);
510 return new XmlQualifiedName (
511 element.LookupNamespace (prefix),
512 index < 0 ? qname : qname.Substring (index + 1));
515 public static object FnExpandQName (string ns, string local)
517 return new XmlQualifiedName (local, ns);
520 public static string FnLocalNameFromQName (XmlQualifiedName name)
522 return name != null ? name.Name : null;
525 public static object FnNamespaceUriFromQName (XmlQualifiedName name)
527 return name != null ? name.Namespace : null;
530 public static object FnNamespaceUriForPrefix (XQueryContext context, string prefix)
532 return prefix != null ? context.LookupNamespace (prefix) : null;
535 public static string [] FnInScopePrefixes (XQueryContext context)
537 IDictionary dict = context.GetNamespacesInScope (XmlNamespaceScope.ExcludeXml);
538 ArrayList keys = new ArrayList (dict.Keys);
539 return keys.ToArray (typeof (string)) as string [];
542 public static string FnName (XPathNavigator nav)
544 return nav != null ? nav.Name : null;
547 public static string FnLocalName (XPathNavigator nav)
549 return nav != null ? nav.LocalName : null;
552 public static string FnNamespaceUri (XPathNavigator nav)
554 return nav != null ? nav.NamespaceURI : null;
557 public static double FnNumber (XQueryContext ctx)
559 return FnNumber (ctx.CurrentItem);
562 public static double FnNumber (object arg)
565 throw new XmlQueryException ("Context item could not be ndetermined during number() evaluation.");
566 XPathItem item = ToItem (arg);
567 return XQueryConvert.ItemToDouble (item);
570 public static bool FnLang (XQueryContext ctx, string testLang)
572 return FnLang (testLang, ctx.CurrentNode);
575 public static bool FnLang (string testLang, XPathNavigator node)
577 return testLang == node.XmlLang;
580 public static XPathNavigator FnRoot (XQueryContext ctx)
582 if (ctx.CurrentItem == null)
583 throw new XmlQueryException ("FONC0001: Undefined context item.");
584 if (ctx.CurrentNode == null)
585 throw new XmlQueryException ("FOTY0011: Context item is not a node.");
586 return FnRoot (ctx.CurrentNode);
589 public static XPathNavigator FnRoot (XPathNavigator node)
593 XPathNavigator root = node.Clone ();
598 public static bool FnBoolean (IEnumerator e)
602 XPathItem item = e.Current as XPathItem;
605 return XQueryConvert.ItemToBoolean (item);
608 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item)
610 return FnIndexOf (ctx, items, item, ctx.DefaultCollation);
613 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item, CultureInfo ci)
615 ArrayList al = new ArrayList ();
616 IEnumerator e = items.GetEnumerator ();
617 for (int i = 0; e.MoveNext (); i++) {
618 XPathItem iter = e.Current as XPathItem;
619 if (iter.XmlType.TypeCode == XmlTypeCode.String) {
620 if (ci.CompareInfo.Compare (iter.Value, item.Value) == 0)
624 IComparable ic = (IComparable) iter.TypedValue;
625 if (ic.CompareTo ((IComparable) item.TypedValue) == 0)
629 return new ListIterator (ctx, al);
632 public static bool FnEmpty (XPathSequence e)
634 if (e is XPathEmptySequence)
636 return !e.GetEnumerator ().MoveNext ();
639 public static bool FnExists (XPathSequence e)
641 if (e is XPathEmptySequence)
643 return e.MoveNext ();
646 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items)
648 return FnDistinctValuesImpl (ctx, items, ctx.DefaultCollation);
651 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items, string collation)
653 return FnDistinctValuesImpl (ctx, items, ctx.GetCulture (collation));
656 private static XPathSequence FnDistinctValuesImpl (XQueryContext ctx, XPathSequence items, CultureInfo collation)
658 return new DistinctValueIterator (ctx, items, collation);
661 public static XPathSequence FnInsertBefore (XPathSequence target, int position, XPathSequence inserts)
665 return new InsertingIterator (target, position, inserts);
668 public static XPathSequence FnRemove (XPathSequence target, int position)
672 return new RemovalIterator (target, position);
675 [MonoTODO ("optimize")]
676 public static XPathSequence FnReverse (XPathSequence arg)
678 ArrayList al = new ArrayList ();
679 while (arg.MoveNext ())
680 al.Add (arg.Current);
682 return new ListIterator (arg.Context, al);
685 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc)
687 return FnSubsequence (sourceSeq, startingLoc, double.MaxValue);
691 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc, double length)
693 throw new NotImplementedException ();
697 // Basically it should be optimized by XQueryASTCompiler
698 public static XPathSequence FnUnordered (XPathSequence e)
703 public static XPathItem FnZeroOrOne (XPathSequence e)
707 XPathItem item = e.Current;
709 throw new XmlQueryException ("zero-or-one() function detected that the argument sequence contains two or more items.");
713 public static object FnOneOrMore (XPathSequence e)
715 if (!e.Clone ().MoveNext ())
716 throw new XmlQueryException ("one-or-more() function detected that the argument sequence contains no items.");
720 public static XPathItem FnExactlyOne (XPathSequence e)
723 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains no items.");
724 XPathItem item = e.Current;
726 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains two or more items.");
730 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2)
732 return FnDeepEqualImpl (p1, p2, ctx.DefaultCollation);
735 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2, string collation)
737 return FnDeepEqualImpl (p1, p2, ctx.GetCulture (collation));
740 public static bool FnDeepEqualImpl (XPathSequence p1, XPathSequence p2, CultureInfo collation)
742 // FIXME: use collation
743 while (p1.MoveNext ()) {
746 if (!FnDeepEqualItem (p1.Current, p2.Current, collation))
754 // FIXME: Actually ValueEQ() should consider collation.
756 private static bool FnDeepEqualItem (XPathItem i1, XPathItem i2, CultureInfo collation)
758 XPathAtomicValue av1 = i1 as XPathAtomicValue;
759 XPathAtomicValue av2 = i1 as XPathAtomicValue;
760 if (av1 != null && av2 != null) {
762 return XQueryComparisonOperator.ValueEQ (av1, av2);
763 } catch (XmlQueryException) {
764 // not-allowed comparison never raises
765 // an error here, just return false.
769 else if (av1 != null || av2 != null)
772 XPathNavigator n1 = i1 as XPathNavigator;
773 XPathNavigator n2 = i2 as XPathNavigator;
774 if (n1.NodeType != n2.NodeType)
776 switch (n1.NodeType) {
777 case XPathNodeType.Root:
778 throw new NotImplementedException ();
779 case XPathNodeType.Element:
780 throw new NotImplementedException ();
781 case XPathNodeType.Attribute:
782 return n1.Name == n2.Name && n1.TypedValue == n2.TypedValue;
783 case XPathNodeType.ProcessingInstruction:
784 case XPathNodeType.Namespace:
785 return n1.Name == n2.Name && n1.Value == n2.Value;
786 case XPathNodeType.Text:
787 case XPathNodeType.Comment:
788 return n1.Value == n2.Value;
793 public static int FnCount (XPathSequence e)
801 public static object FnAvg (XPathSequence e)
805 switch (e.Current.XmlType.TypeCode) {
806 case XmlTypeCode.DayTimeDuration:
807 return FnAvgDayTimeDuration (e);
808 case XmlTypeCode.YearMonthDuration:
809 return FnAvgYearMonthDuration (e);
810 case XmlTypeCode.Decimal:
811 return FnAvgDecimal (e);
812 case XmlTypeCode.Integer:
813 return FnAvgInteger (e);
814 case XmlTypeCode.Float:
815 return FnAvgFloat (e);
816 case XmlTypeCode.UntypedAtomic:
817 case XmlTypeCode.Double:
818 return FnAvgDouble (e);
820 throw new XmlQueryException ("avg() function detected that the sequence contains an item whose type is neither of dayTimeDuration, yearMonthDuration, decimal, integer, float, double, nor untypedAtomic.");
823 private static TimeSpan FnAvgDayTimeDuration (XPathSequence e)
825 throw new NotImplementedException ();
828 private static TimeSpan FnAvgYearMonthDuration (XPathSequence e)
830 throw new NotImplementedException ();
833 private static TimeSpan FnAvgDecimal (XPathSequence e)
835 throw new NotImplementedException ();
838 private static TimeSpan FnAvgInteger (XPathSequence e)
840 throw new NotImplementedException ();
843 private static TimeSpan FnAvgFloat (XPathSequence e)
845 throw new NotImplementedException ();
848 private static TimeSpan FnAvgDouble (XPathSequence e)
850 throw new NotImplementedException ();
854 public static object FnMax (XQueryContext ctx, XPathSequence e)
856 throw new NotImplementedException ();
860 public static object FnMax (XQueryContext ctx, XPathSequence e, string collation)
862 throw new NotImplementedException ();
866 public static object FnMin (XQueryContext ctx, XPathSequence e)
868 throw new NotImplementedException ();
872 public static object FnMin (XQueryContext ctx, XPathSequence e, string collation)
874 throw new NotImplementedException ();
878 public static object FnSum (XPathSequence e)
880 throw new NotImplementedException ();
884 public static object FnSum (XPathSequence e, XPathItem zero)
886 throw new NotImplementedException ();
889 public static XPathNavigator FnId (XQueryContext ctx, string id)
891 return FnId (id, ctx.CurrentNode);
894 public static XPathNavigator FnId (string id, XPathNavigator nav)
896 XPathNavigator node = nav.Clone ();
897 return node.MoveToId (id) ? node : null;
901 public static object FnIdRef (XQueryContext ctx, string arg)
903 return FnIdRef (arg, ctx.CurrentNode);
907 public static object FnIdRef (string arg, XPathNavigator node)
909 throw new NotImplementedException ();
912 public static XPathNavigator FnDoc (XQueryContext ctx, string uri)
914 XmlResolver res = ctx.ContextManager.ExtDocResolver;
915 string baseUriString = ctx.StaticContext.BaseUri;
917 if (baseUriString != null && baseUriString != String.Empty)
918 baseUri = new Uri (baseUriString);
919 Uri relUri = res.ResolveUri (baseUri, uri);
920 Stream s = res.GetEntity (relUri, null, typeof (Stream)) as Stream;
922 XPathDocument doc = new XPathDocument (new XmlValidatingReader (new XmlTextReader (s)), XmlSpace.Preserve);
923 return doc.CreateNavigator ();
929 public static XPathSequence FnCollection (XQueryContext ctx, string name)
931 return ctx.ResolveCollection (name);
934 [XQueryFunctionContext]
935 public static int FnPosition (XPathSequence current)
937 return current.Position;
940 [XQueryFunctionContext]
941 public static int FnLast (XPathSequence current)
943 return current.Count;
946 public static DateTime FnCurrentDateTime ()
951 public static DateTime FnCurrentDate ()
953 return DateTime.Today;
956 public static DateTime FnCurrentTime ()
958 return new DateTime (DateTime.Now.TimeOfDay.Ticks);
962 public static object FnDefaultCollation ()
964 throw new NotImplementedException ();
968 public static object FnImplicitTimeZone ()
970 throw new NotImplementedException ();