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