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 // This is also done after the transformation, just for reuse after exception.
80 foreach (XslKey key
in style
.Keys
.Values
)
83 XPathExpression exp
= root
.Compile (".");
84 PushNodeset (root
.Select (exp
, this.XPathContext
));
86 foreach (XslGlobalVariable v
in CompiledStyle
.Variables
.Values
) {
87 if (args
!= null && v
is XslGlobalParam
) {
88 object p
= args
.GetParam(v
.Name
.Name
, v
.Name
.Namespace
);
90 ((XslGlobalParam
)v
).Override (this, p
);
99 this.PushOutput (outputtter
);
100 this.ApplyTemplates (root
.Select (exp
, this.XPathContext
), QName
.Empty
, null);
103 foreach (XslKey key
in style
.Keys
.Values
)
104 key
.ClearKeyTable ();
107 public CompiledStylesheet CompiledStyle { get { return compiledStyle; }}
108 public XsltArgumentList Arguments {get{return args;}}
110 public MSXslScriptManager ScriptManager
{
111 get { return compiledStyle.ScriptManager; }
114 #region Document Resolution
115 public XmlResolver Resolver {get{return resolver;}}
119 public XPathNavigator
GetDocument (Uri uri
)
121 XPathNavigator result
;
123 if (docCache
!= null) {
124 result
= docCache
[uri
] as XPathNavigator
;
126 return result
.Clone();
128 docCache
= new Hashtable();
131 XmlReader rdr
= null;
133 rdr
= new XmlTextReader (uri
.ToString(), (Stream
) resolver
.GetEntity (uri
, null, null), root
.NameTable
);
134 XmlValidatingReader xvr
= new XmlValidatingReader (rdr
);
135 xvr
.ValidationType
= ValidationType
.None
;
136 result
= new XPathDocument (xvr
, XmlSpace
.Preserve
).CreateNavigator ();
141 docCache
[uri
] = result
.Clone ();
148 Stack outputStack
= new Stack ();
150 public Outputter Out { get { return (Outputter)outputStack.Peek(); }}
152 public void PushOutput (Outputter newOutput
)
154 this.outputStack
.Push (newOutput
);
157 public Outputter
PopOutput ()
159 Outputter ret
= (Outputter
)this.outputStack
.Pop ();
164 public Hashtable Outputs { get { return compiledStyle.Outputs; }}
166 public XslOutput Output { get { return Outputs [currentOutputUri] as XslOutput; }
}
168 public string CurrentOutputUri { get { return currentOutputUri; }
}
170 public bool InsideCDataElement { get { return this.XPathContext.IsCData; }
}
173 #region AVT StringBuilder
177 bool avtSBlock
= false;
180 public StringBuilder
GetAvtStringBuilder ()
184 throw new XsltException ("String Builder was locked", null);
189 avtSB
= new StringBuilder ();
194 public string ReleaseAvtStringBuilder ()
198 throw new XsltException ("you never locked the string builder", null);
202 string ret
= avtSB
.ToString ();
208 #region Templates -- Apply/Call
209 Stack paramPassingCache
= new Stack ();
211 Hashtable
GetParams (ArrayList withParams
)
213 if (withParams
== null) return null;
216 if (paramPassingCache
.Count
!= 0) {
217 ret
= (Hashtable
)paramPassingCache
.Pop ();
220 ret
= new Hashtable ();
222 int len
= withParams
.Count
;
223 for (int i
= 0; i
< len
; i
++) {
224 XslVariableInformation param
= (XslVariableInformation
)withParams
[i
];
225 ret
.Add (param
.Name
, param
.Evaluate (this));
230 public void ApplyTemplates (XPathNodeIterator nodes
, QName mode
, ArrayList withParams
)
233 Hashtable passedParams
= GetParams (withParams
);
236 while (NodesetMoveNext ()) {
237 XslTemplate t
= FindTemplate (CurrentNode
, mode
);
238 currentTemplateStack
.Push (t
);
239 t
.Evaluate (this, passedParams
);
240 currentTemplateStack
.Pop ();
244 if (passedParams
!= null) paramPassingCache
.Push (passedParams
);
247 public void CallTemplate (QName name
, ArrayList withParams
)
249 Hashtable passedParams
= GetParams (withParams
);
251 XslTemplate t
= FindTemplate (name
);
252 currentTemplateStack
.Push (null);
253 t
.Evaluate (this, passedParams
);
254 currentTemplateStack
.Pop ();
256 if (passedParams
!= null) paramPassingCache
.Push (passedParams
);
259 public void ApplyImports ()
262 XslTemplate currentTemplate
= (XslTemplate
)currentTemplateStack
.Peek();
263 if (currentTemplate
== null)
264 throw new XsltException ("Invalid context for apply-imports", null, CurrentNode
);
267 for (int i
= currentTemplate
.Parent
.Imports
.Count
- 1; i
>= 0; i
--) {
268 XslStylesheet s
= (XslStylesheet
)currentTemplate
.Parent
.Imports
[i
];
269 t
= s
.Templates
.FindMatch (CurrentNode
, currentTemplate
.Mode
, this);
271 currentTemplateStack
.Push (t
);
273 currentTemplateStack
.Pop ();
278 switch (CurrentNode
.NodeType
) {
279 case XPathNodeType
.Root
:
280 case XPathNodeType
.Element
:
281 if (currentTemplate
.Mode
== QName
.Empty
)
282 t
= XslDefaultNodeTemplate
.Instance
;
284 t
= new XslDefaultNodeTemplate(currentTemplate
.Mode
);
287 case XPathNodeType
.Attribute
:
288 case XPathNodeType
.SignificantWhitespace
:
289 case XPathNodeType
.Text
:
290 case XPathNodeType
.Whitespace
:
291 t
= XslDefaultTextTemplate
.Instance
;
294 case XPathNodeType
.Comment
:
295 case XPathNodeType
.ProcessingInstruction
:
296 t
= XslEmptyTemplate
.Instance
;
300 t
= XslEmptyTemplate
.Instance
;
303 currentTemplateStack
.Push (t
);
305 currentTemplateStack
.Pop ();
308 internal void TryStylesheetNamespaceOutput (ArrayList excluded
)
310 if (outputStylesheetXmlns
) {
311 foreach (XmlQualifiedName qname
in this.style
.StylesheetNamespaces
) {
312 string prefix
= style
.PrefixInEffect (qname
.Name
, excluded
);
315 else if (prefix
== qname
.Name
)
316 Out
.WriteNamespaceDecl (
317 prefix
== "#default" ? String
.Empty
: prefix
,
320 Out
.WriteNamespaceDecl (prefix
, style
.StyleDocument
.GetNamespace (prefix
));
322 outputStylesheetXmlns
= false;
326 XslTemplate
FindTemplate (XPathNavigator node
, QName mode
)
328 XslTemplate ret
= style
.Templates
.FindMatch (CurrentNode
, mode
, this);
330 if (ret
!= null) return ret
;
332 switch (node
.NodeType
) {
333 case XPathNodeType
.Root
:
334 case XPathNodeType
.Element
:
335 if (mode
== QName
.Empty
)
336 return XslDefaultNodeTemplate
.Instance
;
338 return new XslDefaultNodeTemplate(mode
);
340 case XPathNodeType
.Attribute
:
341 case XPathNodeType
.SignificantWhitespace
:
342 case XPathNodeType
.Text
:
343 case XPathNodeType
.Whitespace
:
344 return XslDefaultTextTemplate
.Instance
;
346 case XPathNodeType
.Comment
:
347 case XPathNodeType
.ProcessingInstruction
:
348 return XslEmptyTemplate
.Instance
;
351 return XslEmptyTemplate
.Instance
;
355 XslTemplate
FindTemplate (QName name
)
357 XslTemplate ret
= style
.Templates
.FindTemplate (name
);
358 if (ret
!= null) return ret
;
360 throw new XsltException ("Could not resolve named template " + name
, null, CurrentNode
);
365 public void PushForEachContext ()
367 currentTemplateStack
.Push (null);
370 public void PopForEachContext ()
372 currentTemplateStack
.Pop ();
376 #region Nodeset Context
377 ArrayList nodesetStack
= new ArrayList ();
379 public XPathNodeIterator CurrentNodeset
{
380 get { return (XPathNodeIterator) nodesetStack [nodesetStack.Count - 1]; }
383 public XPathNavigator CurrentNode
{
385 XPathNavigator nav
= CurrentNodeset
.Current
;
388 // Inside for-each context, CurrentNodeset.Current may be null
389 for (int i
= nodesetStack
.Count
- 2; i
>= 0; i
--) {
390 nav
= ((XPathNodeIterator
) nodesetStack
[i
]).Current
;
398 public bool NodesetMoveNext ()
400 return CurrentNodeset
.MoveNext ();
403 public void PushNodeset (XPathNodeIterator itr
)
405 nodesetStack
.Add (itr
.Clone ());
408 public void PopNodeset ()
410 nodesetStack
.RemoveAt (nodesetStack
.Count
- 1);
416 public bool Matches (Pattern p
, XPathNavigator n
)
418 return CompiledStyle
.ExpressionStore
.PatternMatches (p
, this, n
);
421 public object Evaluate (XPathExpression expr
)
423 expr
= CompiledStyle
.ExpressionStore
.PrepForExecution (expr
, this);
424 expr
.SetContext (XPathContext
);
426 XPathNodeIterator itr
= CurrentNodeset
;
427 return itr
.Current
.Evaluate (expr
, itr
, XPathContext
);
430 public string EvaluateString (XPathExpression expr
)
432 expr
= CompiledStyle
.ExpressionStore
.PrepForExecution (expr
, this);
433 expr
.SetContext (XPathContext
);
435 XPathNodeIterator itr
= CurrentNodeset
;
436 return itr
.Current
.EvaluateString (expr
, itr
, XPathContext
);
439 public bool EvaluateBoolean (XPathExpression expr
)
441 expr
= CompiledStyle
.ExpressionStore
.PrepForExecution (expr
, this);
442 expr
.SetContext (XPathContext
);
444 XPathNodeIterator itr
= CurrentNodeset
;
445 return itr
.Current
.EvaluateBoolean (expr
, itr
, XPathContext
);
448 public double EvaluateNumber (XPathExpression expr
)
450 expr
= CompiledStyle
.ExpressionStore
.PrepForExecution (expr
, this);
451 expr
.SetContext (XPathContext
);
453 XPathNodeIterator itr
= CurrentNodeset
;
454 return itr
.Current
.EvaluateNumber (expr
, itr
, XPathContext
);
457 public XPathNodeIterator
Select (XPathExpression expr
)
459 expr
= CompiledStyle
.ExpressionStore
.PrepForExecution (expr
, this);
460 expr
.SetContext (XPathContext
);
461 return CurrentNodeset
.Current
.Select (expr
, XPathContext
);
466 public XslAttributeSet
ResolveAttributeSet (QName name
)
468 return CompiledStyle
.ResolveAttributeSet (name
);
471 #region Variable Stack
472 Stack variableStack
= new Stack ();
473 object [] currentStack
;
474 public int StackItemCount
{
476 if (currentStack
== null)
478 for (int i
= 0; i
< currentStack
.Length
; i
++)
479 if (currentStack
[i
] == null)
481 return currentStack
.Length
;
485 public object GetStackItem (int slot
)
487 return currentStack
[slot
];
490 public void SetStackItem (int slot
, object o
)
492 currentStack
[slot
] = o
;
495 public void PushStack (int stackSize
)
497 variableStack
.Push (currentStack
);
498 currentStack
= new object [stackSize
];
501 public void PopStack ()
503 currentStack
= (object[])variableStack
.Pop();
509 Hashtable busyTable
= new Hashtable ();
510 static object busyObject
= new object ();
512 public void SetBusy (object o
)
514 busyTable
[o
] = busyObject
;
517 public void SetFree (object o
)
519 busyTable
.Remove (o
);
522 public bool IsBusy (object o
)
524 return busyTable
[o
] == busyObject
;
528 public bool PushElementState (string name
, string ns
, bool preserveWhitespace
)
530 bool b
= IsCData (name
, ns
);
531 XPathContext
.PushScope ();
532 Out
.InsideCDataSection
= XPathContext
.IsCData
= b
;
533 XPathContext
.WhitespaceHandling
= preserveWhitespace
;
537 bool IsCData (string name
, string ns
)
539 for (int i
= 0; i
< Output
.CDataSectionElements
.Length
; i
++) {
540 XmlQualifiedName qname
= Output
.CDataSectionElements
[i
];
541 if (qname
.Name
== name
&& qname
.Namespace
== ns
) {
548 public void PopCDataState (bool isCData
)
550 XPathContext
.PopScope ();
551 Out
.InsideCDataSection
= XPathContext
.IsCData
;
554 public bool PreserveWhitespace ()
556 return XPathContext
.PreserveWhitespace (CurrentNode
);