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