**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / Mono.Xml.Xsl / XslStylesheet.cs
blob79d900125315abb0e58c04c8707949e75c974bab
1 //
2 // XslStylesheet.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
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.
33 using System;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Xml.XPath;
39 using System.Xml.Xsl;
40 using System.IO;
42 using Mono.Xml.Xsl.Operations;
44 using QName = System.Xml.XmlQualifiedName;
46 namespace Mono.Xml.Xsl {
48 internal class XslStylesheet {
49 public const string XsltNamespace = "http://www.w3.org/1999/XSL/Transform";
50 public const string MSXsltNamespace = "urn:schemas-microsoft-com:xslt";
52 Compiler c;
54 XslStylesheet importer;
55 // Top-level elements
56 ArrayList imports = new ArrayList ();
57 // [QName]=>XmlSpace
58 Hashtable spaceControls = new Hashtable ();
59 // [string stylesheet-prefix]=>string result-prefix
60 NameValueCollection namespaceAliases = new NameValueCollection ();
61 // [QName]=>XmlSpace
62 Hashtable parameters = new Hashtable ();
63 // [QName]=>XslKey
64 Hashtable keys = new Hashtable();
66 XslTemplateTable templates;
68 // stylesheet attributes
69 string version;
70 XmlQualifiedName [] extensionElementPrefixes;
71 XmlQualifiedName [] excludeResultPrefixes;
72 ArrayList stylesheetNamespaces = new ArrayList ();
74 // below are newly introduced in XSLT 2.0
75 // elements::
76 // xsl:import-schema should be interpreted into it.
77 XmlSchemaCollection schemas = new XmlSchemaCollection ();
78 // [QName]=>XslCharacterMap
79 Hashtable characterMap = new Hashtable ();
80 // [QName]=>XslDateFormat
81 Hashtable dateFormats = new Hashtable ();
82 // [QName]=>XslFunction
83 Hashtable functions = new Hashtable ();
84 // [QName]=>XslSortKey
85 Hashtable sortKeys = new Hashtable ();
86 // attributes::
87 string xpathDefaultNamespace = "";
88 XslDefaultValidation defaultValidation = XslDefaultValidation.Lax;
90 public string BaseUri {
91 get { return c.Input.BaseURI; }
94 public XmlQualifiedName [] ExtensionElementPrefixes {
95 get { return extensionElementPrefixes; }
98 public XmlQualifiedName [] ExcludeResultPrefixes {
99 get { return excludeResultPrefixes; }
102 public ArrayList StylesheetNamespaces {
103 get { return stylesheetNamespaces; }
106 public ArrayList Imports {
107 get { return imports; }
110 public Hashtable SpaceControls {
111 get { return spaceControls; }
114 public NameValueCollection NamespaceAliases {
115 get { return namespaceAliases; }
118 public Hashtable Parameters {
119 get { return parameters; }
122 public XPathNavigator StyleDocument {
123 get { return c.Input; }
126 public XslTemplateTable Templates {
127 get { return templates; }
130 public Hashtable Keys {
131 get { return keys; }
134 public string Version {
135 get { return version; }
138 public XslStylesheet (Compiler c)
140 this.c = c;
141 c.PushStylesheet (this);
143 templates = new XslTemplateTable (this);
145 // move to root element
146 while (c.Input.NodeType != XPathNodeType.Element)
147 if (!c.Input.MoveToNext ())
148 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
150 if (c.Input.NamespaceURI != XsltNamespace) {
151 if (c.Input.GetAttribute ("version", XsltNamespace) == null)
152 throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
153 // then it is simplified stylesheet.
154 Templates.Add (new XslTemplate (c));
155 } else {
156 if (c.Input.LocalName != "stylesheet" &&
157 c.Input.LocalName != "transform")
158 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
160 version = c.Input.GetAttribute ("version", "");
161 if (version == null)
162 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c.Input);
164 extensionElementPrefixes = c.ParseQNameListAttribute ("extension-element-prefixes");
165 excludeResultPrefixes = c.ParseQNameListAttribute ("exclude-result-prefixes");
166 if (c.Input.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
167 do {
168 if (c.Input.Value == XsltNamespace)
169 continue;
170 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
171 } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
172 c.Input.MoveToParent ();
174 ProcessTopLevelElements ();
177 c.PopStylesheet ();
180 public XslKey FindKey (QName name)
182 XslKey key = Keys [name] as XslKey;
183 if (key != null)
184 return key;
185 for (int i = Imports.Count - 1; i >= 0; i--) {
186 key = ((XslStylesheet) Imports [i]).FindKey (name);
187 if (key != null)
188 return key;
190 return null;
193 bool countedSpaceControlExistence;
194 bool cachedHasSpaceControls;
195 public bool HasSpaceControls {
196 get {
197 if (!countedSpaceControlExistence) {
198 countedSpaceControlExistence = true;
199 if (this.spaceControls.Count > 0)
200 cachedHasSpaceControls = true;
201 else if (imports.Count == 0)
202 cachedHasSpaceControls = false;
203 else {
204 for (int i = 0; i < imports.Count; i++)
205 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
206 countedSpaceControlExistence = true;
207 cachedHasSpaceControls = false;
210 return cachedHasSpaceControls;
214 public bool GetPreserveWhitespace (string localName, string ns)
216 if (!HasSpaceControls)
217 return true;
219 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
220 object o = spaceControls [qname];
221 if (o == null) {
223 for (int i = 0; i < imports.Count; i++) {
224 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
225 if (o != null)
226 break;
230 if (o == null) {
231 qname = new XmlQualifiedName ("*", ns);
232 o = spaceControls [qname];
233 if (o == null) {
234 for (int i = 0; i < imports.Count; i++) {
235 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
236 if (o != null)
237 break;
242 if (o == null) {
243 qname = new XmlQualifiedName ("*", String.Empty);
244 o = spaceControls [qname];
245 if (o == null) {
246 for (int i = 0; i < imports.Count; i++) {
247 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
248 if (o != null)
249 break;
254 if (o != null) {
255 XmlSpace space = (XmlSpace) o;
256 switch ((XmlSpace) o) {
257 case XmlSpace.Preserve:
258 return true;
259 case XmlSpace.Default:
260 return false;
263 return true;
266 bool countedNamespaceAliases;
267 bool cachedHasNamespaceAliases;
268 public bool HasNamespaceAliases {
269 get {
270 if (!countedNamespaceAliases) {
271 countedNamespaceAliases = true;
272 if (namespaceAliases.Count > 0)
273 cachedHasNamespaceAliases = true;
274 else if (imports.Count == 0)
275 cachedHasNamespaceAliases = false;
276 else {
277 for (int i = 0; i < imports.Count; i++)
278 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
279 countedNamespaceAliases = true;
280 cachedHasNamespaceAliases = false;
283 return cachedHasNamespaceAliases;
287 public string GetActualPrefix (string prefix)
289 if (!HasNamespaceAliases)
290 return prefix;
292 string result = namespaceAliases [prefix];
293 if (result == null) {
294 for (int i = 0; i < imports.Count; i++) {
295 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
296 if (result != null)
297 break;
301 return result != null ? result : prefix;
304 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
306 this.importer = importer;
309 private void HandleInclude (string href)
311 c.PushInputDocument (href);
312 ProcessTopLevelElements ();
313 c.PopInputDocument ();
316 private void HandleImport (string href)
318 c.PushInputDocument (href);
319 imports.Add (new XslStylesheet (c, this));
320 c.PopInputDocument ();
323 private void HandleTopLevelElement ()
325 XPathNavigator n = c.Input;
326 switch (n.NamespaceURI)
328 case XsltNamespace:
330 switch (n.LocalName)
332 case "include":
333 HandleInclude (c.GetAttribute ("href"));
334 break;
335 case "import":
336 HandleImport (c.GetAttribute ("href"));
337 break;
338 case "preserve-space":
339 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
340 break;
342 case "strip-space":
343 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
344 break;
346 case "namespace-alias":
347 namespaceAliases.Add ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
348 break;
350 case "attribute-set":
351 c.AddAttributeSet (new XslAttributeSet (c));
352 break;
354 case "key":
355 keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
356 break;
358 case "output":
359 c.CompileOutput ();
360 break;
362 case "decimal-format":
363 c.CompileDecimalFormat ();
364 break;
366 case "template":
367 templates.Add (new XslTemplate (c));
368 break;
369 case "variable":
370 c.AddGlobalVariable (new XslGlobalVariable (c));
371 break;
372 case "param":
373 c.AddGlobalVariable (new XslGlobalParam (c));
374 break;
375 default:
376 if (version == "1.0")
377 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
378 break;
380 break;
381 case MSXsltNamespace:
382 switch (n.LocalName)
384 case "script":
385 c.ScriptManager.AddScript (c);
386 break;
388 break;
392 private void ProcessTopLevelElements ()
394 if (c.Input.MoveToFirstChild ()) {
395 do {
396 if (c.Input.NodeType == XPathNodeType.Element) {
397 Debug.EnterNavigator (c);
398 this.HandleTopLevelElement();
399 Debug.ExitNavigator (c);
401 } while (c.Input.MoveToNext ());
403 c.Input.MoveToParent ();
407 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
409 // XSLT 3.4 - This implementation recovers from errors.
410 foreach (QName name in names)
411 spaceControls [name] = result;
414 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
416 if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
417 return null;
418 if (prefix == "#default")
419 prefix = String.Empty;
421 if (ExcludeResultPrefixes != null) {
422 bool exclude = false;
423 foreach (XmlQualifiedName exc in ExcludeResultPrefixes)
424 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
425 exclude = true;
426 break;
428 if (exclude)
429 return null;
432 if (ExtensionElementPrefixes != null) {
433 bool exclude = false;
434 foreach (XmlQualifiedName exc in ExtensionElementPrefixes)
435 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
436 exclude = true;
437 break;
439 if (exclude)
440 return null;
443 return GetActualPrefix (prefix);
448 internal enum XslDefaultValidation
450 Strict,
451 Lax,
452 Preserve,
453 Strip