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