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