In .:
[mono.git] / mcs / class / Mono.Xml.Ext / Mono.Xml.XPath2 / XQueryFunctionCliImpl.cs
1 //
2 // XQueryFunctionCliImple.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 //
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
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:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
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.
28 //
29
30 //
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.
34 //
35 #if NET_2_0
36 using System;
37 using System.Collections;
38 using System.Globalization;
39 using System.IO;
40 using System.Text.RegularExpressions;
41 using System.Xml;
42 using System.Xml.Query;
43 using System.Xml.Schema;
44 using System.Xml.XPath;
45 using Mono.Xml;
46
47 namespace Mono.Xml.XPath2
48 {
49         public class XQueryFunctionCliImpl
50         {
51                 internal static XmlSchemaType XmlTypeFromCliType (Type cliType)
52                 {
53                         switch (Type.GetTypeCode (cliType)) {
54                         case TypeCode.Int32:
55                                 return InternalPool.XsInt;
56                         case TypeCode.Decimal:
57                                 return InternalPool.XsDecimal;
58                         case TypeCode.Double:
59                                 return InternalPool.XsDouble;
60                         case TypeCode.Single:
61                                 return InternalPool.XsFloat;
62                         case TypeCode.Int64:
63                                 return InternalPool.XsLong;
64                         case TypeCode.Int16:
65                                 return InternalPool.XsShort;
66                         case TypeCode.UInt16:
67                                 return InternalPool.XsUnsignedShort;
68                         case TypeCode.UInt32:
69                                 return InternalPool.XsUnsignedInt;
70                         case TypeCode.String:
71                                 return InternalPool.XsString;
72                         case TypeCode.DateTime:
73                                 return InternalPool.XsDateTime;
74                         case TypeCode.Boolean:
75                                 return InternalPool.XsBoolean;
76                         }
77                         if (cliType == typeof (XmlQualifiedName))
78                                 return InternalPool.XsQName;
79                         return null;
80                 }
81
82                 private static XPathItem ToItem (object arg)
83                 {
84                         if (arg == null)
85                                 return null;
86                         XPathItem item = arg as XPathItem;
87                         if (item != null)
88                                 return item;
89                         XPathSequence seq = arg as XPathSequence;
90                         if (seq != null)
91                                 return seq.MoveNext () ? seq.Current : null;
92                         return new XPathAtomicValue (arg, XmlTypeFromCliType (arg.GetType ()));
93                 }
94
95                 // Accessors
96
97                 public static XmlQualifiedName FnNodeName (XPathNavigator arg)
98                 {
99                         if (arg == null)
100                                 return null;
101
102                         return arg.LocalName == String.Empty ?
103                                 XmlQualifiedName.Empty :
104                                 new XmlQualifiedName (arg.LocalName, arg.NamespaceURI);
105                 }
106
107                 public static bool FnNilled (XPathNavigator arg)
108                 {
109                         if (arg == null)
110                                 throw new XmlQueryException ("Function nilled() does not allow empty sequence parameter.");
111
112                         IXmlSchemaInfo info = arg.NodeType == XPathNodeType.Element ? arg.SchemaInfo : null;
113                         return info != null && info.IsNil;
114                 }
115
116                 public static string FnString (XQueryContext context)
117                 {
118                         XPathItem item = context.CurrentItem;
119                         if (item == null)
120                                 throw new ArgumentException ("FONC0001: undefined context item");
121                         return FnString (item);
122                 }
123
124                 [MonoTODO]
125                 public static string FnString (object arg)
126                 {
127                         if (arg == null)
128                                 return String.Empty;
129                         XPathNavigator nav = arg as XPathNavigator;
130                         if (nav != null)
131                                 return nav.Value;
132                         // FIXME: it should be exactly the same as "arg cast as xs:string"
133                         XPathItem item = ToItem (arg);
134                         return item != null ? XQueryConvert.ItemToString (item) : null;
135                 }
136
137                 [MonoTODO]
138                 public static XPathAtomicValue FnData (object arg)
139                 {
140                         // FIXME: parameter should be object []
141                         XPathNavigator nav = arg as XPathNavigator;
142                         if (nav != null) {
143                                 XmlSchemaType st = nav.SchemaInfo != null ? nav.SchemaInfo.SchemaType : null;
144                                 return new XPathAtomicValue (nav.TypedValue, st != null ? st : InternalPool.XsAnyType);
145                         }
146                         else
147                                 return (XPathAtomicValue) arg;
148                 }
149
150                 public static string FnBaseUri (XPathNavigator nav)
151                 {
152                         return nav != null ? nav.BaseURI : null;
153                 }
154
155                 public static string FnDocumentUri (XPathNavigator nav)
156                 {
157                         if (nav == null)
158                                 return null;
159                         XPathNavigator root = nav.Clone ();
160                         root.MoveToRoot ();
161                         return root.BaseURI;
162                 }
163
164                 // Error
165
166                 [MonoTODO]
167                 public static void FnError (object arg)
168                 {
169                         throw new NotImplementedException ();
170                 }
171
172                 // Trace
173
174                 [MonoTODO]
175                 public static object FnTrace (XQueryContext ctx, object value, string label)
176                 {
177                         if (value == null)
178                                 return new XPathEmptySequence (ctx);
179                         XPathSequence seq = value as XPathSequence;
180                         if (seq == null) {
181                                 XPathAtomicValue av = value as XPathAtomicValue;
182                                 if (av == null)
183                                         av = new XPathAtomicValue (value,
184                                                 InternalPool.GetBuiltInType (
185                                                         InternalPool.XmlTypeCodeFromRuntimeType (
186                                                                 value.GetType (), true)));
187                                 seq = new SingleItemIterator (av, ctx);
188                         }
189                         return new TracingIterator (seq, label);
190                 }
191
192                 // Numeric Operation
193
194                 [MonoTODO]
195                 public static object FnAbs (object arg)
196                 {
197                         if (arg is int)
198                                 return System.Math.Abs ((int) arg);
199                         if (arg is long)
200                                 return System.Math.Abs ((long) arg);
201                         else if (arg is decimal)
202                                 return System.Math.Abs ((decimal) arg);
203                         else if (arg is double)
204                                 return System.Math.Abs ((double) arg);
205                         else if (arg is float)
206                                 return System.Math.Abs ((float) arg);
207                         else if (arg is short)
208                                 return System.Math.Abs ((short) arg);
209                         else if (arg is uint || arg is ulong || arg is ushort)
210                                 return arg;
211                         return null;
212                 }
213
214                 [MonoTODO]
215                 public static object FnCeiling (object arg)
216                 {
217                         if (arg is decimal) {
218                                 decimal d = (decimal) arg;
219                                 decimal d2 = Decimal.Floor (d);
220                                 return d2 != d ? d2 + 1 : d2;
221                         }
222                         else if (arg is double || arg is float)
223                                 return System.Math.Ceiling ((double) arg);
224                         else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
225                                 return arg;
226                         return null;
227                 }
228
229                 [MonoTODO]
230                 public static object FnFloor (object arg)
231                 {
232                         if (arg is decimal)
233                                 return Decimal.Floor ((decimal) arg);
234                         else if (arg is double || arg is float)
235                                 return System.Math.Floor ((double) arg);
236                         else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
237                                 return arg;
238                         return null;
239                 }
240
241                 [MonoTODO]
242                 public static object FnRound (object arg)
243                 {
244                         if (arg is decimal)
245                                 return Decimal.Round ((decimal) arg, 0);
246                         else if (arg is double || arg is float)
247                                 return System.Math.Round ((double) arg);
248                         else if (arg is int || arg is long || arg is short || arg is uint || arg is ulong || arg is ushort)
249                                 return arg;
250                         return null;
251                 }
252
253                 [MonoTODO]
254                 public static object FnRoundHalfToEven (object arg)
255                 {
256                         throw new NotImplementedException ();
257                 }
258
259                 [MonoTODO]
260                 public static string FnCodepointsToString (int [] arg)
261                 {
262                         throw new NotImplementedException ();
263                 }
264
265                 [MonoTODO]
266                 public static int [] FnStringToCodepoints (string arg)
267                 {
268                         throw new NotImplementedException ();
269                 }
270
271                 public static int FnCompare (XQueryContext ctx, string s1, string s2)
272                 {
273                         return FnCompare (s1, s2, ctx.DefaultCollation);
274                 }
275
276                 public static int FnCompare (XQueryContext ctx, string s1, string s2, string collation)
277                 {
278                         return FnCompare (s1, s2, ctx.GetCulture (collation));
279                 }
280
281                 private static int FnCompare (string s1, string s2, CultureInfo ci)
282                 {
283                         return ci.CompareInfo.Compare (s1, s2);
284                 }
285
286                 public static string FnConcat (object o1, object o2)
287                 {
288                         return String.Concat (o1, o2);
289                 }
290
291                 public static string FnStringJoin (string [] strings, string separator)
292                 {
293                         return String.Join (separator, strings);
294                 }
295
296                 public static string FnSubstring (string src, double loc)
297                 {
298                         return src.Substring ((int) loc);
299                 }
300
301                 public static string FnSubstring (string src, double loc, double length)
302                 {
303                         return src.Substring ((int) loc, (int) length);
304                 }
305
306                 public static int FnStringLength (XQueryContext ctx)
307                 {
308                         return FnString (ctx).Length;
309                 }
310
311                 public static int FnStringLength (string s)
312                 {
313                         return s.Length;
314                 }
315
316                 public static string FnNormalizeSpace (XQueryContext ctx)
317                 {
318                         return FnNormalizeSpace (FnString (ctx));
319                 }
320
321                 [MonoTODO]
322                 public static string FnNormalizeSpace (string s)
323                 {
324                         throw new NotImplementedException ();
325                 }
326
327                 public static string FnNormalizeUnicode (string arg)
328                 {
329                         return FnNormalizeUnicode (arg, "NFC");
330                 }
331
332                 [MonoTODO]
333                 public static string FnNormalizeUnicode (string arg, string normalizationForm)
334                 {
335                         throw new NotImplementedException ();
336                 }
337
338                 public static string FnUpperCase (string arg)
339                 {
340                         // FIXME: supply culture
341                         return arg.ToUpper ();
342                 }
343
344                 public static string FnLowerCase (string arg)
345                 {
346                         // FIXME: supply culture
347                         return arg.ToLower ();
348                 }
349
350                 public static string FnTranslate (string arg, string mapString, string transString)
351                 {
352                         return arg == null ? null : arg.Replace (mapString, transString);
353                 }
354
355                 [MonoTODO]
356                 public static string FnEscapeUri (string uriPart, bool escapeReserved)
357                 {
358                         throw new NotImplementedException ();
359                 }
360
361                 public static bool FnContains (XQueryContext ctx, string arg1, string arg2)
362                 {
363                         return FnContains (arg1, arg2, ctx.DefaultCollation);
364                 }
365
366                 public static bool FnContains (XQueryContext ctx, string arg1, string arg2, string collation)
367                 {
368                         return FnContains (arg1, arg2, ctx.GetCulture (collation));
369                 }
370
371                 private static bool FnContains (string arg1, string arg2, CultureInfo ci)
372                 {
373                         if (arg1 == null)
374                                 arg1 = String.Empty;
375                         if (arg2 == null)
376                                 arg2 = String.Empty;
377                         if (arg2 == String.Empty)
378                                 return true;
379                         return ci.CompareInfo.IndexOf (arg1, arg2) >= 0;
380                 }
381
382                 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2)
383                 {
384                         return FnStartsWith (arg1, arg2, ctx.DefaultCollation);
385                 }
386
387                 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2, string collation)
388                 {
389                         return FnStartsWith (arg1, arg2, ctx.GetCulture (collation));
390                 }
391
392                 private static bool FnStartsWith (string arg1, string arg2, CultureInfo ci)
393                 {
394                         return ci.CompareInfo.IsPrefix (arg1, arg2);
395                 }
396
397                 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2)
398                 {
399                         return FnEndsWith (arg1, arg2, ctx.DefaultCollation);
400                 }
401
402                 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2, string collation)
403                 {
404                         return FnEndsWith (arg1, arg2, ctx.GetCulture (collation));
405                 }
406
407                 private static bool FnEndsWith (string arg1, string arg2, CultureInfo ci)
408                 {
409                         return ci.CompareInfo.IsSuffix (arg1, arg2);
410                 }
411
412                 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2)
413                 {
414                         return FnSubstringBefore (arg1, arg2, ctx.DefaultCollation);
415                 }
416
417                 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2, string collation)
418                 {
419                         return FnSubstringBefore (arg1, arg2, ctx.GetCulture (collation));
420                 }
421
422                 private static string FnSubstringBefore (string arg1, string arg2, CultureInfo ci)
423                 {
424                         int index = ci.CompareInfo.IndexOf (arg1, arg2);
425                         return arg1.Substring (0, index);
426                 }
427
428                 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2)
429                 {
430                         return FnSubstringAfter (arg1, arg2, ctx.DefaultCollation);
431                 }
432
433                 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2, string collation)
434                 {
435                         return FnSubstringAfter (arg1, arg2, ctx.GetCulture (collation));
436                 }
437
438                 private static string FnSubstringAfter (string arg1, string arg2, CultureInfo ci)
439                 {
440                         int index = ci.CompareInfo.IndexOf (arg1, arg2);
441                         return arg1.Substring (index);
442                 }
443
444                 public static bool FnMatches (string input, string pattern)
445                 {
446                         return new Regex (pattern).IsMatch (input);
447                 }
448
449                 [MonoTODO]
450                 public static bool FnMatches (string input, string pattern, string flags)
451                 {
452                         throw new NotImplementedException ();
453                 }
454
455                 public static string FnReplace (string input, string pattern, string replace)
456                 {
457                         return new Regex (pattern).Replace (input, replace);
458                 }
459
460                 [MonoTODO]
461                 public static string FnReplace (string input, string pattern, string replace, string flags)
462                 {
463                         throw new NotImplementedException ();
464                 }
465
466                 public static string [] FnTokenize (string input, string pattern)
467                 {
468                         return new Regex (pattern).Split (input);
469                 }
470
471                 [MonoTODO]
472                 public static string [] FnTokenize (string input, string pattern, string flags)
473                 {
474                         throw new NotImplementedException ();
475                 }
476
477                 public static string FnResolveUri (XQueryContext ctx, string relUri)
478                 {
479                         return new Uri (new Uri (ctx.StaticContext.BaseUri), relUri).ToString ();
480                 }
481
482                 public static string FnResolveUri (string relUri, string baseUri)
483                 {
484                         return new Uri (new Uri (baseUri), relUri).ToString ();
485                 }
486
487                 public static object FnTrue ()
488                 {
489                         return true;
490                 }
491
492                 public static object FnFalse ()
493                 {
494                         return false;
495                 }
496
497                 public static object FnNot (bool value)
498                 {
499                         return !value;
500                 }
501
502                 // FIXME: add a bunch of annoying datetime functions
503
504                 public static object FnResolveQName (string qname, XPathNavigator element)
505                 {
506                         if (qname == null)
507                                 return null;
508
509                         int index = qname.IndexOf (':');
510                         string prefix = (index < 0) ? "" : qname.Substring (index);
511                         return new XmlQualifiedName (
512                                 element.LookupNamespace (prefix),
513                                 index < 0 ? qname : qname.Substring (index + 1));
514                 }
515
516                 public static object FnExpandQName (string ns, string local)
517                 {
518                         return new XmlQualifiedName (local, ns);
519                 }
520
521                 public static string FnLocalNameFromQName (XmlQualifiedName name)
522                 {
523                         return name != null ? name.Name : null;
524                 }
525
526                 public static object FnNamespaceUriFromQName (XmlQualifiedName name)
527                 {
528                         return name != null ? name.Namespace : null;
529                 }
530
531                 public static object FnNamespaceUriForPrefix (XQueryContext context, string prefix)
532                 {
533                         return prefix != null ? context.LookupNamespace (prefix) : null;
534                 }
535
536                 public static string [] FnInScopePrefixes (XQueryContext context)
537                 {
538                         IDictionary dict = context.GetNamespacesInScope (XmlNamespaceScope.ExcludeXml);
539                         ArrayList keys = new ArrayList (dict.Keys);
540                         return keys.ToArray (typeof (string)) as string [];
541                 }
542
543                 public static string FnName (XPathNavigator nav)
544                 {
545                         return nav != null ? nav.Name : null;
546                 }
547
548                 public static string FnLocalName (XPathNavigator nav)
549                 {
550                         return nav != null ? nav.LocalName : null;
551                 }
552
553                 public static string FnNamespaceUri (XPathNavigator nav)
554                 {
555                         return nav != null ? nav.NamespaceURI : null;
556                 }
557
558                 public static double FnNumber (XQueryContext ctx)
559                 {
560                         return FnNumber (ctx.CurrentItem);
561                 }
562
563                 public static double FnNumber (object arg)
564                 {
565                         if (arg == null)
566                                 throw new XmlQueryException ("Context item could not be ndetermined during number() evaluation.");
567                         XPathItem item = ToItem (arg);
568                         return XQueryConvert.ItemToDouble (item);
569                 }
570
571                 public static bool FnLang (XQueryContext ctx, string testLang)
572                 {
573                         return FnLang (testLang, ctx.CurrentNode);
574                 }
575
576                 public static bool FnLang (string testLang, XPathNavigator node)
577                 {
578                         return testLang == node.XmlLang;
579                 }
580
581                 public static XPathNavigator FnRoot (XQueryContext ctx)
582                 {
583                         if (ctx.CurrentItem == null)
584                                 throw new XmlQueryException ("FONC0001: Undefined context item.");
585                         if (ctx.CurrentNode == null)
586                                 throw new XmlQueryException ("FOTY0011: Context item is not a node.");
587                         return FnRoot (ctx.CurrentNode);
588                 }
589
590                 public static XPathNavigator FnRoot (XPathNavigator node)
591                 {
592                         if (node == null)
593                                 return null;
594                         XPathNavigator root = node.Clone ();
595                         root.MoveToRoot ();
596                         return root;
597                 }
598
599                 public static bool FnBoolean (IEnumerator e)
600                 {
601                         if (!e.MoveNext ())
602                                 return false;
603                         XPathItem item = e.Current as XPathItem;
604                         if (e.MoveNext ())
605                                 return true;
606                         return XQueryConvert.ItemToBoolean (item);
607                 }
608
609                 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item)
610                 {
611                         return FnIndexOf (ctx, items, item, ctx.DefaultCollation);
612                 }
613
614                 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item, CultureInfo ci)
615                 {
616                         ArrayList al = new ArrayList ();
617                         IEnumerator e = items.GetEnumerator ();
618                         for (int i = 0; e.MoveNext (); i++) {
619                                 XPathItem iter = e.Current as XPathItem;
620                                 if (iter.XmlType.TypeCode == XmlTypeCode.String) {
621                                         if (ci.CompareInfo.Compare (iter.Value, item.Value) == 0)
622                                                 al.Add (i);
623                                 }
624                                 else {
625                                         IComparable ic = (IComparable) iter.TypedValue;
626                                         if (ic.CompareTo ((IComparable) item.TypedValue) == 0)
627                                                 al.Add (i);
628                                 }
629                         }
630                         return new ListIterator (ctx, al);
631                 }
632
633                 public static bool FnEmpty (XPathSequence e)
634                 {
635                         if (e is XPathEmptySequence)
636                                 return true;
637                         return !e.GetEnumerator ().MoveNext ();
638                 }
639
640                 public static bool FnExists (XPathSequence e)
641                 {
642                         if (e is XPathEmptySequence)
643                                 return false;
644                         return e.MoveNext ();
645                 }
646
647                 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items)
648                 {
649                         return FnDistinctValuesImpl (ctx, items, ctx.DefaultCollation);
650                 }
651
652                 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items, string collation)
653                 {
654                         return FnDistinctValuesImpl (ctx, items, ctx.GetCulture (collation));
655                 }
656
657                 private static XPathSequence FnDistinctValuesImpl (XQueryContext ctx, XPathSequence items, CultureInfo collation)
658                 {
659                         return new DistinctValueIterator (ctx, items, collation);
660                 }
661
662                 public static XPathSequence FnInsertBefore (XPathSequence target, int position, XPathSequence inserts)
663                 {
664                         if (position < 1)
665                                 position = 1;
666                         return new InsertingIterator (target, position, inserts);
667                 }
668
669                 public static XPathSequence FnRemove (XPathSequence target, int position)
670                 {
671                         if (position < 1)
672                                 return target;
673                         return new RemovalIterator (target, position);
674                 }
675
676                 [MonoTODO ("optimize")]
677                 public static XPathSequence FnReverse (XPathSequence arg)
678                 {
679                         ArrayList al = new ArrayList ();
680                         while (arg.MoveNext ())
681                                 al.Add (arg.Current);
682                         al.Reverse ();
683                         return new ListIterator (arg.Context, al);
684                 }
685
686                 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc)
687                 {
688                         return FnSubsequence (sourceSeq, startingLoc, double.MaxValue);
689                 }
690
691                 [MonoTODO]
692                 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc, double length)
693                 {
694                         throw new NotImplementedException ();
695                 }
696
697                 [MonoTODO]
698                 // Basically it should be optimized by XQueryASTCompiler
699                 public static XPathSequence FnUnordered (XPathSequence e)
700                 {
701                         return e;
702                 }
703
704                 public static XPathItem FnZeroOrOne (XPathSequence e)
705                 {
706                         if (!e.MoveNext ())
707                                 return null;
708                         XPathItem item = e.Current;
709                         if (e.MoveNext ())
710                                 throw new XmlQueryException ("zero-or-one() function detected that the argument sequence contains two or more items.");
711                         return item;
712                 }
713
714                 public static object FnOneOrMore (XPathSequence e)
715                 {
716                         if (!e.Clone ().MoveNext ())
717                                 throw new XmlQueryException ("one-or-more() function detected that the argument sequence contains no items.");
718                         return e;
719                 }
720
721                 public static XPathItem FnExactlyOne (XPathSequence e)
722                 {
723                         if (!e.MoveNext ())
724                                 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains no items.");
725                         XPathItem item = e.Current;
726                         if (e.MoveNext ())
727                                 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains two or more items.");
728                         return item;
729                 }
730
731                 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2)
732                 {
733                         return FnDeepEqualImpl (p1, p2, ctx.DefaultCollation);
734                 }
735
736                 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2, string collation)
737                 {
738                         return FnDeepEqualImpl (p1, p2, ctx.GetCulture (collation));
739                 }
740
741                 public static bool FnDeepEqualImpl (XPathSequence p1, XPathSequence p2, CultureInfo collation)
742                 {
743                         // FIXME: use collation
744                         while (p1.MoveNext ()) {
745                                 if (!p2.MoveNext ())
746                                         return false;
747                                 if (!FnDeepEqualItem (p1.Current, p2.Current, collation))
748                                         return false;
749                         }
750                         if (p2.MoveNext ())
751                                 return false;
752                         return true;
753                 }
754
755                 // FIXME: Actually ValueEQ() should consider collation.
756                 [MonoTODO]
757                 private static bool FnDeepEqualItem (XPathItem i1, XPathItem i2, CultureInfo collation)
758                 {
759                         XPathAtomicValue av1 = i1 as XPathAtomicValue;
760                         XPathAtomicValue av2 = i1 as XPathAtomicValue;
761                         if (av1 != null && av2 != null) {
762                                 try {
763                                         return XQueryComparisonOperator.ValueEQ (av1, av2);
764                                 } catch (XmlQueryException) {
765                                         // not-allowed comparison never raises
766                                         // an error here, just return false.
767                                         return false;
768                                 }
769                         }
770                         else if (av1 != null || av2 != null)
771                                 return false;
772
773                         XPathNavigator n1 = i1 as XPathNavigator;
774                         XPathNavigator n2 = i2 as XPathNavigator;
775                         if (n1.NodeType != n2.NodeType)
776                                 return false;
777                         switch (n1.NodeType) {
778                         case XPathNodeType.Root:
779                                 throw new NotImplementedException ();
780                         case XPathNodeType.Element:
781                                 throw new NotImplementedException ();
782                         case XPathNodeType.Attribute:
783                                 return n1.Name == n2.Name && n1.TypedValue == n2.TypedValue;
784                         case XPathNodeType.ProcessingInstruction:
785                         case XPathNodeType.Namespace:
786                                 return n1.Name == n2.Name && n1.Value == n2.Value;
787                         case XPathNodeType.Text:
788                         case XPathNodeType.Comment:
789                                 return n1.Value == n2.Value;
790                         }
791                         return false;
792                 }
793
794                 public static int FnCount (XPathSequence e)
795                 {
796                         if (e == null)
797                                 return 0;
798                         return e.Count;
799                 }
800
801                 [MonoTODO]
802                 public static object FnAvg (XPathSequence e)
803                 {
804                         if (!e.MoveNext ())
805                                 return null;
806                         switch (e.Current.XmlType.TypeCode) {
807                         case XmlTypeCode.DayTimeDuration:
808                                 return FnAvgDayTimeDuration (e);
809                         case XmlTypeCode.YearMonthDuration:
810                                 return FnAvgYearMonthDuration (e);
811                         case XmlTypeCode.Decimal:
812                                 return FnAvgDecimal (e);
813                         case XmlTypeCode.Integer:
814                                 return FnAvgInteger (e);
815                         case XmlTypeCode.Float:
816                                 return FnAvgFloat (e);
817                         case XmlTypeCode.UntypedAtomic:
818                         case XmlTypeCode.Double:
819                                 return FnAvgDouble (e);
820                         }
821                         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.");
822                 }
823
824                 private static TimeSpan FnAvgDayTimeDuration (XPathSequence e)
825                 {
826                         throw new NotImplementedException ();
827                 }
828
829                 private static TimeSpan FnAvgYearMonthDuration (XPathSequence e)
830                 {
831                         throw new NotImplementedException ();
832                 }
833
834                 private static TimeSpan FnAvgDecimal (XPathSequence e)
835                 {
836                         throw new NotImplementedException ();
837                 }
838
839                 private static TimeSpan FnAvgInteger (XPathSequence e)
840                 {
841                         throw new NotImplementedException ();
842                 }
843
844                 private static TimeSpan FnAvgFloat (XPathSequence e)
845                 {
846                         throw new NotImplementedException ();
847                 }
848
849                 private static TimeSpan FnAvgDouble (XPathSequence e)
850                 {
851                         throw new NotImplementedException ();
852                 }
853
854                 public static object FnMax (XQueryContext ctx, XPathSequence e)
855                 {
856                         return FnMaxImpl (e, ctx.DefaultCollation);
857                 }
858
859                 public static object FnMax (XQueryContext ctx, XPathSequence e, string collation)
860                 {
861                         return FnMaxImpl (e, ctx.GetCulture (collation));
862                 }
863
864                 private static object FnMaxImpl (XPathSequence e, CultureInfo collation)
865                 {
866                         if (!e.MoveNext ())
867                                 return null;
868                         switch (e.Current.XmlType.TypeCode) {
869                         case XmlTypeCode.DayTimeDuration:
870                                 return FnMaxDayTimeDuration (e);
871                         case XmlTypeCode.YearMonthDuration:
872                                 return FnMaxYearMonthDuration (e);
873                         case XmlTypeCode.Decimal:
874                                 return FnMaxDecimal (e);
875                         case XmlTypeCode.Integer:
876                                 return FnMaxInteger (e);
877                         case XmlTypeCode.Float:
878                                 return FnMaxFloat (e);
879                         case XmlTypeCode.UntypedAtomic:
880                         case XmlTypeCode.Double:
881                                 return FnMaxDouble (e);
882                         }
883                         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.");
884                 }
885
886                 private static TimeSpan FnMaxDayTimeDuration (XPathSequence e)
887                 {
888                         // FIXME: reject yMD (but is it possible...?)
889                         TimeSpan ret = TimeSpan.Zero;
890                         do {
891                                 TimeSpan ts = (TimeSpan) e.Current.TypedValue;
892                                 if (ts > ret)
893                                         ret = ts;
894                         } while (e.MoveNext ());
895                         return ret;
896                 }
897
898                 private static TimeSpan FnMaxYearMonthDuration (XPathSequence e)
899                 {
900                         // FIXME: reject dTD (but is it possible...?)
901                         TimeSpan ret = TimeSpan.Zero;
902                         do {
903                                 TimeSpan ts = (TimeSpan) e.Current.TypedValue;
904                                 if (ts > ret)
905                                         ret = ts;
906                         } while (e.MoveNext ());
907                         return ret;
908                 }
909
910                 private static decimal FnMaxDecimal (XPathSequence e)
911                 {
912                         decimal ret = decimal.MinValue;
913                         do {
914                                 ret = System.Math.Max (e.Current.ValueAsDecimal, ret);
915                         } while (e.MoveNext ());
916                         return ret;
917                 }
918
919                 private static int FnMaxInteger (XPathSequence e)
920                 {
921                         int ret = int.MinValue;
922                         do {
923                                 ret = System.Math.Max (e.Current.ValueAsInt32, ret);
924                         } while (e.MoveNext ());
925                         return ret;
926                 }
927
928                 private static float FnMaxFloat (XPathSequence e)
929                 {
930                         float ret = float.MinValue;
931                         do {
932                                 ret = System.Math.Max (e.Current.ValueAsSingle, ret);
933                         } while (e.MoveNext ());
934                         return ret;
935                 }
936
937                 private static double FnMaxDouble (XPathSequence e)
938                 {
939                         double ret = double.MinValue;
940                         do {
941                                 ret = System.Math.Max (e.Current.ValueAsDouble, ret);
942                         } while (e.MoveNext ());
943                         return ret;
944                 }
945
946                 public static object FnMin (XQueryContext ctx, XPathSequence e)
947                 {
948                         return FnMinImpl (e, ctx.DefaultCollation);
949                 }
950
951                 public static object FnMin (XQueryContext ctx, XPathSequence e, string collation)
952                 {
953                         return FnMinImpl (e, ctx.GetCulture (collation));
954                 }
955
956                 private static object FnMinImpl (XPathSequence e, CultureInfo collation)
957                 {
958                         if (!e.MoveNext ())
959                                 return null;
960                         switch (e.Current.XmlType.TypeCode) {
961                         case XmlTypeCode.DayTimeDuration:
962                                 return FnMinDayTimeDuration (e);
963                         case XmlTypeCode.YearMonthDuration:
964                                 return FnMinYearMonthDuration (e);
965                         case XmlTypeCode.Decimal:
966                                 return FnMinDecimal (e);
967                         case XmlTypeCode.Integer:
968                                 return FnMinInteger (e);
969                         case XmlTypeCode.Float:
970                                 return FnMinFloat (e);
971                         case XmlTypeCode.UntypedAtomic:
972                         case XmlTypeCode.Double:
973                                 return FnMinDouble (e);
974                         }
975                         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.");
976                 }
977
978                 private static TimeSpan FnMinDayTimeDuration (XPathSequence e)
979                 {
980                         // FIXME: reject yMD (but is it possible...?)
981                         TimeSpan ret = TimeSpan.Zero;
982                         do {
983                                 TimeSpan ts = (TimeSpan) e.Current.TypedValue;
984                                 if (ts > ret)
985                                         ret = ts;
986                         } while (e.MoveNext ());
987                         return ret;
988                 }
989
990                 private static TimeSpan FnMinYearMonthDuration (XPathSequence e)
991                 {
992                         // FIXME: reject dTD (but is it possible...?)
993                         TimeSpan ret = TimeSpan.Zero;
994                         do {
995                                 TimeSpan ts = (TimeSpan) e.Current.TypedValue;
996                                 if (ts > ret)
997                                         ret = ts;
998                         } while (e.MoveNext ());
999                         return ret;
1000                 }
1001
1002                 private static decimal FnMinDecimal (XPathSequence e)
1003                 {
1004                         decimal ret = decimal.MaxValue;
1005                         do {
1006                                 ret = System.Math.Min (e.Current.ValueAsDecimal, ret);
1007                         } while (e.MoveNext ());
1008                         return ret;
1009                 }
1010
1011                 private static int FnMinInteger (XPathSequence e)
1012                 {
1013                         int ret = int.MaxValue;
1014                         do {
1015                                 ret = System.Math.Min (e.Current.ValueAsInt32, ret);
1016                         } while (e.MoveNext ());
1017                         return ret;
1018                 }
1019
1020                 private static float FnMinFloat (XPathSequence e)
1021                 {
1022                         float ret = float.MaxValue;
1023                         do {
1024                                 ret = System.Math.Min (e.Current.ValueAsSingle, ret);
1025                         } while (e.MoveNext ());
1026                         return ret;
1027                 }
1028
1029                 private static double FnMinDouble (XPathSequence e)
1030                 {
1031                         double ret = double.MaxValue;
1032                         do {
1033                                 ret = System.Math.Min (e.Current.ValueAsDouble, ret);
1034                         } while (e.MoveNext ());
1035                         return ret;
1036                 }
1037
1038                 [MonoTODO]
1039                 public static object FnSum (XPathSequence e)
1040                 {
1041                         throw new NotImplementedException ();
1042                 }
1043
1044                 [MonoTODO]
1045                 public static object FnSum (XPathSequence e, XPathItem zero)
1046                 {
1047                         throw new NotImplementedException ();
1048                 }
1049
1050                 public static XPathNavigator FnId (XQueryContext ctx, string id)
1051                 {
1052                         return FnId (id, ctx.CurrentNode);
1053                 }
1054
1055                 public static XPathNavigator FnId (string id, XPathNavigator nav)
1056                 {
1057                         XPathNavigator node = nav.Clone ();
1058                         return node.MoveToId (id) ? node : null;
1059                 }
1060
1061                 [MonoTODO]
1062                 public static object FnIdRef (XQueryContext ctx, string arg)
1063                 {
1064                         return FnIdRef (arg, ctx.CurrentNode);
1065                 }
1066
1067                 [MonoTODO]
1068                 public static object FnIdRef (string arg, XPathNavigator node)
1069                 {
1070                         throw new NotImplementedException ();
1071                 }
1072
1073                 public static XPathNavigator FnDoc (XQueryContext ctx, string uri)
1074                 {
1075                         XmlResolver res = ctx.ContextManager.ExtDocResolver;
1076                         string baseUriString = ctx.StaticContext.BaseUri;
1077                         Uri baseUri = null;
1078                         if (baseUriString != null && baseUriString != String.Empty)
1079                                 baseUri = new Uri (baseUriString);
1080                         Uri relUri = res.ResolveUri (baseUri, uri);
1081                         Stream s = res.GetEntity (relUri, null, typeof (Stream)) as Stream;
1082                         try {
1083                                 XPathDocument doc = new XPathDocument (new XmlValidatingReader (new XmlTextReader (s)), XmlSpace.Preserve);
1084                                 return doc.CreateNavigator ();
1085                         } finally {
1086                                 s.Close ();
1087                         }
1088                 }
1089
1090                 public static XPathSequence FnCollection (XQueryContext ctx, string name)
1091                 {
1092                         return ctx.ResolveCollection (name);
1093                 }
1094
1095                 [XQueryFunctionContext]
1096                 public static int FnPosition (XPathSequence current)
1097                 {
1098                         return current.Position;
1099                 }
1100
1101                 [XQueryFunctionContext]
1102                 public static int FnLast (XPathSequence current)
1103                 {
1104                         return current.Count;
1105                 }
1106
1107                 public static DateTime FnCurrentDateTime ()
1108                 {
1109                         return DateTime.Now;
1110                 }
1111
1112                 public static DateTime FnCurrentDate ()
1113                 {
1114                         return DateTime.Today;
1115                 }
1116
1117                 public static DateTime FnCurrentTime ()
1118                 {
1119                         return new DateTime (DateTime.Now.TimeOfDay.Ticks);
1120                 }
1121
1122                 [MonoTODO]
1123                 public static object FnDefaultCollation ()
1124                 {
1125                         throw new NotImplementedException ();
1126                 }
1127
1128                 [MonoTODO]
1129                 public static object FnImplicitTimeZone ()
1130                 {
1131                         throw new NotImplementedException ();
1132                 }
1133         }
1134 }
1135 #endif