2 // XslTransformProcessor.cs
5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 // (C) 2003 Atsushi Enomoto
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
35 using System.Collections;
38 using System.Xml.XPath;
40 using Mono.Xml.Xsl.Operations;
43 using QName = System.Xml.XmlQualifiedName;
45 namespace Mono.Xml.Xsl {
46 internal class XslTransformProcessor {
47 CompiledStylesheet compiledStyle;
51 Stack currentTemplateStack = new Stack ();
54 XsltArgumentList args;
56 bool outputStylesheetXmlns;
57 string currentOutputUri;
59 internal readonly XsltCompiledContext XPathContext;
61 // Store the values of global params
62 internal Hashtable globalVariableTable = new Hashtable ();
64 public XslTransformProcessor (CompiledStylesheet style)
66 this.XPathContext = new XsltCompiledContext (this);
67 this.compiledStyle = style;
68 this.style = style.Style;
71 public void Process (XPathNavigator root, Outputter outputtter, XsltArgumentList args, XmlResolver resolver)
75 this.resolver = resolver != null ? resolver : new XmlUrlResolver ();
76 this.outputStylesheetXmlns = true;
77 this.currentOutputUri = String.Empty;
79 XPathExpression exp = root.Compile (".");
80 PushNodeset (root.Select (exp, this.XPathContext));
82 // have to evaluate the params first, as Global vars may
83 // be dependant on them
86 foreach (XslGlobalVariable v in CompiledStyle.Variables.Values)
88 if (v is XslGlobalParam)
90 object p = args.GetParam(v.Name.Name, v.Name.Namespace);
92 ((XslGlobalParam)v).Override (this, p);
99 foreach (XslGlobalVariable v in CompiledStyle.Variables.Values) {
100 if (args == null || !(v is XslGlobalParam)) {
107 this.PushOutput (outputtter);
108 this.ApplyTemplates (root.Select (exp, this.XPathContext), QName.Empty, null);
112 public CompiledStylesheet CompiledStyle { get { return compiledStyle; }}
113 public XsltArgumentList Arguments {get{return args;}}
115 public MSXslScriptManager ScriptManager {
116 get { return compiledStyle.ScriptManager; }
119 #region Document Resolution
120 public XmlResolver Resolver {get{return resolver;}}
124 public XPathNavigator GetDocument (Uri uri)
126 XPathNavigator result;
128 if (docCache != null) {
129 result = docCache [uri] as XPathNavigator;
131 return result.Clone();
133 docCache = new Hashtable();
136 XmlReader rdr = null;
138 rdr = new XmlTextReader (uri.ToString(), (Stream) resolver.GetEntity (uri, null, null), root.NameTable);
139 XmlValidatingReader xvr = new XmlValidatingReader (rdr);
140 xvr.ValidationType = ValidationType.None;
141 result = new XPathDocument (xvr, XmlSpace.Preserve).CreateNavigator ();
146 docCache [uri] = result.Clone ();
153 Stack outputStack = new Stack ();
155 public Outputter Out { get { return (Outputter)outputStack.Peek(); }}
157 public void PushOutput (Outputter newOutput)
159 this.outputStack.Push (newOutput);
162 public Outputter PopOutput ()
164 Outputter ret = (Outputter)this.outputStack.Pop ();
169 public Hashtable Outputs { get { return compiledStyle.Outputs; }}
171 public XslOutput Output { get { return Outputs [currentOutputUri] as XslOutput; } }
173 public string CurrentOutputUri { get { return currentOutputUri; } }
175 public bool InsideCDataElement { get { return this.XPathContext.IsCData; } }
178 #region AVT StringBuilder
182 bool avtSBlock = false;
185 public StringBuilder GetAvtStringBuilder ()
189 throw new XsltException ("String Builder was locked", null);
194 avtSB = new StringBuilder ();
199 public string ReleaseAvtStringBuilder ()
203 throw new XsltException ("you never locked the string builder", null);
207 string ret = avtSB.ToString ();
213 #region Templates -- Apply/Call
214 Stack paramPassingCache = new Stack ();
216 Hashtable GetParams (ArrayList withParams)
218 if (withParams == null) return null;
221 if (paramPassingCache.Count != 0) {
222 ret = (Hashtable)paramPassingCache.Pop ();
225 ret = new Hashtable ();
227 int len = withParams.Count;
228 for (int i = 0; i < len; i++) {
229 XslVariableInformation param = (XslVariableInformation)withParams [i];
230 ret.Add (param.Name, param.Evaluate (this));
235 public void ApplyTemplates (XPathNodeIterator nodes, QName mode, ArrayList withParams)
238 Hashtable passedParams = GetParams (withParams);
240 while (NodesetMoveNext (nodes)) {
242 XslTemplate t = FindTemplate (CurrentNode, mode);
243 currentTemplateStack.Push (t);
244 t.Evaluate (this, passedParams);
245 currentTemplateStack.Pop ();
249 if (passedParams != null) paramPassingCache.Push (passedParams);
252 public void CallTemplate (QName name, ArrayList withParams)
254 Hashtable passedParams = GetParams (withParams);
256 XslTemplate t = FindTemplate (name);
257 currentTemplateStack.Push (null);
258 t.Evaluate (this, passedParams);
259 currentTemplateStack.Pop ();
261 if (passedParams != null) paramPassingCache.Push (passedParams);
264 public void ApplyImports ()
267 XslTemplate currentTemplate = (XslTemplate)currentTemplateStack.Peek();
268 if (currentTemplate == null)
269 throw new XsltException ("Invalid context for apply-imports", null, CurrentNode);
272 for (int i = currentTemplate.Parent.Imports.Count - 1; i >= 0; i--) {
273 XslStylesheet s = (XslStylesheet)currentTemplate.Parent.Imports [i];
274 t = s.Templates.FindMatch (CurrentNode, currentTemplate.Mode, this);
276 currentTemplateStack.Push (t);
278 currentTemplateStack.Pop ();
283 switch (CurrentNode.NodeType) {
284 case XPathNodeType.Root:
285 case XPathNodeType.Element:
286 if (currentTemplate.Mode == QName.Empty)
287 t = XslDefaultNodeTemplate.Instance;
289 t = new XslDefaultNodeTemplate(currentTemplate.Mode);
292 case XPathNodeType.Attribute:
293 case XPathNodeType.SignificantWhitespace:
294 case XPathNodeType.Text:
295 case XPathNodeType.Whitespace:
296 t = XslDefaultTextTemplate.Instance;
299 case XPathNodeType.Comment:
300 case XPathNodeType.ProcessingInstruction:
301 t = XslEmptyTemplate.Instance;
305 t = XslEmptyTemplate.Instance;
308 currentTemplateStack.Push (t);
310 currentTemplateStack.Pop ();
313 // Outputs Literal namespace nodes described in spec 7.7.1
314 internal void OutputLiteralNamespaceUriNodes (Hashtable nsDecls, ArrayList excludedPrefixes, string localPrefixInCopy)
319 foreach (DictionaryEntry cur in nsDecls) {
320 string name = (string)cur.Key;
321 string value = (string)cur.Value;
323 // See XSLT 1.0 errata E25
324 if (localPrefixInCopy == name)
326 if (localPrefixInCopy != null &&
328 XPathContext.ElementNamespace.Length == 0)
331 // exclude-result-prefixes, see the spec 7.1.1
333 if (style.ExcludeResultPrefixes != null) {
334 foreach (XmlQualifiedName exc in style.ExcludeResultPrefixes) {
335 if (exc.Namespace == value) {
344 if (style.NamespaceAliases [name] != null)
347 switch (value) {//FIXME: compare names by reference
348 case "http://www.w3.org/1999/XSL/Transform":
349 // if ("xsl" == name)
353 case XmlNamespaceManager.XmlnsXml:
354 if (XmlNamespaceManager.PrefixXml == name)
358 case XmlNamespaceManager.XmlnsXmlns:
359 if (XmlNamespaceManager.PrefixXmlns == name)
364 if (excludedPrefixes == null || !excludedPrefixes.Contains (name))
365 Out.WriteNamespaceDecl (name, value);
371 XslTemplate FindTemplate (XPathNavigator node, QName mode)
373 XslTemplate ret = style.Templates.FindMatch (CurrentNode, mode, this);
375 if (ret != null) return ret;
377 switch (node.NodeType) {
378 case XPathNodeType.Root:
379 case XPathNodeType.Element:
380 if (mode == QName.Empty)
381 return XslDefaultNodeTemplate.Instance;
383 return new XslDefaultNodeTemplate(mode);
385 case XPathNodeType.Attribute:
386 case XPathNodeType.SignificantWhitespace:
387 case XPathNodeType.Text:
388 case XPathNodeType.Whitespace:
389 return XslDefaultTextTemplate.Instance;
391 case XPathNodeType.Comment:
392 case XPathNodeType.ProcessingInstruction:
393 return XslEmptyTemplate.Instance;
396 return XslEmptyTemplate.Instance;
400 XslTemplate FindTemplate (QName name)
402 XslTemplate ret = style.Templates.FindTemplate (name);
403 if (ret != null) return ret;
405 throw new XsltException ("Could not resolve named template " + name, null, CurrentNode);
410 public void PushForEachContext ()
412 currentTemplateStack.Push (null);
415 public void PopForEachContext ()
417 currentTemplateStack.Pop ();
421 #region Nodeset Context
422 ArrayList nodesetStack = new ArrayList ();
424 public XPathNodeIterator CurrentNodeset {
425 get { return (XPathNodeIterator) nodesetStack [nodesetStack.Count - 1]; }
428 public XPathNavigator CurrentNode {
430 XPathNavigator nav = CurrentNodeset.Current;
433 // Inside for-each context, CurrentNodeset.Current may be null
434 for (int i = nodesetStack.Count - 2; i >= 0; i--) {
435 nav = ((XPathNodeIterator) nodesetStack [i]).Current;
443 public bool NodesetMoveNext ()
445 return NodesetMoveNext (CurrentNodeset);
448 public bool NodesetMoveNext (XPathNodeIterator iter)
450 if (!iter.MoveNext ())
452 if (iter.Current.NodeType == XPathNodeType.Whitespace && !XPathContext.PreserveWhitespace (iter.Current))
453 return NodesetMoveNext (iter);
457 public void PushNodeset (XPathNodeIterator itr)
459 nodesetStack.Add (itr);
462 public void PopNodeset ()
464 nodesetStack.RemoveAt (nodesetStack.Count - 1);
470 public bool Matches (Pattern p, XPathNavigator n)
472 return p.Matches (n, this.XPathContext);
475 public object Evaluate (XPathExpression expr)
477 XPathNodeIterator itr = CurrentNodeset;
478 return itr.Current.Evaluate (expr, itr, XPathContext);
481 public string EvaluateString (XPathExpression expr)
483 XPathNodeIterator itr = CurrentNodeset;
484 return itr.Current.EvaluateString (expr, itr, XPathContext);
487 public bool EvaluateBoolean (XPathExpression expr)
489 XPathNodeIterator itr = CurrentNodeset;
490 return itr.Current.EvaluateBoolean (expr, itr, XPathContext);
493 public double EvaluateNumber (XPathExpression expr)
495 XPathNodeIterator itr = CurrentNodeset;
496 return itr.Current.EvaluateNumber (expr, itr, XPathContext);
499 public XPathNodeIterator Select (XPathExpression expr)
501 return CurrentNodeset.Current.Select (expr, XPathContext);
506 public XslAttributeSet ResolveAttributeSet (QName name)
508 return CompiledStyle.ResolveAttributeSet (name);
511 #region Variable Stack
512 Stack variableStack = new Stack ();
513 object [] currentStack;
514 public int StackItemCount {
516 if (currentStack == null)
518 for (int i = 0; i < currentStack.Length; i++)
519 if (currentStack [i] == null)
521 return currentStack.Length;
525 public object GetStackItem (int slot)
527 return currentStack [slot];
530 public void SetStackItem (int slot, object o)
532 currentStack [slot] = o;
535 public void PushStack (int stackSize)
537 variableStack.Push (currentStack);
538 currentStack = new object [stackSize];
541 public void PopStack ()
543 currentStack = (object[])variableStack.Pop();
549 Hashtable busyTable = new Hashtable ();
550 static object busyObject = new object ();
552 public void SetBusy (object o)
554 busyTable [o] = busyObject;
557 public void SetFree (object o)
559 busyTable.Remove (o);
562 public bool IsBusy (object o)
564 return busyTable [o] == busyObject;
568 public bool PushElementState (string prefix, string name, string ns, bool preserveWhitespace)
570 bool b = IsCData (name, ns);
571 XPathContext.PushScope ();
572 Out.InsideCDataSection = XPathContext.IsCData = b;
573 XPathContext.WhitespaceHandling = true;//preserveWhitespace;
574 XPathContext.ElementPrefix = prefix;
575 XPathContext.ElementNamespace = ns;
579 bool IsCData (string name, string ns)
581 for (int i = 0; i < Output.CDataSectionElements.Length; i++) {
582 XmlQualifiedName qname = Output.CDataSectionElements [i];
583 if (qname.Name == name && qname.Namespace == ns) {
590 public void PopCDataState (bool isCData)
592 XPathContext.PopScope ();
593 Out.InsideCDataSection = XPathContext.IsCData;
596 public bool PreserveWhitespace ()
598 // return XPathContext.PreserveWhitespace (CurrentNode);
599 return XPathContext.Whitespace;