TARGET_JVM: refer nunit from mono tree
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / Compiler.cs
1 //
2 // Compiler.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //      
8 // (C) 2003 Ben Maurer
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Security.Policy;
36 using System.Xml;
37 using System.Xml.XPath;
38 using System.Xml.Xsl;
39 using System.IO;
40
41 using Mono.Xml.Xsl.Operations;
42 using Mono.Xml.XPath;
43
44 using QName = System.Xml.XmlQualifiedName;
45
46 namespace Mono.Xml.Xsl 
47 {
48         internal class CompiledStylesheet {
49                 XslStylesheet style;
50                 Hashtable globalVariables;
51                 Hashtable attrSets;
52                 XmlNamespaceManager nsMgr;
53                 Hashtable keys;
54                 Hashtable outputs;
55                 Hashtable decimalFormats;
56                 MSXslScriptManager msScripts;
57                 
58                 public CompiledStylesheet (XslStylesheet style, Hashtable globalVariables, Hashtable attrSets, XmlNamespaceManager nsMgr, Hashtable keys, Hashtable outputs, Hashtable decimalFormats,
59                         MSXslScriptManager msScripts)
60                 {
61                         this.style = style;
62                         this.globalVariables = globalVariables;
63                         this.attrSets = attrSets;
64                         this.nsMgr = nsMgr;
65                         this.keys = keys;
66                         this.outputs = outputs;
67                         this.decimalFormats = decimalFormats;
68                         this.msScripts = msScripts;
69                 }
70                 public Hashtable Variables {get{return globalVariables;}}
71                 public XslStylesheet Style { get { return style; }}
72                 public XmlNamespaceManager NamespaceManager {get{return nsMgr;}}
73                 public Hashtable Keys {get { return keys;}}
74                 public Hashtable Outputs { get { return outputs; }}
75                 
76                 public MSXslScriptManager ScriptManager {
77                         get { return msScripts; }
78                 }
79                 
80                 
81                 public XslDecimalFormat LookupDecimalFormat (QName name)
82                 {
83                         XslDecimalFormat ret = decimalFormats [name] as XslDecimalFormat;
84                         if (ret == null && name == QName.Empty)
85                                 return XslDecimalFormat.Default;
86                         return ret;
87                 }
88                 
89                 public XslGeneralVariable ResolveVariable (QName name)
90                 {
91                         return (XslGeneralVariable)globalVariables [name];
92                 }
93                 
94                 public XslKey ResolveKey (QName name)
95                 {
96                         return (XslKey) keys [name];
97                 }
98                 
99                 public XslAttributeSet ResolveAttributeSet (QName name)
100                 {
101                         return (XslAttributeSet)attrSets [name];
102                 }
103         }
104         
105         internal class Compiler : IStaticXsltContext {
106                 public const string XsltNamespace = "http://www.w3.org/1999/XSL/Transform";
107                         
108                 ArrayList inputStack = new ArrayList ();
109                 XPathNavigator currentInput;
110                 
111                 Stack styleStack = new Stack ();
112                 XslStylesheet currentStyle;
113                 
114                 Hashtable keys = new Hashtable ();
115                 Hashtable globalVariables = new Hashtable ();
116                 Hashtable attrSets = new Hashtable ();
117         
118                 XmlNamespaceManager nsMgr = new XmlNamespaceManager (new NameTable ());
119                                 
120                 XmlResolver res;
121                 Evidence evidence;
122
123                 XslStylesheet rootStyle;
124                 Hashtable outputs = new Hashtable ();
125                 bool keyCompilationMode;        
126                 string stylesheetVersion;
127
128                 public CompiledStylesheet Compile (XPathNavigator nav, XmlResolver res, Evidence evidence)
129                 {
130                         this.xpathParser = new XPathParser (this);
131                         this.patternParser = new XsltPatternParser (this);
132                         this.res = res;
133                         if (res == null)
134                                 this.res = new XmlUrlResolver ();
135                         this.evidence = evidence;
136
137                         // reject empty document.
138                         if (nav.NodeType == XPathNodeType.Root && !nav.MoveToFirstChild ())
139                                 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element", null, nav);
140                         while (nav.NodeType != XPathNodeType.Element) nav.MoveToNext();
141                         
142                         stylesheetVersion = nav.GetAttribute ("version", 
143                                 (nav.NamespaceURI != XsltNamespace) ?
144                                 XsltNamespace : String.Empty);
145                         outputs [""] = new XslOutput ("", stylesheetVersion);
146                                 
147                         PushInputDocument (nav);
148                         if (nav.MoveToFirstNamespace (XPathNamespaceScope.ExcludeXml))
149                         {
150                                 do {
151                                         nsMgr.AddNamespace (nav.LocalName, nav.Value);
152                                 } while (nav.MoveToNextNamespace (XPathNamespaceScope.ExcludeXml));
153                                 nav.MoveToParent ();
154                         }
155                         try {
156                                 rootStyle = new XslStylesheet ();
157                                 rootStyle.Compile (this);
158                         } catch (XsltCompileException) {
159                                 throw;
160                         } catch (Exception x) {
161                                 throw new XsltCompileException ("XSLT compile error. " + x.Message, x,  Input);
162                         }
163                         
164                         return new CompiledStylesheet (rootStyle, globalVariables, attrSets, nsMgr, keys, outputs, decimalFormats, msScripts);
165                 }
166                 
167                 MSXslScriptManager msScripts = new MSXslScriptManager ();
168                 public MSXslScriptManager ScriptManager {
169                         get { return msScripts; }
170                 }
171
172                 public bool KeyCompilationMode {
173                         get { return keyCompilationMode; }
174                         set { keyCompilationMode = value; }
175                 }
176
177                 internal Evidence Evidence {
178                         get { return evidence; }
179                 }
180                 
181 #region Input
182                 public XPathNavigator Input {
183                         get { return currentInput; }
184                 }
185                 
186                 public XslStylesheet CurrentStylesheet {
187                         get { return currentStyle; }
188                 }
189
190                 public void PushStylesheet (XslStylesheet style)
191                 {
192                         if (currentStyle != null) styleStack.Push (currentStyle);
193                         currentStyle = style;
194                 }
195                 
196                 public void PopStylesheet ()
197                 {
198                         if (styleStack.Count == 0)
199                                 currentStyle = null;
200                         else
201                                 currentStyle = (XslStylesheet)styleStack.Pop ();
202                 }
203                 
204                 public void PushInputDocument (string url)
205                 {
206                         // todo: detect recursion
207                         Uri baseUriObj = (Input.BaseURI == String.Empty) ? null : new Uri (Input.BaseURI);
208                         Uri absUri = res.ResolveUri (baseUriObj, url);
209                         string absUriString = absUri != null ? absUri.ToString () : String.Empty;
210                         using (Stream s = (Stream)res.GetEntity (absUri, null, typeof(Stream)))
211                         {
212                                 if (s == null)
213                                         throw new XsltCompileException ("Can not access URI " + absUri.ToString (), null, Input);
214                                 XmlValidatingReader vr = new XmlValidatingReader (new XmlTextReader (absUriString, s, nsMgr.NameTable));
215                                 vr.ValidationType = ValidationType.None;
216                                 XPathNavigator n = new XPathDocument (vr, XmlSpace.Preserve).CreateNavigator ();
217                                 vr.Close ();
218                                 n.MoveToFirstChild ();
219                                 do {
220                                         if (n.NodeType == XPathNodeType.Element)
221                                                 break;
222                                 } while (n.MoveToNext ());
223                                 PushInputDocument (n);
224                         }
225                 }
226                 
227                 public void PushInputDocument (XPathNavigator nav)
228                 {
229                         // Inclusion nest check
230                         IXmlLineInfo li = currentInput as IXmlLineInfo;
231                         bool hasLineInfo = (li != null && !li.HasLineInfo ());
232                         for (int i = 0; i < inputStack.Count; i++) {
233                                 XPathNavigator cur = (XPathNavigator) inputStack [i];
234                                 if (cur.BaseURI == nav.BaseURI) {
235                                         throw new XsltCompileException (null,
236                                                 currentInput.BaseURI, 
237                                                 hasLineInfo ? li.LineNumber : 0,
238                                                 hasLineInfo ? li.LinePosition : 0);
239                                 }
240                         }
241                         if (currentInput != null)
242                                 inputStack.Add (currentInput);
243                         currentInput = nav;
244                 }
245                 
246                 public void PopInputDocument ()
247                 {
248                         int last = inputStack.Count - 1;
249                         currentInput = (XPathNavigator) inputStack [last];
250                         inputStack.RemoveAt (last);
251                 }
252                 
253                 public QName ParseQNameAttribute (string localName)
254                 {
255                         return ParseQNameAttribute (localName, String.Empty);
256                 }
257                 public QName ParseQNameAttribute (string localName, string ns)
258                 {
259                         return XslNameUtil.FromString (Input.GetAttribute (localName, ns), Input);
260                 }
261                 
262                 public QName [] ParseQNameListAttribute (string localName)
263                 {
264                         return ParseQNameListAttribute (localName, String.Empty);
265                 }
266                 
267                 public QName [] ParseQNameListAttribute (string localName, string ns)
268                 {
269                         string s = GetAttribute (localName, ns);
270                         if (s == null) return null;
271                                 
272                         string [] names = s.Split (new char [] {' ', '\r', '\n', '\t'});
273                         int count=0;
274                         for (int i=0; i<names.Length; i++)
275                                 if (names[i].Length != 0)
276                                         count ++;
277
278                         QName [] ret = new QName [count];
279                         
280                         for (int i = 0, j=0; i < names.Length; i++)
281                                 if (names[i].Length != 0)
282                                         ret [j++] = XslNameUtil.FromString (names [i], Input);
283                         
284                         return ret;
285                 }
286                 
287                 public bool ParseYesNoAttribute (string localName, bool defaultVal)
288                 {
289                         return ParseYesNoAttribute (localName, String.Empty, defaultVal);
290                 }
291                 
292                 public bool ParseYesNoAttribute (string localName, string ns, bool defaultVal)
293                 {
294                         string s = GetAttribute (localName, ns);
295
296                         switch (s) {
297                         case null: return defaultVal;
298                         case "yes": return true;
299                         case "no": return false;
300                         default:
301                                 throw new XsltCompileException ("Invalid value for " + localName, null, Input);
302                         }
303                 }
304                 
305                 public string GetAttribute (string localName)
306                 {
307                         return GetAttribute (localName, String.Empty);
308                 }
309                 
310                 public string GetAttribute (string localName, string ns)
311                 {
312                         if (!Input.MoveToAttribute (localName, ns))
313                                 return null;
314                         
315                         string ret = Input.Value;
316                         Input.MoveToParent ();
317                         return ret;
318                 }
319                 public XslAvt ParseAvtAttribute (string localName)
320                 {
321                         return ParseAvtAttribute (localName, String.Empty);
322                 }
323                 public XslAvt ParseAvtAttribute (string localName, string ns)
324                 {
325                         return ParseAvt (GetAttribute (localName, ns));
326                 }
327                 
328                 public void AssertAttribute (string localName)
329                 {
330                         AssertAttribute (localName, "");
331                 }
332                 public void AssertAttribute (string localName, string ns)
333                 {
334                         if (Input.GetAttribute (localName, ns) == null)
335                                 throw new XsltCompileException ("Was expecting the " + localName + " attribute", null, Input);
336                 }
337                 
338                 public XslAvt ParseAvt (string s)
339                 {
340                         if (s == null) return null;
341                         return new XslAvt (s, this);
342                 }
343                 
344                 
345 #endregion
346 #region Compile
347                 public Pattern CompilePattern (string pattern, XPathNavigator loc)
348                 {
349                         if (pattern == null || pattern == "") return null;
350                         Pattern p = Pattern.Compile (pattern, this);
351                         if (p == null)
352                                 throw new XsltCompileException (String.Format ("Invalid pattern '{0}'", pattern), null, loc);
353                         
354                         return p;
355                 }
356
357                 internal XPathParser xpathParser;
358                 internal XsltPatternParser patternParser;
359                 internal CompiledExpression CompileExpression (string expression)
360                 {
361                         return CompileExpression (expression, false);
362                 }
363
364                 internal CompiledExpression CompileExpression (string expression, bool isKey)
365                 {
366                         if (expression == null || expression == "") return null;
367
368                         Expression expr = xpathParser.Compile (expression);
369                         if (isKey)
370                                 expr = new ExprKeyContainer (expr);
371                         CompiledExpression e = new CompiledExpression (expression, expr);
372
373                         return e;
374                 }
375                 
376                 public XslOperation CompileTemplateContent ()
377                 {
378                         return CompileTemplateContent (XPathNodeType.All, false);
379                 }
380
381                 public XslOperation CompileTemplateContent (XPathNodeType parentType)
382                 {
383                         return CompileTemplateContent (parentType, false);
384                 }
385                 
386                 public XslOperation CompileTemplateContent (XPathNodeType parentType, bool xslForEach)
387                 {
388                         return new XslTemplateContent (this, parentType, xslForEach);
389                 }
390 #endregion
391 #region Variables
392                 public void AddGlobalVariable (XslGlobalVariable var)
393                 {
394                         globalVariables [var.Name] = var;
395                 }
396
397                 public void AddKey (XslKey key)
398                 {
399                         keys [key.Name] = key;
400                 }
401
402                 public void AddAttributeSet (XslAttributeSet set)
403                 {
404                         XslAttributeSet existing = attrSets [set.Name] as XslAttributeSet;
405                         // The latter set will have higher priority
406                         if (existing != null) {
407                                 existing.Merge (set);
408                                 attrSets [set.Name] = existing;
409                         }
410                         else
411                                 attrSets [set.Name] = set;
412                 }
413                 
414                 VariableScope curVarScope;
415                 
416                 public void PushScope ()
417                 {
418                         curVarScope = new VariableScope (curVarScope);
419                 }
420                 
421                 public VariableScope PopScope ()
422                 {
423                         curVarScope.giveHighTideToParent ();
424                         VariableScope cur = curVarScope;
425                         curVarScope = curVarScope.Parent;
426                         return cur;
427                 }
428                 
429                 public int AddVariable (XslLocalVariable v)
430                 {
431                         if (curVarScope == null)
432                                 throw new XsltCompileException ("Not initialized variable", null, Input);
433                         
434                         return curVarScope.AddVariable (v);
435                 }
436                 
437                 public VariableScope CurrentVariableScope { get { return curVarScope; }}
438 #endregion
439                 
440 #region Scope (version, {excluded, extension} namespaces)
441                 [MonoTODO ("This will work, but is *very* slow")]
442                 public bool IsExtensionNamespace (string nsUri)
443                 {
444                         if (nsUri == XsltNamespace) return true;
445                                 
446                         XPathNavigator nav = Input.Clone ();
447                         XPathNavigator nsScope = nav.Clone ();
448                         do {
449                                 bool isXslt = nav.NamespaceURI == XsltNamespace;
450                                 nsScope.MoveTo (nav);
451                                 if (nav.MoveToFirstAttribute ()) {
452                                         do {
453                                                 if (nav.LocalName == "extension-element-prefixes" &&
454                                                         nav.NamespaceURI == (isXslt ? String.Empty : XsltNamespace))
455                                                 {
456                                                 
457                                                         foreach (string ns in nav.Value.Split (' '))
458                                                                 if (nsScope.GetNamespace (ns == "#default" ? "" : ns) == nsUri)
459                                                                         return true;
460                                                 }
461                                         } while (nav.MoveToNextAttribute ());
462                                         nav.MoveToParent ();
463                                 }
464                         } while (nav.MoveToParent ());
465                                 
466                         return false;
467                 }
468                 
469                 public Hashtable GetNamespacesToCopy ()
470                 {
471                         Hashtable ret = new Hashtable ();
472                         
473                         XPathNavigator nav = Input.Clone ();
474                         XPathNavigator nsScope = nav.Clone ();
475                         
476                         if (nav.MoveToFirstNamespace (XPathNamespaceScope.ExcludeXml)) {
477                                 do {
478                                         if (nav.Value != XsltNamespace && !ret.Contains (nav.Name))
479                                                 ret.Add (nav.Name, nav.Value);
480                                 } while (nav.MoveToNextNamespace (XPathNamespaceScope.ExcludeXml));
481                                 nav.MoveToParent ();
482                         }
483                         
484                         do {
485                                 bool isXslt = nav.NamespaceURI == XsltNamespace;
486                                 nsScope.MoveTo (nav);
487
488                                 if (nav.MoveToFirstAttribute ()) {
489                                         do {
490                                                 if ((nav.LocalName == "extension-element-prefixes" || nav.LocalName == "exclude-result-prefixes") &&
491                                                         nav.NamespaceURI == (isXslt ? String.Empty : XsltNamespace))
492                                                 {
493                                                         foreach (string ns in nav.Value.Split (' ')) {
494                                                                 string realNs = ns == "#default" ? "" : ns;
495                                                                 
496                                                                 if ((string)ret [realNs] == nsScope.GetNamespace (realNs))
497                                                                         ret.Remove (realNs);
498                                                         }
499                                                 }
500                                         } while (nav.MoveToNextAttribute ());
501                                         nav.MoveToParent ();
502                                 }
503                         } while (nav.MoveToParent ());
504                         
505                         return ret;
506                 }
507 #endregion
508                 
509 #region Decimal Format
510                 Hashtable decimalFormats = new Hashtable ();
511                 
512                 public void CompileDecimalFormat ()
513                 {
514                         QName nm = ParseQNameAttribute ("name");
515                         try {
516                                 if (nm.Name != String.Empty)
517                                         XmlConvert.VerifyNCName (nm.Name);
518                         } catch (XmlException ex) {
519                                 throw new XsltCompileException ("Invalid qualified name", ex, Input);
520                         }
521                         XslDecimalFormat df = new XslDecimalFormat (this);
522                         
523                         if (decimalFormats.Contains (nm))
524                                 ((XslDecimalFormat)decimalFormats [nm]).CheckSameAs (df);
525                         else
526                                 decimalFormats [nm] = df;
527                 }
528 #endregion
529 #region Static XSLT context
530                 Expression IStaticXsltContext.TryGetVariable (string nm)
531                 {
532                         if (curVarScope == null)
533                                 return null;
534                         
535                         XslLocalVariable var = curVarScope.ResolveStatic (XslNameUtil.FromString (nm, Input));
536                         
537                         if (var == null)
538                                 return null;
539                         
540                         return new XPathVariableBinding (var);
541                 }
542                 
543                 Expression IStaticXsltContext.TryGetFunction (QName name, FunctionArguments args)
544                 {
545                         string ns = LookupNamespace (name.Namespace);
546                         if (ns == XslStylesheet.MSXsltNamespace && name.Name == "node-set")
547                                 return new MSXslNodeSet (args);
548                         
549                         if (ns != "")
550                                 return null;
551
552                         switch (name.Name) {
553                                 case "current": return new XsltCurrent (args);
554                                 case "unparsed-entity-uri": return new XsltUnparsedEntityUri (args);
555                                 case "element-available": return new XsltElementAvailable (args, this);
556                                 case "system-property": return new XsltSystemProperty (args, this);
557                                 case "function-available": return new XsltFunctionAvailable (args, this);
558                                 case "generate-id": return new XsltGenerateId (args);
559                                 case "format-number": return new XsltFormatNumber (args, this);
560                                 case "key":
561                                         if (KeyCompilationMode)
562                                                 throw new XsltCompileException ("Cannot use key() function inside key definition", null, this.Input);
563                                         return new XsltKey (args, this);
564                                 case "document": return new XsltDocument (args, this);
565                         }
566                         
567                         return null;
568                 }
569                 
570                 QName IStaticXsltContext.LookupQName (string s)
571                 {
572                         return XslNameUtil.FromString (s, Input);
573                 }
574
575                 public string LookupNamespace (string prefix)
576                 {
577                         if (prefix == "" || prefix == null)
578                                 return "";
579                         
580                         XPathNavigator n = Input;
581                         if (Input.NodeType == XPathNodeType.Attribute) {
582                                 n = Input.Clone ();
583                                 n.MoveToParent ();
584                         }
585
586                         return n.GetNamespace (prefix);
587                 }
588 #endregion
589                 public void CompileOutput ()
590                 {
591                         XPathNavigator n = Input;
592                         string uri = n.GetAttribute ("href", "");
593                         XslOutput output = outputs [uri] as XslOutput;
594                         if (output == null) {
595                                 output = new XslOutput (uri, stylesheetVersion);
596                                 outputs.Add (uri, output);
597                         }
598                         output.Fill (n);
599                 }
600         }
601         
602         internal class VariableScope {
603                 ArrayList variableNames;
604                 Hashtable variables;
605                 VariableScope parent;
606                 int nextSlot = 0;
607                 int highTide = 0; // this will be the size of the stack frame
608                 
609                 internal void giveHighTideToParent ()
610                 {
611                         if (parent != null)
612                                 parent.highTide = System.Math.Max (VariableHighTide, parent.VariableHighTide);
613                 }
614
615                 public int VariableHighTide { get { return  System.Math.Max (highTide, nextSlot); }}
616                 
617                 public VariableScope (VariableScope parent)
618                 {
619                         this.parent = parent;
620                         if (parent != null)
621                                 this.nextSlot = parent.nextSlot;
622                 }
623                 
624                 public VariableScope Parent { get { return parent; }}
625                 
626                 public int AddVariable (XslLocalVariable v)
627                 {
628                         if (variables == null) {
629                                 variableNames = new ArrayList ();
630                                 variables = new Hashtable ();
631                         }
632                         variables [v.Name] = v;
633                         int idx = variableNames.IndexOf (v.Name);
634                         if (idx >= 0)
635                                 return idx;
636                         variableNames.Add (v.Name);
637                         return nextSlot++;
638                 }
639                 
640                 public XslLocalVariable ResolveStatic (QName name)
641                 {
642                         for (VariableScope s = this; s != null; s = s.Parent) {
643                                 if (s.variables == null) continue;
644                                 XslLocalVariable v = s.variables [name] as XslLocalVariable;
645                                 if (v != null) return v;
646                         }
647                         return null;
648                 }
649                 
650                 public XslLocalVariable Resolve (XslTransformProcessor p, QName name)
651                 {
652                         for (VariableScope s = this; s != null; s = s.Parent) {
653                                 if (s.variables == null) continue;
654                                 XslLocalVariable v = s.variables [name] as XslLocalVariable;
655                                 if (v != null && v.IsEvaluated (p))
656                                         return v;
657
658                         }
659                         return null;
660                 }
661         }
662         
663         internal class Sort {
664                 string lang;
665                 XmlDataType dataType;
666                 XmlSortOrder order;
667                 XmlCaseOrder caseOrder;
668                 
669                 XslAvt langAvt, dataTypeAvt, orderAvt, caseOrderAvt;
670                 XPathExpression expr;
671                         
672                 public Sort (Compiler c)
673                 {
674                         expr = c.CompileExpression (c.GetAttribute ("select"));
675                         if (expr == null)
676                                 expr = c.CompileExpression ("string(.)");
677                         
678                         langAvt = c.ParseAvtAttribute ("lang");
679                         dataTypeAvt = c.ParseAvtAttribute ("data-type");
680                         orderAvt = c.ParseAvtAttribute ("order");
681                         caseOrderAvt = c.ParseAvtAttribute ("case-order");
682                         
683                         // Precalc whatever we can
684                         lang = ParseLang (XslAvt.AttemptPreCalc (ref langAvt));
685                         dataType = ParseDataType (XslAvt.AttemptPreCalc (ref dataTypeAvt));
686                         order = ParseOrder (XslAvt.AttemptPreCalc (ref orderAvt));
687                         caseOrder = ParseCaseOrder (XslAvt.AttemptPreCalc (ref caseOrderAvt));
688                 }
689
690                 public bool IsContextDependent {
691                         get { return orderAvt != null || caseOrderAvt != null || langAvt != null || dataTypeAvt != null; }
692                 }
693
694                 string ParseLang (string value)
695                 {
696                         return value;
697                 }
698
699                 XmlDataType ParseDataType (string value)
700                 {
701                         switch (value)
702                         {
703                         case "number":
704                                 return XmlDataType.Number;
705                         case "text":
706                         case null:
707                         default:
708                                 return XmlDataType.Text;
709                         }
710                 }
711
712                 XmlSortOrder ParseOrder (string value)
713                 {
714                         switch (value)
715                         {
716                         case "descending":
717                                 return XmlSortOrder.Descending;
718                         case "ascending":
719                         case null:
720                         default:
721                                 return XmlSortOrder.Ascending;
722                         }         
723                 }
724
725                 XmlCaseOrder ParseCaseOrder (string value)
726                 {
727                         switch (value)
728                         {
729                         case "upper-first":
730                                 return XmlCaseOrder.UpperFirst;
731                         case "lower-first":
732                                 return XmlCaseOrder.LowerFirst;
733                         case null:
734                         default:
735                                 return XmlCaseOrder.None;
736                         }         
737                 }
738                 
739                 
740                 public void AddToExpr (XPathExpression e, XslTransformProcessor p)
741                 {
742                         e.AddSort (
743                                 expr,
744                                 orderAvt == null ? order : ParseOrder (orderAvt.Evaluate (p)),
745                                 caseOrderAvt == null ? caseOrder: ParseCaseOrder (caseOrderAvt.Evaluate (p)),
746                                 langAvt == null ? lang : ParseLang (langAvt.Evaluate (p)),
747                                 dataTypeAvt == null ? dataType : ParseDataType (dataTypeAvt.Evaluate (p))
748                         );
749                 }
750
751                 public XPathSorter ToXPathSorter (XslTransformProcessor p)
752                 {
753                         return new XPathSorter (expr, 
754                                 orderAvt == null ? order : ParseOrder (orderAvt.Evaluate (p)),
755                                 caseOrderAvt == null ? caseOrder: ParseCaseOrder (caseOrderAvt.Evaluate (p)),
756                                 langAvt == null ? lang : ParseLang (langAvt.Evaluate (p)),
757                                 dataTypeAvt == null ? dataType : ParseDataType (dataTypeAvt.Evaluate (p))
758                         );
759                 }
760         }
761         
762         internal class XslNameUtil
763         {
764                 public static QName [] FromListString (string names, XPathNavigator current)
765                 {
766                         string [] nameArray = names.Split (XmlChar.WhitespaceChars);
767                         int idx = 0;
768                         for (int i = 0; i < nameArray.Length; i++)
769                                 if (nameArray [i] != String.Empty)
770                                         idx++;
771
772                         XmlQualifiedName [] qnames = new XmlQualifiedName [idx];
773
774                         idx = 0;
775                         for (int i = 0; i < nameArray.Length; i++)
776                                 if (nameArray [i] != String.Empty)
777                                         qnames [idx++] = FromString (nameArray [i], current, true);
778
779                         return qnames;
780                 }
781
782                 public static QName FromString (string name, XPathNavigator current)
783                 {
784                         return FromString (name, current, false);
785                 }
786
787                 public static QName FromString (string name, XPathNavigator current, bool useDefaultXmlns)
788                 {
789                         if (current.NodeType == XPathNodeType.Attribute)
790                                 (current = current.Clone ()).MoveToParent ();
791                         
792                         int colon = name.IndexOf (':');
793                         if (colon > 0)
794                                 return new QName (name.Substring (colon+ 1), current.GetNamespace (name.Substring (0, colon)));
795                         else if (colon < 0)
796                                 return new QName (name, useDefaultXmlns ? current.GetNamespace (String.Empty) : "");
797                         else
798                                 throw new ArgumentException ("Invalid name: " + name);
799                 }
800
801                 public static QName FromString (string name, Hashtable nsDecls)
802                 {
803                         int colon = name.IndexOf (':');
804                         if (colon > 0)
805                                 return new QName (name.Substring (colon + 1), nsDecls [name.Substring (0, colon)] as string);
806                         else if (colon < 0)
807                                 return new QName (name,
808                                         nsDecls.ContainsKey (String.Empty) ?
809                                         (string) nsDecls [String.Empty] : String.Empty);
810                         else
811                                 throw new ArgumentException ("Invalid name: " + name);
812                 }
813
814                 public static QName FromString (string name, IStaticXsltContext ctx)
815                 {
816                         int colon = name.IndexOf (':');
817                         if (colon > 0)
818                                 return new QName (name.Substring (colon + 1), ctx.LookupNamespace (name.Substring (0, colon)));
819                         else if (colon < 0)
820                                 // Default namespace is not used for unprefixed names.
821                                 return new QName (name, "");
822                         else
823                                 throw new ArgumentException ("Invalid name: " + name);
824                 }
825
826                 public static QName FromString (string name, XmlNamespaceManager ctx)
827                 {
828                         int colon = name.IndexOf (':');
829                         if (colon > 0)
830                                 return new QName (name.Substring (colon + 1), ctx.LookupNamespace (name.Substring (0, colon), false));
831                         else if (colon < 0)
832                                 // Default namespace is not used for unprefixed names.
833                                 return new QName (name, "");
834                         else
835                                 throw new ArgumentException ("Invalid name: " + name);
836                 }
837                 
838                 public static string LocalNameOf (string name)
839                 {
840                         int colon = name.IndexOf (':');
841                         if (colon > 0)
842                                 return name.Substring (colon + 1);
843                         else if (colon < 0)
844                                 return name;
845                         else
846                                 throw new ArgumentException ("Invalid name: " + name);
847                 }
848         }
849 }