5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 // (C) 2003 Atsushi Enomoto
14 using System.Collections;
15 using System.Collections.Specialized;
17 using System.Xml.Schema;
18 using System.Xml.XPath;
22 using Mono.Xml.Xsl.Operations;
24 using QName = System.Xml.XmlQualifiedName;
26 namespace Mono.Xml.Xsl {
28 public class XslStylesheet {
29 public const string XsltNamespace = "http://www.w3.org/1999/XSL/Transform";
30 public const string MSXsltNamespace = "urn:schemas-microsoft-com:xslt";
34 XslStylesheet importer;
36 ArrayList imports = new ArrayList ();
38 Hashtable spaceControls = new Hashtable ();
39 // [string stylesheet-prefix]=>string result-prefix
40 NameValueCollection namespaceAliases = new NameValueCollection ();
42 Hashtable parameters = new Hashtable ();
44 Hashtable keys = new Hashtable();
46 XslTemplateTable templates;
48 // stylesheet attributes
50 XmlQualifiedName [] extensionElementPrefixes;
51 XmlQualifiedName [] excludeResultPrefixes;
52 ArrayList stylesheetNamespaces = new ArrayList ();
54 // below are newly introduced in XSLT 2.0
56 // xsl:import-schema should be interpreted into it.
57 XmlSchemaCollection schemas = new XmlSchemaCollection ();
58 // [QName]=>XslCharacterMap
59 Hashtable characterMap = new Hashtable ();
60 // [QName]=>XslDateFormat
61 Hashtable dateFormats = new Hashtable ();
62 // [QName]=>XslFunction
63 Hashtable functions = new Hashtable ();
64 // [QName]=>XslSortKey
65 Hashtable sortKeys = new Hashtable ();
67 string xpathDefaultNamespace = "";
68 XslDefaultValidation defaultValidation = XslDefaultValidation.Lax;
70 public string BaseUri {
71 get { return c.Input.BaseURI; }
74 public XmlQualifiedName [] ExtensionElementPrefixes {
75 get { return extensionElementPrefixes; }
78 public XmlQualifiedName [] ExcludeResultPrefixes {
79 get { return excludeResultPrefixes; }
82 public ArrayList StylesheetNamespaces {
83 get { return stylesheetNamespaces; }
86 public ArrayList Imports {
87 get { return imports; }
90 public Hashtable SpaceControls {
91 get { return spaceControls; }
94 public NameValueCollection NamespaceAliases {
95 get { return namespaceAliases; }
98 public Hashtable Parameters {
99 get { return parameters; }
102 public XPathNavigator StyleDocument {
103 get { return c.Input; }
106 public XslTemplateTable Templates {
107 get { return templates; }
110 public Hashtable Keys {
114 public string Version {
115 get { return version; }
118 public XslStylesheet (Compiler c)
121 c.PushStylesheet (this);
123 templates = new XslTemplateTable (this);
124 if (c.Input.NamespaceURI != XsltNamespace) {
125 // then it is simplified stylesheet.
126 Templates.Add (new XslTemplate (c));
128 version = c.Input.GetAttribute ("version", "");
129 extensionElementPrefixes = c.ParseQNameListAttribute ("extension-element-prefixes");
130 excludeResultPrefixes = c.ParseQNameListAttribute ("exclude-result-prefixes");
131 if (c.Input.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
133 if (c.Input.Value == XsltNamespace)
135 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
136 } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
137 c.Input.MoveToParent ();
139 ProcessTopLevelElements ();
145 public XslKey FindKey (QName name)
147 XslKey key = Keys [name] as XslKey;
150 for (int i = Imports.Count - 1; i >= 0; i--) {
151 key = ((XslStylesheet) Imports [i]).FindKey (name);
158 bool countedSpaceControlExistence;
159 bool cachedHasSpaceControls;
160 public bool HasSpaceControls {
162 if (!countedSpaceControlExistence) {
163 countedSpaceControlExistence = true;
164 if (this.spaceControls.Count > 0)
165 cachedHasSpaceControls = true;
166 else if (imports.Count == 0)
167 cachedHasSpaceControls = false;
169 for (int i = 0; i < imports.Count; i++)
170 if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
171 countedSpaceControlExistence = true;
172 cachedHasSpaceControls = false;
175 return cachedHasSpaceControls;
179 public bool GetPreserveWhitespace (string localName, string ns)
181 if (!HasSpaceControls)
184 XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
185 object o = spaceControls [qname];
188 for (int i = 0; i < imports.Count; i++) {
189 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
196 qname = new XmlQualifiedName ("*", ns);
197 o = spaceControls [qname];
199 for (int i = 0; i < imports.Count; i++) {
200 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
208 qname = new XmlQualifiedName ("*", String.Empty);
209 o = spaceControls [qname];
211 for (int i = 0; i < imports.Count; i++) {
212 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
220 XmlSpace space = (XmlSpace) o;
221 switch ((XmlSpace) o) {
222 case XmlSpace.Preserve:
224 case XmlSpace.Default:
231 bool countedNamespaceAliases;
232 bool cachedHasNamespaceAliases;
233 public bool HasNamespaceAliases {
235 if (!countedNamespaceAliases) {
236 countedNamespaceAliases = true;
237 if (namespaceAliases.Count > 0)
238 cachedHasNamespaceAliases = true;
239 else if (imports.Count == 0)
240 cachedHasNamespaceAliases = false;
242 for (int i = 0; i < imports.Count; i++)
243 if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
244 countedNamespaceAliases = true;
245 cachedHasNamespaceAliases = false;
248 return cachedHasNamespaceAliases;
252 public string GetActualPrefix (string prefix)
254 if (!HasNamespaceAliases)
257 string result = namespaceAliases [prefix];
258 if (result == null) {
259 for (int i = 0; i < imports.Count; i++) {
260 result = ((XslStylesheet) imports [i]).namespaceAliases [prefix];
266 return result != null ? result : prefix;
269 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
271 this.importer = importer;
274 private void HandleInclude (string href)
276 c.PushInputDocument (href);
277 ProcessTopLevelElements ();
278 c.PopInputDocument ();
281 private void HandleImport (string href)
283 c.PushInputDocument (href);
284 imports.Add (new XslStylesheet (c, this));
285 c.PopInputDocument ();
288 private void HandleTopLevelElement ()
290 XPathNavigator n = c.Input;
291 switch (n.NamespaceURI)
298 HandleInclude (c.GetAttribute ("href"));
301 HandleImport (c.GetAttribute ("href"));
303 case "preserve-space":
304 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
308 AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
311 case "namespace-alias":
312 namespaceAliases.Add ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
315 case "attribute-set":
316 c.AddAttributeSet (new XslAttributeSet (c));
320 keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
327 case "decimal-format":
328 c.CompileDecimalFormat ();
332 templates.Add (new XslTemplate (c));
335 c.AddGlobalVariable (new XslGlobalVariable (c));
338 c.AddGlobalVariable (new XslGlobalParam (c));
341 if (version == "1.0")
342 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
346 case MSXsltNamespace:
350 c.ScriptManager.AddScript (c);
357 private void ProcessTopLevelElements ()
359 if (c.Input.MoveToFirstChild ()) {
361 if (c.Input.NodeType == XPathNodeType.Element) {
362 Debug.EnterNavigator (c);
363 this.HandleTopLevelElement();
364 Debug.ExitNavigator (c);
366 } while (c.Input.MoveToNext ());
368 c.Input.MoveToParent ();
372 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
374 // XSLT 3.4 - This implementation recovers from errors.
375 foreach (QName name in names)
376 spaceControls [name] = result;
379 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
381 if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
383 if (prefix == "#default")
384 prefix = String.Empty;
386 if (ExcludeResultPrefixes != null) {
387 bool exclude = false;
388 foreach (XmlQualifiedName exc in ExcludeResultPrefixes)
389 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
397 if (ExtensionElementPrefixes != null) {
398 bool exclude = false;
399 foreach (XmlQualifiedName exc in ExtensionElementPrefixes)
400 if (exc.Name == "#default" && prefix == String.Empty || exc.Name == prefix) {
408 return GetActualPrefix (prefix);
413 public enum XslDefaultValidation