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 if (c.Input.GetAttribute ("version", XsltNamespace) == null)
131 throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
132 // then it is simplified stylesheet.
133 Templates.Add (new XslTemplate (c));
135 if (c.Input.LocalName != "stylesheet" &&
136 c.Input.LocalName != "transform")
137 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
139 version = c.Input.GetAttribute ("version", "");
141 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c.Input);
143 extensionElementPrefixes = c.ParseQNameListAttribute ("extension-element-prefixes");
144 excludeResultPrefixes = c.ParseQNameListAttribute ("exclude-result-prefixes");
145 if (c.Input.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
147 if (c.Input.Value == XsltNamespace)
149 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
150 } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
151 c.Input.MoveToParent ();
153 ProcessTopLevelElements ();
159 public XslKey FindKey (QName name)
161 XslKey key = Keys [name] as XslKey;
164 for (int i = Imports.Count - 1; i >= 0; i--) {
165 key = ((XslStylesheet) Imports [i]).FindKey (name);
172 bool countedSpaceControlExistence;
173 bool cachedHasSpaceControls;
174 public bool HasSpaceControls {
176 if (!countedSpaceControlExistence) {
177 countedSpaceControlExistence = true;
178 if (this.spaceControls.Count > 0)
179 cachedHasSpaceControls = true;
180 else if (imports.Count == 0)
181 cachedHasSpaceControls = false;
183 for (int i = 0; i < imports.Count; i++)
184 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
185 countedSpaceControlExistence = true;
186 cachedHasSpaceControls = false;
189 return cachedHasSpaceControls;
193 public bool GetPreserveWhitespace (string localName, string ns)
195 if (!HasSpaceControls)
198 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
199 object o = spaceControls [qname];
202 for (int i = 0; i < imports.Count; i++) {
203 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
210 qname = new XmlQualifiedName ("*", ns);
211 o = spaceControls [qname];
213 for (int i = 0; i < imports.Count; i++) {
214 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
222 qname = new XmlQualifiedName ("*", String.Empty);
223 o = spaceControls [qname];
225 for (int i = 0; i < imports.Count; i++) {
226 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
234 XmlSpace space = (XmlSpace) o;
235 switch ((XmlSpace) o) {
236 case XmlSpace.Preserve:
238 case XmlSpace.Default:
245 bool countedNamespaceAliases;
246 bool cachedHasNamespaceAliases;
247 public bool HasNamespaceAliases {
249 if (!countedNamespaceAliases) {
250 countedNamespaceAliases = true;
251 if (namespaceAliases.Count > 0)
252 cachedHasNamespaceAliases = true;
253 else if (imports.Count == 0)
254 cachedHasNamespaceAliases = false;
256 for (int i = 0; i < imports.Count; i++)
257 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
258 countedNamespaceAliases = true;
259 cachedHasNamespaceAliases = false;
262 return cachedHasNamespaceAliases;
266 public string GetActualPrefix (string prefix)
268 if (!HasNamespaceAliases)
271 string result = namespaceAliases [prefix];
272 if (result == null) {
273 for (int i = 0; i < imports.Count; i++) {
274 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
280 return result != null ? result : prefix;
283 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
285 this.importer = importer;
288 private void HandleInclude (string href)
290 c.PushInputDocument (href);
291 ProcessTopLevelElements ();
292 c.PopInputDocument ();
295 private void HandleImport (string href)
297 c.PushInputDocument (href);
298 imports.Add (new XslStylesheet (c, this));
299 c.PopInputDocument ();
302 private void HandleTopLevelElement ()
304 XPathNavigator n = c.Input;
305 switch (n.NamespaceURI)
312 HandleInclude (c.GetAttribute ("href"));
315 HandleImport (c.GetAttribute ("href"));
317 case "preserve-space":
318 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
322 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
325 case "namespace-alias":
326 namespaceAliases.Add ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
329 case "attribute-set":
330 c.AddAttributeSet (new XslAttributeSet (c));
334 keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
341 case "decimal-format":
342 c.CompileDecimalFormat ();
346 templates.Add (new XslTemplate (c));
349 c.AddGlobalVariable (new XslGlobalVariable (c));
352 c.AddGlobalVariable (new XslGlobalParam (c));
355 if (version == "1.0")
356 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
360 case MSXsltNamespace:
364 c.ScriptManager.AddScript (c);
371 private void ProcessTopLevelElements ()
373 if (c.Input.MoveToFirstChild ()) {
375 if (c.Input.NodeType == XPathNodeType.Element) {
376 Debug.EnterNavigator (c);
377 this.HandleTopLevelElement();
378 Debug.ExitNavigator (c);
380 } while (c.Input.MoveToNext ());
382 c.Input.MoveToParent ();
386 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
388 // XSLT 3.4 - This implementation recovers from errors.
389 foreach (QName name in names)
390 spaceControls [name] = result;
393 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
395 if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
397 if (prefix == "#default")
398 prefix = String.Empty;
400 if (ExcludeResultPrefixes != null) {
401 bool exclude = false;
402 foreach (XmlQualifiedName exc in ExcludeResultPrefixes)
403 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
411 if (ExtensionElementPrefixes != null) {
412 bool exclude = false;
413 foreach (XmlQualifiedName exc in ExtensionElementPrefixes)
414 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
422 return GetActualPrefix (prefix);
427 public enum XslDefaultValidation