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.
34 using System.Collections;
35 using System.Collections.Specialized;
37 using System.Xml.Schema;
38 using System.Xml.XPath;
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";
54 // XslStylesheet importer;
56 ArrayList imports = new ArrayList ();
58 Hashtable spaceControls = new Hashtable ();
59 // [string stylesheet-prefix]=>string result-prefix
60 NameValueCollection namespaceAliases = new NameValueCollection ();
62 Hashtable parameters = new Hashtable ();
64 Hashtable keys = new Hashtable();
66 XslTemplateTable templates;
68 // stylesheet attributes
70 XmlQualifiedName [] extensionElementPrefixes;
71 XmlQualifiedName [] excludeResultPrefixes;
72 ArrayList stylesheetNamespaces = new ArrayList ();
74 public string BaseUri {
75 get { return c.Input.BaseURI; }
78 public XmlQualifiedName [] ExtensionElementPrefixes {
79 get { return extensionElementPrefixes; }
82 public XmlQualifiedName [] ExcludeResultPrefixes {
83 get { return excludeResultPrefixes; }
86 public ArrayList StylesheetNamespaces {
87 get { return stylesheetNamespaces; }
90 public ArrayList Imports {
91 get { return imports; }
94 public Hashtable SpaceControls {
95 get { return spaceControls; }
98 public NameValueCollection NamespaceAliases {
99 get { return namespaceAliases; }
102 public Hashtable Parameters {
103 get { return parameters; }
106 public XPathNavigator StyleDocument {
107 get { return c.Input; }
110 public XslTemplateTable Templates {
111 get { return templates; }
114 public Hashtable Keys {
118 public string Version {
119 get { return version; }
122 public XslStylesheet (Compiler c)
125 c.PushStylesheet (this);
127 templates = new XslTemplateTable (this);
129 // move to root element
130 while (c.Input.NodeType != XPathNodeType.Element)
131 if (!c.Input.MoveToNext ())
132 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
134 if (c.Input.NamespaceURI != XsltNamespace) {
135 if (c.Input.GetAttribute ("version", XsltNamespace) == String.Empty)
136 throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
137 // then it is simplified stylesheet.
138 Templates.Add (new XslTemplate (c));
140 if (c.Input.LocalName != "stylesheet" &&
141 c.Input.LocalName != "transform")
142 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
144 version = c.Input.GetAttribute ("version", "");
145 if (version == String.Empty)
146 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c.Input);
148 extensionElementPrefixes = ParseMappedPrefixes (c.GetAttribute ("extension-element-prefixes"), c.Input);
149 excludeResultPrefixes = ParseMappedPrefixes (c.GetAttribute ("exclude-result-prefixes"), c.Input);
150 if (c.Input.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
152 if (c.Input.Value == XsltNamespace)
154 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
155 } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
156 c.Input.MoveToParent ();
158 ProcessTopLevelElements ();
164 private QName [] ParseMappedPrefixes (string list, XPathNavigator nav)
168 ArrayList al = new ArrayList ();
169 foreach (string entry in list.Split (XmlChar.WhitespaceChars)) {
170 if (entry.Length == 0)
172 if (entry == "#default")
173 al.Add (new QName (String.Empty, String.Empty));
175 string entryNS = nav.GetNamespace (entry);
176 if (entryNS != String.Empty)
177 al.Add (new QName (entry, entryNS));
180 return (QName []) al.ToArray (typeof (QName));
183 public XslKey FindKey (QName name)
185 XslKey key = Keys [name] as XslKey;
188 for (int i = Imports.Count - 1; i >= 0; i--) {
189 key = ((XslStylesheet) Imports [i]).FindKey (name);
196 bool countedSpaceControlExistence;
197 bool cachedHasSpaceControls;
198 public bool HasSpaceControls {
200 if (!countedSpaceControlExistence) {
201 countedSpaceControlExistence = true;
202 if (this.spaceControls.Count > 0)
203 cachedHasSpaceControls = true;
204 else if (imports.Count == 0)
205 cachedHasSpaceControls = false;
207 for (int i = 0; i < imports.Count; i++)
208 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
209 countedSpaceControlExistence = true;
210 cachedHasSpaceControls = false;
213 return cachedHasSpaceControls;
217 public bool GetPreserveWhitespace (string localName, string ns)
219 if (!HasSpaceControls)
222 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
223 object o = spaceControls [qname];
226 for (int i = 0; i < imports.Count; i++) {
227 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
234 qname = new XmlQualifiedName ("*", ns);
235 o = spaceControls [qname];
237 for (int i = 0; i < imports.Count; i++) {
238 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
246 qname = new XmlQualifiedName ("*", String.Empty);
247 o = spaceControls [qname];
249 for (int i = 0; i < imports.Count; i++) {
250 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
258 switch ((XmlSpace) o) {
259 case XmlSpace.Preserve:
261 case XmlSpace.Default:
268 bool countedNamespaceAliases;
269 bool cachedHasNamespaceAliases;
270 public bool HasNamespaceAliases {
272 if (!countedNamespaceAliases) {
273 countedNamespaceAliases = true;
274 if (namespaceAliases.Count > 0)
275 cachedHasNamespaceAliases = true;
276 else if (imports.Count == 0)
277 cachedHasNamespaceAliases = false;
279 for (int i = 0; i < imports.Count; i++)
280 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
281 countedNamespaceAliases = true;
282 cachedHasNamespaceAliases = false;
285 return cachedHasNamespaceAliases;
289 public string GetActualPrefix (string prefix)
291 if (!HasNamespaceAliases)
294 string result = namespaceAliases [prefix];
295 if (result == null) {
296 for (int i = 0; i < imports.Count; i++) {
297 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
303 return result != null ? result : prefix;
306 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
308 // this.importer = importer;
311 private void HandleInclude (string href)
313 c.PushInputDocument (href);
315 // move to root element
316 while (c.Input.NodeType != XPathNodeType.Element)
317 if (!c.Input.MoveToNext ())
318 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
320 if (c.Input.NamespaceURI != XsltNamespace) {
321 if (c.Input.GetAttribute ("version", XsltNamespace) == String.Empty)
322 throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
323 // then it is simplified stylesheet.
324 Templates.Add (new XslTemplate (c));
327 ProcessTopLevelElements ();
329 c.PopInputDocument ();
332 private void HandleImport (string href)
334 c.PushInputDocument (href);
335 imports.Add (new XslStylesheet (c, this));
336 c.PopInputDocument ();
339 private void HandleTopLevelElement ()
341 XPathNavigator n = c.Input;
342 switch (n.NamespaceURI)
349 HandleInclude (c.GetAttribute ("href"));
352 HandleImport (c.GetAttribute ("href"));
354 case "preserve-space":
355 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
359 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
361 case "namespace-alias":
362 // do nothing. It is handled in prior.
365 case "attribute-set":
366 c.AddAttributeSet (new XslAttributeSet (c));
370 keys [c.ParseQNameAttribute ("name")] = new XslKey (c);
377 case "decimal-format":
378 c.CompileDecimalFormat ();
382 templates.Add (new XslTemplate (c));
385 c.AddGlobalVariable (new XslGlobalVariable (c));
388 c.AddGlobalVariable (new XslGlobalParam (c));
391 if (version == "1.0")
392 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
396 case MSXsltNamespace:
400 c.ScriptManager.AddScript (c);
407 private void ProcessTopLevelElements ()
409 if (!c.Input.MoveToFirstChild ())
412 // Collect namespace aliases first.
413 if (c.Input.NodeType != XPathNodeType.Element ||
414 c.Input.LocalName != "namespace-alias" ||
415 c.Input.NamespaceURI != XsltNamespace)
417 string sprefix = (string) c.GetAttribute ("stylesheet-prefix", "");
418 if (sprefix == "#default")
419 sprefix = String.Empty;
420 string rprefix= (string) c.GetAttribute ("result-prefix", "");
421 if (rprefix == "#default")
422 rprefix = String.Empty;
423 namespaceAliases.Set (sprefix, rprefix);
424 } while (c.Input.MoveToNext ());
426 c.Input.MoveToFirst ();
428 if (c.Input.NodeType != XPathNodeType.Element)
430 Debug.EnterNavigator (c);
431 this.HandleTopLevelElement ();
432 Debug.ExitNavigator (c);
433 } while (c.Input.MoveToNext ());
435 c.Input.MoveToParent ();
438 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
440 // XSLT 3.4 - This implementation recovers from errors.
441 foreach (QName name in names)
442 spaceControls [name] = result;