2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.XML / System.Xml.Query / 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
46 namespace Mono.Xml.XPath2
47 {
48         public class XQueryFunctionCliImpl
49         {
50                 internal static XmlSchemaType XmlTypeFromCliType (Type cliType)
51                 {
52                         switch (Type.GetTypeCode (cliType)) {
53                         case TypeCode.Int32:
54                                 return XmlSchemaSimpleType.XsInt;
55                         case TypeCode.Decimal:
56                                 return XmlSchemaSimpleType.XsDecimal;
57                         case TypeCode.Double:
58                                 return XmlSchemaSimpleType.XsDouble;
59                         case TypeCode.Single:
60                                 return XmlSchemaSimpleType.XsFloat;
61                         case TypeCode.Int64:
62                                 return XmlSchemaSimpleType.XsLong;
63                         case TypeCode.Int16:
64                                 return XmlSchemaSimpleType.XsShort;
65                         case TypeCode.UInt16:
66                                 return XmlSchemaSimpleType.XsUnsignedShort;
67                         case TypeCode.UInt32:
68                                 return XmlSchemaSimpleType.XsUnsignedInt;
69                         case TypeCode.String:
70                                 return XmlSchemaSimpleType.XsString;
71                         case TypeCode.DateTime:
72                                 return XmlSchemaSimpleType.XsDateTime;
73                         case TypeCode.Boolean:
74                                 return XmlSchemaSimpleType.XsBoolean;
75                         }
76                         if (cliType == typeof (XmlQualifiedName))
77                                 return XmlSchemaSimpleType.XsQName;
78                         return null;
79                 }
80
81                 private static XPathItem ToItem (object arg)
82                 {
83                         if (arg == null)
84                                 return null;
85                         XPathItem item = arg as XPathItem;
86                         if (item != null)
87                                 return item;
88                         XPathSequence seq = arg as XPathSequence;
89                         if (seq != null)
90                                 return seq.MoveNext () ? seq.Current : null;
91                         return new XPathAtomicValue (arg, XmlTypeFromCliType (arg.GetType ()));
92                 }
93
94                 // Accessors
95
96                 public static XmlQualifiedName FnNodeName (XPathNavigator arg)
97                 {
98                         if (arg == null)
99                                 return null;
100
101                         return arg.LocalName == String.Empty ?
102                                 XmlQualifiedName.Empty :
103                                 new XmlQualifiedName (arg.LocalName, arg.NamespaceURI);
104                 }
105
106                 public static bool FnNilled (XPathNavigator arg)
107                 {
108                         if (arg == null)
109                                 throw new XmlQueryException ("Function nilled() does not allow empty sequence parameter.");
110
111                         IXmlSchemaInfo info = arg.NodeType == XPathNodeType.Element ? arg.SchemaInfo : null;
112                         return info != null && info.IsNil;
113                 }
114
115                 public static string FnString (XQueryContext context)
116                 {
117                         XPathItem item = context.CurrentItem;
118                         if (item == null)
119                                 throw new ArgumentException ("FONC0001: undefined context item");
120                         return FnString (item);
121                 }
122
123                 [MonoTODO]
124                 public static string FnString (object arg)
125                 {
126                         if (arg == null)
127                                 return String.Empty;
128                         XPathNavigator nav = arg as XPathNavigator;
129                         if (nav != null)
130                                 return nav.Value;
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;
134                 }
135
136                 [MonoTODO]
137                 public static XPathAtomicValue FnData (object arg)
138                 {
139                         // FIXME: parameter should be object []
140                         XPathNavigator nav = arg as XPathNavigator;
141                         if (nav != null) {
142                                 XmlSchemaType st = nav.SchemaInfo != null ? nav.SchemaInfo.SchemaType : null;
143                                 return new XPathAtomicValue (nav.TypedValue, st != null ? st : XmlSchemaComplexType.AnyType);
144                         }
145                         else
146                                 return (XPathAtomicValue) arg;
147                 }
148
149                 public static string FnBaseUri (XPathNavigator nav)
150                 {
151                         return nav != null ? nav.BaseURI : null;
152                 }
153
154                 public static string FnDocumentUri (XPathNavigator nav)
155                 {
156                         if (nav == null)
157                                 return null;
158                         XPathNavigator root = nav.Clone ();
159                         root.MoveToRoot ();
160                         return root.BaseURI;
161                 }
162
163                 // Error
164
165                 [MonoTODO]
166                 public static void FnError (object arg)
167                 {
168                         throw new NotImplementedException ();
169                 }
170
171                 // Trace
172
173                 [MonoTODO]
174                 public static object FnTrace (XQueryContext ctx, object value, string label)
175                 {
176                         if (value == null)
177                                 return new XPathEmptySequence (ctx);
178                         XPathSequence seq = value as XPathSequence;
179                         if (seq == null) {
180                                 XPathAtomicValue av = value as XPathAtomicValue;
181                                 if (av == null)
182                                         av = new XPathAtomicValue (value,
183                                                 XmlSchemaType.GetBuiltInType (
184                                                         XPathAtomicValue.XmlTypeCodeFromRuntimeType (
185                                                                 value.GetType (), true)));
186                                 seq = new SingleItemIterator (av, ctx);
187                         }
188                         return new TracingIterator (seq, label);
189                 }
190
191                 // Numeric Operation
192
193                 [MonoTODO]
194                 public static object FnAbs (object arg)
195                 {
196                         if (arg is int)
197                                 return System.Math.Abs ((int) arg);
198                         if (arg is long)
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)
209                                 return arg;
210                         return null;
211                 }
212
213                 [MonoTODO]
214                 public static object FnCeiling (object arg)
215                 {
216                         if (arg is decimal) {
217                                 decimal d = (decimal) arg;
218                                 decimal d2 = Decimal.Floor (d);
219                                 return d2 != d ? d2 + 1 : d2;
220                         }
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)
224                                 return arg;
225                         return null;
226                 }
227
228                 [MonoTODO]
229                 public static object FnFloor (object arg)
230                 {
231                         if (arg is decimal)
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)
236                                 return arg;
237                         return null;
238                 }
239
240                 [MonoTODO]
241                 public static object FnRound (object arg)
242                 {
243                         if (arg is decimal)
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)
248                                 return arg;
249                         return null;
250                 }
251
252                 [MonoTODO]
253                 public static object FnRoundHalfToEven (object arg)
254                 {
255                         throw new NotImplementedException ();
256                 }
257
258                 [MonoTODO]
259                 public static string FnCodepointsToString (int [] arg)
260                 {
261                         throw new NotImplementedException ();
262                 }
263
264                 [MonoTODO]
265                 public static int [] FnStringToCodepoints (string arg)
266                 {
267                         throw new NotImplementedException ();
268                 }
269
270                 public static int FnCompare (XQueryContext ctx, string s1, string s2)
271                 {
272                         return FnCompare (s1, s2, ctx.DefaultCollation);
273                 }
274
275                 public static int FnCompare (XQueryContext ctx, string s1, string s2, string collation)
276                 {
277                         return FnCompare (s1, s2, ctx.GetCulture (collation));
278                 }
279
280                 private static int FnCompare (string s1, string s2, CultureInfo ci)
281                 {
282                         return ci.CompareInfo.Compare (s1, s2);
283                 }
284
285                 public static string FnConcat (object o1, object o2)
286                 {
287                         return String.Concat (o1, o2);
288                 }
289
290                 public static string FnStringJoin (string [] strings, string separator)
291                 {
292                         return String.Join (separator, strings);
293                 }
294
295                 public static string FnSubstring (string src, double loc)
296                 {
297                         return src.Substring ((int) loc);
298                 }
299
300                 public static string FnSubstring (string src, double loc, double length)
301                 {
302                         return src.Substring ((int) loc, (int) length);
303                 }
304
305                 public static int FnStringLength (XQueryContext ctx)
306                 {
307                         return FnString (ctx).Length;
308                 }
309
310                 public static int FnStringLength (string s)
311                 {
312                         return s.Length;
313                 }
314
315                 public static string FnNormalizeSpace (XQueryContext ctx)
316                 {
317                         return FnNormalizeSpace (FnString (ctx));
318                 }
319
320                 [MonoTODO]
321                 public static string FnNormalizeSpace (string s)
322                 {
323                         throw new NotImplementedException ();
324                 }
325
326                 public static string FnNormalizeUnicode (string arg)
327                 {
328                         return FnNormalizeUnicode (arg, "NFC");
329                 }
330
331                 [MonoTODO]
332                 public static string FnNormalizeUnicode (string arg, string normalizationForm)
333                 {
334                         throw new NotImplementedException ();
335                 }
336
337                 public static string FnUpperCase (string arg)
338                 {
339                         // FIXME: supply culture
340                         return arg.ToUpper ();
341                 }
342
343                 public static string FnLowerCase (string arg)
344                 {
345                         // FIXME: supply culture
346                         return arg.ToLower ();
347                 }
348
349                 public static string FnTranslate (string arg, string mapString, string transString)
350                 {
351                         return arg == null ? null : arg.Replace (mapString, transString);
352                 }
353
354                 [MonoTODO]
355                 public static string FnEscapeUri (string uriPart, bool escapeReserved)
356                 {
357                         throw new NotImplementedException ();
358                 }
359
360                 public static bool FnContains (XQueryContext ctx, string arg1, string arg2)
361                 {
362                         return FnContains (arg1, arg2, ctx.DefaultCollation);
363                 }
364
365                 public static bool FnContains (XQueryContext ctx, string arg1, string arg2, string collation)
366                 {
367                         return FnContains (arg1, arg2, ctx.GetCulture (collation));
368                 }
369
370                 private static bool FnContains (string arg1, string arg2, CultureInfo ci)
371                 {
372                         if (arg1 == null)
373                                 arg1 = String.Empty;
374                         if (arg2 == null)
375                                 arg2 = String.Empty;
376                         if (arg2 == String.Empty)
377                                 return true;
378                         return ci.CompareInfo.IndexOf (arg1, arg2) >= 0;
379                 }
380
381                 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2)
382                 {
383                         return FnStartsWith (arg1, arg2, ctx.DefaultCollation);
384                 }
385
386                 public static bool FnStartsWith (XQueryContext ctx, string arg1, string arg2, string collation)
387                 {
388                         return FnStartsWith (arg1, arg2, ctx.GetCulture (collation));
389                 }
390
391                 private static bool FnStartsWith (string arg1, string arg2, CultureInfo ci)
392                 {
393                         return ci.CompareInfo.IsPrefix (arg1, arg2);
394                 }
395
396                 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2)
397                 {
398                         return FnEndsWith (arg1, arg2, ctx.DefaultCollation);
399                 }
400
401                 public static bool FnEndsWith (XQueryContext ctx, string arg1, string arg2, string collation)
402                 {
403                         return FnEndsWith (arg1, arg2, ctx.GetCulture (collation));
404                 }
405
406                 private static bool FnEndsWith (string arg1, string arg2, CultureInfo ci)
407                 {
408                         return ci.CompareInfo.IsSuffix (arg1, arg2);
409                 }
410
411                 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2)
412                 {
413                         return FnSubstringBefore (arg1, arg2, ctx.DefaultCollation);
414                 }
415
416                 public static string FnSubstringBefore (XQueryContext ctx, string arg1, string arg2, string collation)
417                 {
418                         return FnSubstringBefore (arg1, arg2, ctx.GetCulture (collation));
419                 }
420
421                 private static string FnSubstringBefore (string arg1, string arg2, CultureInfo ci)
422                 {
423                         int index = ci.CompareInfo.IndexOf (arg1, arg2);
424                         return arg1.Substring (0, index);
425                 }
426
427                 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2)
428                 {
429                         return FnSubstringAfter (arg1, arg2, ctx.DefaultCollation);
430                 }
431
432                 public static string FnSubstringAfter (XQueryContext ctx, string arg1, string arg2, string collation)
433                 {
434                         return FnSubstringAfter (arg1, arg2, ctx.GetCulture (collation));
435                 }
436
437                 private static string FnSubstringAfter (string arg1, string arg2, CultureInfo ci)
438                 {
439                         int index = ci.CompareInfo.IndexOf (arg1, arg2);
440                         return arg1.Substring (index);
441                 }
442
443                 public static bool FnMatches (string input, string pattern)
444                 {
445                         return new Regex (pattern).IsMatch (input);
446                 }
447
448                 [MonoTODO]
449                 public static bool FnMatches (string input, string pattern, string flags)
450                 {
451                         throw new NotImplementedException ();
452                 }
453
454                 public static string FnReplace (string input, string pattern, string replace)
455                 {
456                         return new Regex (pattern).Replace (input, replace);
457                 }
458
459                 [MonoTODO]
460                 public static string FnReplace (string input, string pattern, string replace, string flags)
461                 {
462                         throw new NotImplementedException ();
463                 }
464
465                 public static string [] FnTokenize (string input, string pattern)
466                 {
467                         return new Regex (pattern).Split (input);
468                 }
469
470                 [MonoTODO]
471                 public static string [] FnTokenize (string input, string pattern, string flags)
472                 {
473                         throw new NotImplementedException ();
474                 }
475
476                 public static string FnResolveUri (XQueryContext ctx, string relUri)
477                 {
478                         return new Uri (new Uri (ctx.StaticContext.BaseUri), relUri).ToString ();
479                 }
480
481                 public static string FnResolveUri (string relUri, string baseUri)
482                 {
483                         return new Uri (new Uri (baseUri), relUri).ToString ();
484                 }
485
486                 public static object FnTrue ()
487                 {
488                         return true;
489                 }
490
491                 public static object FnFalse ()
492                 {
493                         return false;
494                 }
495
496                 public static object FnNot (bool value)
497                 {
498                         return !value;
499                 }
500
501                 // FIXME: add a bunch of annoying datetime functions
502
503                 public static object FnResolveQName (string qname, XPathNavigator element)
504                 {
505                         if (qname == null)
506                                 return null;
507
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));
513                 }
514
515                 public static object FnExpandQName (string ns, string local)
516                 {
517                         return new XmlQualifiedName (local, ns);
518                 }
519
520                 public static string FnLocalNameFromQName (XmlQualifiedName name)
521                 {
522                         return name != null ? name.Name : null;
523                 }
524
525                 public static object FnNamespaceUriFromQName (XmlQualifiedName name)
526                 {
527                         return name != null ? name.Namespace : null;
528                 }
529
530                 public static object FnNamespaceUriForPrefix (XQueryContext context, string prefix)
531                 {
532                         return prefix != null ? context.LookupNamespace (prefix) : null;
533                 }
534
535                 public static string [] FnInScopePrefixes (XQueryContext context)
536                 {
537                         IDictionary dict = context.GetNamespacesInScope (XmlNamespaceScope.ExcludeXml);
538                         ArrayList keys = new ArrayList (dict.Keys);
539                         return keys.ToArray (typeof (string)) as string [];
540                 }
541
542                 public static string FnName (XPathNavigator nav)
543                 {
544                         return nav != null ? nav.Name : null;
545                 }
546
547                 public static string FnLocalName (XPathNavigator nav)
548                 {
549                         return nav != null ? nav.LocalName : null;
550                 }
551
552                 public static string FnNamespaceUri (XPathNavigator nav)
553                 {
554                         return nav != null ? nav.NamespaceURI : null;
555                 }
556
557                 public static double FnNumber (XQueryContext ctx)
558                 {
559                         return FnNumber (ctx.CurrentItem);
560                 }
561
562                 public static double FnNumber (object arg)
563                 {
564                         if (arg == null)
565                                 throw new XmlQueryException ("Context item could not be ndetermined during number() evaluation.");
566                         XPathItem item = ToItem (arg);
567                         return XQueryConvert.ItemToDouble (item);
568                 }
569
570                 public static bool FnLang (XQueryContext ctx, string testLang)
571                 {
572                         return FnLang (testLang, ctx.CurrentNode);
573                 }
574
575                 public static bool FnLang (string testLang, XPathNavigator node)
576                 {
577                         return testLang == node.XmlLang;
578                 }
579
580                 public static XPathNavigator FnRoot (XQueryContext ctx)
581                 {
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);
587                 }
588
589                 public static XPathNavigator FnRoot (XPathNavigator node)
590                 {
591                         if (node == null)
592                                 return null;
593                         XPathNavigator root = node.Clone ();
594                         root.MoveToRoot ();
595                         return root;
596                 }
597
598                 public static bool FnBoolean (IEnumerator e)
599                 {
600                         if (!e.MoveNext ())
601                                 return false;
602                         XPathItem item = e.Current as XPathItem;
603                         if (e.MoveNext ())
604                                 return true;
605                         return XQueryConvert.ItemToBoolean (item);
606                 }
607
608                 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item)
609                 {
610                         return FnIndexOf (ctx, items, item, ctx.DefaultCollation);
611                 }
612
613                 public static XPathSequence FnIndexOf (XQueryContext ctx, XPathSequence items, XPathItem item, CultureInfo ci)
614                 {
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)
621                                                 al.Add (i);
622                                 }
623                                 else {
624                                         IComparable ic = (IComparable) iter.TypedValue;
625                                         if (ic.CompareTo ((IComparable) item.TypedValue) == 0)
626                                                 al.Add (i);
627                                 }
628                         }
629                         return new ListIterator (ctx, al);
630                 }
631
632                 public static bool FnEmpty (XPathSequence e)
633                 {
634                         if (e is XPathEmptySequence)
635                                 return true;
636                         return !e.GetEnumerator ().MoveNext ();
637                 }
638
639                 public static bool FnExists (XPathSequence e)
640                 {
641                         if (e is XPathEmptySequence)
642                                 return false;
643                         return e.MoveNext ();
644                 }
645
646                 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items)
647                 {
648                         return FnDistinctValuesImpl (ctx, items, ctx.DefaultCollation);
649                 }
650
651                 public static XPathSequence FnDistinctValues (XQueryContext ctx, XPathSequence items, string collation)
652                 {
653                         return FnDistinctValuesImpl (ctx, items, ctx.GetCulture (collation));
654                 }
655
656                 private static XPathSequence FnDistinctValuesImpl (XQueryContext ctx, XPathSequence items, CultureInfo collation)
657                 {
658                         return new DistinctValueIterator (ctx, items, collation);
659                 }
660
661                 public static XPathSequence FnInsertBefore (XPathSequence target, int position, XPathSequence inserts)
662                 {
663                         if (position < 1)
664                                 position = 1;
665                         return new InsertingIterator (target, position, inserts);
666                 }
667
668                 public static XPathSequence FnRemove (XPathSequence target, int position)
669                 {
670                         if (position < 1)
671                                 return target;
672                         return new RemovalIterator (target, position);
673                 }
674
675                 [MonoTODO ("optimize")]
676                 public static XPathSequence FnReverse (XPathSequence arg)
677                 {
678                         ArrayList al = new ArrayList ();
679                         while (arg.MoveNext ())
680                                 al.Add (arg.Current);
681                         al.Reverse ();
682                         return new ListIterator (arg.Context, al);
683                 }
684
685                 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc)
686                 {
687                         return FnSubsequence (sourceSeq, startingLoc, double.MaxValue);
688                 }
689
690                 [MonoTODO]
691                 public static object FnSubsequence (XPathSequence sourceSeq, double startingLoc, double length)
692                 {
693                         throw new NotImplementedException ();
694                 }
695
696                 [MonoTODO]
697                 // Basically it should be optimized by XQueryASTCompiler
698                 public static XPathSequence FnUnordered (XPathSequence e)
699                 {
700                         return e;
701                 }
702
703                 public static XPathItem FnZeroOrOne (XPathSequence e)
704                 {
705                         if (!e.MoveNext ())
706                                 return null;
707                         XPathItem item = e.Current;
708                         if (e.MoveNext ())
709                                 throw new XmlQueryException ("zero-or-one() function detected that the argument sequence contains two or more items.");
710                         return item;
711                 }
712
713                 public static object FnOneOrMore (XPathSequence e)
714                 {
715                         if (!e.Clone ().MoveNext ())
716                                 throw new XmlQueryException ("one-or-more() function detected that the argument sequence contains no items.");
717                         return e;
718                 }
719
720                 public static XPathItem FnExactlyOne (XPathSequence e)
721                 {
722                         if (!e.MoveNext ())
723                                 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains no items.");
724                         XPathItem item = e.Current;
725                         if (e.MoveNext ())
726                                 throw new XmlQueryException ("exactly-one() function detected that the argument sequence contains two or more items.");
727                         return item;
728                 }
729
730                 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2)
731                 {
732                         return FnDeepEqualImpl (p1, p2, ctx.DefaultCollation);
733                 }
734
735                 public static object FnDeepEqual (XQueryContext ctx, XPathSequence p1, XPathSequence p2, string collation)
736                 {
737                         return FnDeepEqualImpl (p1, p2, ctx.GetCulture (collation));
738                 }
739
740                 public static bool FnDeepEqualImpl (XPathSequence p1, XPathSequence p2, CultureInfo collation)
741                 {
742                         // FIXME: use collation
743                         while (p1.MoveNext ()) {
744                                 if (!p2.MoveNext ())
745                                         return false;
746                                 if (!FnDeepEqualItem (p1.Current, p2.Current, collation))
747                                         return false;
748                         }
749                         if (p2.MoveNext ())
750                                 return false;
751                         return true;
752                 }
753
754                 // FIXME: Actually ValueEQ() should consider collation.
755                 [MonoTODO]
756                 private static bool FnDeepEqualItem (XPathItem i1, XPathItem i2, CultureInfo collation)
757                 {
758                         XPathAtomicValue av1 = i1 as XPathAtomicValue;
759                         XPathAtomicValue av2 = i1 as XPathAtomicValue;
760                         if (av1 != null && av2 != null) {
761                                 try {
762                                         return XQueryComparisonOperator.ValueEQ (av1, av2);
763                                 } catch (XmlQueryException) {
764                                         // not-allowed comparison never raises
765                                         // an error here, just return false.
766                                         return false;
767                                 }
768                         }
769                         else if (av1 != null || av2 != null)
770                                 return false;
771
772                         XPathNavigator n1 = i1 as XPathNavigator;
773                         XPathNavigator n2 = i2 as XPathNavigator;
774                         if (n1.NodeType != n2.NodeType)
775                                 return false;
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;
789                         }
790                         return false;
791                 }
792
793                 public static int FnCount (XPathSequence e)
794                 {
795                         if (e == null)
796                                 return 0;
797                         return e.Count;
798                 }
799
800                 [MonoTODO]
801                 public static object FnAvg (XPathSequence e)
802                 {
803                         if (!e.MoveNext ())
804                                 return null;
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);
819                         }
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.");
821                 }
822
823                 private static TimeSpan FnAvgDayTimeDuration (XPathSequence e)
824                 {
825                         throw new NotImplementedException ();
826                 }
827
828                 private static TimeSpan FnAvgYearMonthDuration (XPathSequence e)
829                 {
830                         throw new NotImplementedException ();
831                 }
832
833                 private static TimeSpan FnAvgDecimal (XPathSequence e)
834                 {
835                         throw new NotImplementedException ();
836                 }
837
838                 private static TimeSpan FnAvgInteger (XPathSequence e)
839                 {
840                         throw new NotImplementedException ();
841                 }
842
843                 private static TimeSpan FnAvgFloat (XPathSequence e)
844                 {
845                         throw new NotImplementedException ();
846                 }
847
848                 private static TimeSpan FnAvgDouble (XPathSequence e)
849                 {
850                         throw new NotImplementedException ();
851                 }
852
853                 [MonoTODO]
854                 public static object FnMax (XQueryContext ctx, XPathSequence e)
855                 {
856                         throw new NotImplementedException ();
857                 }
858
859                 [MonoTODO]
860                 public static object FnMax (XQueryContext ctx, XPathSequence e, string collation)
861                 {
862                         throw new NotImplementedException ();
863                 }
864
865                 [MonoTODO]
866                 public static object FnMin (XQueryContext ctx, XPathSequence e)
867                 {
868                         throw new NotImplementedException ();
869                 }
870
871                 [MonoTODO]
872                 public static object FnMin (XQueryContext ctx, XPathSequence e, string collation)
873                 {
874                         throw new NotImplementedException ();
875                 }
876
877                 [MonoTODO]
878                 public static object FnSum (XPathSequence e)
879                 {
880                         throw new NotImplementedException ();
881                 }
882
883                 [MonoTODO]
884                 public static object FnSum (XPathSequence e, XPathItem zero)
885                 {
886                         throw new NotImplementedException ();
887                 }
888
889                 public static XPathNavigator FnId (XQueryContext ctx, string id)
890                 {
891                         return FnId (id, ctx.CurrentNode);
892                 }
893
894                 public static XPathNavigator FnId (string id, XPathNavigator nav)
895                 {
896                         XPathNavigator node = nav.Clone ();
897                         return node.MoveToId (id) ? node : null;
898                 }
899
900                 [MonoTODO]
901                 public static object FnIdRef (XQueryContext ctx, string arg)
902                 {
903                         return FnIdRef (arg, ctx.CurrentNode);
904                 }
905
906                 [MonoTODO]
907                 public static object FnIdRef (string arg, XPathNavigator node)
908                 {
909                         throw new NotImplementedException ();
910                 }
911
912                 public static XPathNavigator FnDoc (XQueryContext ctx, string uri)
913                 {
914                         XmlResolver res = ctx.ContextManager.ExtDocResolver;
915                         string baseUriString = ctx.StaticContext.BaseUri;
916                         Uri baseUri = null;
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;
921                         try {
922                                 XPathDocument doc = new XPathDocument (new XmlValidatingReader (new XmlTextReader (s)), XmlSpace.Preserve);
923                                 return doc.CreateNavigator ();
924                         } finally {
925                                 s.Close ();
926                         }
927                 }
928
929                 public static XPathSequence FnCollection (XQueryContext ctx, string name)
930                 {
931                         return ctx.ResolveCollection (name);
932                 }
933
934                 [XQueryFunctionContext]
935                 public static int FnPosition (XPathSequence current)
936                 {
937                         return current.Position;
938                 }
939
940                 [XQueryFunctionContext]
941                 public static int FnLast (XPathSequence current)
942                 {
943                         return current.Count;
944                 }
945
946                 public static DateTime FnCurrentDateTime ()
947                 {
948                         return DateTime.Now;
949                 }
950
951                 public static DateTime FnCurrentDate ()
952                 {
953                         return DateTime.Today;
954                 }
955
956                 public static DateTime FnCurrentTime ()
957                 {
958                         return new DateTime (DateTime.Now.TimeOfDay.Ticks);
959                 }
960
961                 [MonoTODO]
962                 public static object FnDefaultCollation ()
963                 {
964                         throw new NotImplementedException ();
965                 }
966
967                 [MonoTODO]
968                 public static object FnImplicitTimeZone ()
969                 {
970                         throw new NotImplementedException ();
971                 }
972         }
973 }
974 #endif