5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 // (C) 2003 Atsushi Enomoto
13 using System.Collections;
14 using System.Collections.Specialized;
16 using System.Xml.Schema;
17 using System.Xml.XPath;
21 using Mono.Xml.Xsl.Operations;
23 using QName = System.Xml.XmlQualifiedName;
25 namespace Mono.Xml.Xsl {
27 public class XslStylesheet {
28 public const string XsltNamespace = "http://www.w3.org/1999/XSL/Transform";
29 public const string MSXsltNamespace = "urn:schemas-microsoft-com:xslt";
33 XslStylesheet importer;
35 ArrayList imports = new ArrayList ();
37 Hashtable spaceControls = new Hashtable ();
38 // [string stylesheet-prefix]=>string result-prefix
39 NameValueCollection namespaceAliases = new NameValueCollection ();
41 Hashtable parameters = new Hashtable ();
43 Hashtable keys = new Hashtable();
45 XslTemplateTable templates;
47 // stylesheet attributes
49 XmlQualifiedName [] extensionElementPrefixes;
50 XmlQualifiedName [] excludeResultPrefixes;
51 ArrayList stylesheetNamespaces = new ArrayList ();
53 // below are newly introduced in XSLT 2.0
55 // xsl:import-schema should be interpreted into it.
56 XmlSchemaCollection schemas = new XmlSchemaCollection ();
57 // [QName]=>XslCharacterMap
58 Hashtable characterMap = new Hashtable ();
59 // [QName]=>XslDateFormat
60 Hashtable dateFormats = new Hashtable ();
61 // [QName]=>XslFunction
62 Hashtable functions = new Hashtable ();
63 // [QName]=>XslSortKey
64 Hashtable sortKeys = new Hashtable ();
66 string xpathDefaultNamespace = "";
67 XslDefaultValidation defaultValidation = XslDefaultValidation.Lax;
69 public string BaseUri {
70 get { return c.Input.BaseURI; }
73 public XmlQualifiedName [] ExtensionElementPrefixes {
74 get { return extensionElementPrefixes; }
77 public XmlQualifiedName [] ExcludeResultPrefixes {
78 get { return excludeResultPrefixes; }
81 public ArrayList StylesheetNamespaces {
82 get { return stylesheetNamespaces; }
85 public ArrayList Imports {
86 get { return imports; }
89 public Hashtable SpaceControls {
90 get { return spaceControls; }
93 public NameValueCollection NamespaceAliases {
94 get { return namespaceAliases; }
97 public Hashtable Parameters {
98 get { return parameters; }
101 public XPathNavigator StyleDocument {
102 get { return c.Input; }
105 public XslTemplateTable Templates {
106 get { return templates; }
109 public Hashtable Keys {
113 public string Version {
114 get { return version; }
117 public XslStylesheet (Compiler c)
120 c.PushStylesheet (this);
122 templates = new XslTemplateTable (this);
124 // move to root element
125 while (c.Input.NodeType != XPathNodeType.Element)
126 if (!c.Input.MoveToNext ())
127 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
129 if (c.Input.NamespaceURI != XsltNamespace) {
130 // then it is simplified stylesheet.
131 Templates.Add (new XslTemplate (c));
133 if (c.Input.LocalName != "stylesheet" &&
134 c.Input.LocalName != "transform")
135 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
137 version = c.Input.GetAttribute ("version", "");
139 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c.Input);
141 extensionElementPrefixes = c.ParseQNameListAttribute ("extension-element-prefixes");
142 excludeResultPrefixes = c.ParseQNameListAttribute ("exclude-result-prefixes");
143 if (c.Input.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
145 if (c.Input.Value == XsltNamespace)
147 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
148 } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
149 c.Input.MoveToParent ();
151 ProcessTopLevelElements ();
157 public XslKey FindKey (QName name)
159 XslKey key = Keys [name] as XslKey;
162 for (int i = Imports.Count - 1; i >= 0; i--) {
163 key = ((XslStylesheet) Imports [i]).FindKey (name);
170 bool countedSpaceControlExistence;
171 bool cachedHasSpaceControls;
172 public bool HasSpaceControls {
174 if (!countedSpaceControlExistence) {
175 countedSpaceControlExistence = true;
176 if (this.spaceControls.Count > 0)
177 cachedHasSpaceControls = true;
178 else if (imports.Count == 0)
179 cachedHasSpaceControls = false;
181 for (int i = 0; i < imports.Count; i++)
182 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
183 countedSpaceControlExistence = true;
184 cachedHasSpaceControls = false;
187 return cachedHasSpaceControls;
191 public bool GetPreserveWhitespace (string localName, string ns)
193 if (!HasSpaceControls)
196 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
197 object o = spaceControls [qname];
200 for (int i = 0; i < imports.Count; i++) {
201 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
208 qname = new XmlQualifiedName ("*", ns);
209 o = spaceControls [qname];
211 for (int i = 0; i < imports.Count; i++) {
212 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
220 qname = new XmlQualifiedName ("*", String.Empty);
221 o = spaceControls [qname];
223 for (int i = 0; i < imports.Count; i++) {
224 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
232 XmlSpace space = (XmlSpace) o;
233 switch ((XmlSpace) o) {
234 case XmlSpace.Preserve:
236 case XmlSpace.Default:
243 bool countedNamespaceAliases;
244 bool cachedHasNamespaceAliases;
245 public bool HasNamespaceAliases {
247 if (!countedNamespaceAliases) {
248 countedNamespaceAliases = true;
249 if (namespaceAliases.Count > 0)
250 cachedHasNamespaceAliases = true;
251 else if (imports.Count == 0)
252 cachedHasNamespaceAliases = false;
254 for (int i = 0; i < imports.Count; i++)
255 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
256 countedNamespaceAliases = true;
257 cachedHasNamespaceAliases = false;
260 return cachedHasNamespaceAliases;
264 public string GetActualPrefix (string prefix)
266 if (!HasNamespaceAliases)
269 string result = namespaceAliases [prefix];
270 if (result == null) {
271 for (int i = 0; i < imports.Count; i++) {
272 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
278 return result != null ? result : prefix;
281 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
283 this.importer = importer;
286 private void HandleInclude (string href)
288 c.PushInputDocument (href);
289 ProcessTopLevelElements ();
290 c.PopInputDocument ();
293 private void HandleImport (string href)
295 c.PushInputDocument (href);
296 imports.Add (new XslStylesheet (c, this));
297 c.PopInputDocument ();
300 private void HandleTopLevelElement ()
302 XPathNavigator n = c.Input;
303 switch (n.NamespaceURI)
310 HandleInclude (c.GetAttribute ("href"));
313 HandleImport (c.GetAttribute ("href"));
315 case "preserve-space":
316 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
320 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
323 case "namespace-alias":
324 namespaceAliases.Add ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
327 case "attribute-set":
328 c.AddAttributeSet (new XslAttributeSet (c));
332 keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
339 case "decimal-format":
340 c.CompileDecimalFormat ();
344 templates.Add (new XslTemplate (c));
347 c.AddGlobalVariable (new XslGlobalVariable (c));
350 c.AddGlobalVariable (new XslGlobalParam (c));
353 if (version == "1.0")
354 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
358 case MSXsltNamespace:
362 c.ScriptManager.AddScript (c);
369 private void ProcessTopLevelElements ()
371 if (c.Input.MoveToFirstChild ()) {
373 if (c.Input.NodeType == XPathNodeType.Element) {
374 Debug.EnterNavigator (c);
375 this.HandleTopLevelElement();
376 Debug.ExitNavigator (c);
378 } while (c.Input.MoveToNext ());
380 c.Input.MoveToParent ();
384 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
386 // XSLT 3.4 - This implementation recovers from errors.
387 foreach (QName name in names)
388 spaceControls [name] = result;
391 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
393 if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
395 if (prefix == "#default")
396 prefix = String.Empty;
398 if (ExcludeResultPrefixes != null) {
399 bool exclude = false;
400 foreach (XmlQualifiedName exc in ExcludeResultPrefixes)
401 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
409 if (ExtensionElementPrefixes != null) {
410 bool exclude = false;
411 foreach (XmlQualifiedName exc in ExtensionElementPrefixes)
412 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
420 return GetActualPrefix (prefix);
425 public enum XslDefaultValidation