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) == null)
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", "");
146 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c.Input);
148 extensionElementPrefixes = c.ParseQNameListAttribute ("extension-element-prefixes");
149 excludeResultPrefixes = c.ParseQNameListAttribute ("exclude-result-prefixes");
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 public XslKey FindKey (QName name)
166 XslKey key = Keys [name] as XslKey;
169 for (int i = Imports.Count - 1; i >= 0; i--) {
170 key = ((XslStylesheet) Imports [i]).FindKey (name);
177 bool countedSpaceControlExistence;
178 bool cachedHasSpaceControls;
179 public bool HasSpaceControls {
181 if (!countedSpaceControlExistence) {
182 countedSpaceControlExistence = true;
183 if (this.spaceControls.Count > 0)
184 cachedHasSpaceControls = true;
185 else if (imports.Count == 0)
186 cachedHasSpaceControls = false;
188 for (int i = 0; i < imports.Count; i++)
189 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
190 countedSpaceControlExistence = true;
191 cachedHasSpaceControls = false;
194 return cachedHasSpaceControls;
198 public bool GetPreserveWhitespace (string localName, string ns)
200 if (!HasSpaceControls)
203 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
204 object o = spaceControls [qname];
207 for (int i = 0; i < imports.Count; i++) {
208 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
215 qname = new XmlQualifiedName ("*", ns);
216 o = spaceControls [qname];
218 for (int i = 0; i < imports.Count; i++) {
219 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
227 qname = new XmlQualifiedName ("*", String.Empty);
228 o = spaceControls [qname];
230 for (int i = 0; i < imports.Count; i++) {
231 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
239 switch ((XmlSpace) o) {
240 case XmlSpace.Preserve:
242 case XmlSpace.Default:
249 bool countedNamespaceAliases;
250 bool cachedHasNamespaceAliases;
251 public bool HasNamespaceAliases {
253 if (!countedNamespaceAliases) {
254 countedNamespaceAliases = true;
255 if (namespaceAliases.Count > 0)
256 cachedHasNamespaceAliases = true;
257 else if (imports.Count == 0)
258 cachedHasNamespaceAliases = false;
260 for (int i = 0; i < imports.Count; i++)
261 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
262 countedNamespaceAliases = true;
263 cachedHasNamespaceAliases = false;
266 return cachedHasNamespaceAliases;
270 public string GetActualPrefix (string prefix)
272 if (!HasNamespaceAliases)
275 string result = namespaceAliases [prefix];
276 if (result == null) {
277 for (int i = 0; i < imports.Count; i++) {
278 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
284 return result != null ? result : prefix;
287 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
289 // this.importer = importer;
292 private void HandleInclude (string href)
294 c.PushInputDocument (href);
295 ProcessTopLevelElements ();
296 c.PopInputDocument ();
299 private void HandleImport (string href)
301 c.PushInputDocument (href);
302 imports.Add (new XslStylesheet (c, this));
303 c.PopInputDocument ();
306 private void HandleTopLevelElement ()
308 XPathNavigator n = c.Input;
309 switch (n.NamespaceURI)
316 HandleInclude (c.GetAttribute ("href"));
319 HandleImport (c.GetAttribute ("href"));
321 case "preserve-space":
322 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
326 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
329 case "namespace-alias":
330 namespaceAliases.Set ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
333 case "attribute-set":
334 c.AddAttributeSet (new XslAttributeSet (c));
338 keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
345 case "decimal-format":
346 c.CompileDecimalFormat ();
350 templates.Add (new XslTemplate (c));
353 c.AddGlobalVariable (new XslGlobalVariable (c));
356 c.AddGlobalVariable (new XslGlobalParam (c));
359 if (version == "1.0")
360 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
364 case MSXsltNamespace:
368 c.ScriptManager.AddScript (c);
375 private void ProcessTopLevelElements ()
377 if (c.Input.MoveToFirstChild ()) {
379 if (c.Input.NodeType == XPathNodeType.Element) {
380 Debug.EnterNavigator (c);
381 this.HandleTopLevelElement();
382 Debug.ExitNavigator (c);
384 } while (c.Input.MoveToNext ());
386 c.Input.MoveToParent ();
390 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
392 // XSLT 3.4 - This implementation recovers from errors.
393 foreach (QName name in names)
394 spaceControls [name] = result;
397 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
399 if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
401 if (prefix == "#default")
402 prefix = String.Empty;
404 if (ExcludeResultPrefixes != null) {
405 bool exclude = false;
406 foreach (XmlQualifiedName exc in ExcludeResultPrefixes)
407 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
415 if (ExtensionElementPrefixes != null) {
416 bool exclude = false;
417 foreach (XmlQualifiedName exc in ExtensionElementPrefixes)
418 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
426 return GetActualPrefix (prefix);
431 internal enum XslDefaultValidation