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