2004-02-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / XslStylesheet.cs
1 //
2 // XslStylesheet.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //      
8 // (C) 2003 Ben Maurer
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 using System;
13 using System.CodeDom;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Xml;
17 using System.Xml.Schema;
18 using System.Xml.XPath;
19 using System.Xml.Xsl;
20 using System.IO;
21
22 using Mono.Xml.Xsl.Operations;
23
24 using QName = System.Xml.XmlQualifiedName;
25
26 namespace Mono.Xml.Xsl {
27
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";
31                 
32                 Compiler c;
33
34                 XslStylesheet importer;
35                 // Top-level elements
36                 ArrayList imports = new ArrayList ();
37                 // [QName]=>XmlSpace
38                 Hashtable spaceControls = new Hashtable ();
39                 // [string stylesheet-prefix]=>string result-prefix
40                 NameValueCollection namespaceAliases = new NameValueCollection ();
41                 // [QName]=>XmlSpace
42                 Hashtable parameters = new Hashtable ();
43                 // [QName]=>XslKey
44                 Hashtable keys = new Hashtable();
45
46                 XslTemplateTable templates;
47
48                 // stylesheet attributes
49                 string version;
50                 XmlQualifiedName [] extensionElementPrefixes;
51                 XmlQualifiedName [] excludeResultPrefixes;
52                 ArrayList stylesheetNamespaces = new ArrayList ();
53
54                 // below are newly introduced in XSLT 2.0
55                 //  elements::
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 ();
66                 //  attributes::
67                 string xpathDefaultNamespace = "";
68                 XslDefaultValidation defaultValidation = XslDefaultValidation.Lax;
69
70                 public string BaseUri {
71                         get { return c.Input.BaseURI; }
72                 }
73
74                 public XmlQualifiedName [] ExtensionElementPrefixes {
75                         get { return extensionElementPrefixes; }
76                 }
77
78                 public XmlQualifiedName [] ExcludeResultPrefixes {
79                         get { return excludeResultPrefixes; }
80                 }
81
82                 public ArrayList StylesheetNamespaces {
83                         get { return stylesheetNamespaces; }
84                 }
85
86                 public ArrayList Imports {
87                         get { return imports; }
88                 }
89
90                 public Hashtable SpaceControls {
91                         get { return spaceControls; }
92                 }
93
94                 public NameValueCollection NamespaceAliases {
95                         get { return namespaceAliases; }
96                 }
97
98                 public Hashtable Parameters {
99                         get { return parameters; }
100                 }
101
102                 public XPathNavigator StyleDocument {
103                         get { return c.Input; }
104                 }
105
106                 public XslTemplateTable Templates {
107                         get { return templates; }
108                 }
109
110                 public Hashtable Keys {
111                         get { return keys; }
112                 }
113
114                 public string Version {
115                         get { return version; }
116                 }
117
118                 public XslStylesheet (Compiler c)
119                 {
120                         this.c = c;
121                         c.PushStylesheet (this);
122                         
123                         templates = new XslTemplateTable (this);
124                         if (c.Input.NamespaceURI != XsltNamespace) {
125                                 // then it is simplified stylesheet.
126                                 Templates.Add (new XslTemplate (c));
127                         } else {
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)) {
132                                         do {
133                                                 if (c.Input.Value == XsltNamespace)
134                                                         continue;
135                                                 this.stylesheetNamespaces.Insert (0, new QName (c.Input.Name, c.Input.Value));
136                                         } while (c.Input.MoveToNextNamespace (XPathNamespaceScope.Local));
137                                         c.Input.MoveToParent ();
138                                 }
139                                 ProcessTopLevelElements ();
140                         }
141                         
142                         c.PopStylesheet ();
143                 }
144                 
145                 public XslKey FindKey (QName name)
146                 {
147                         XslKey key = Keys [name] as XslKey;
148                         if (key != null)
149                                 return key;
150                         for (int i = Imports.Count - 1; i >= 0; i--) {
151                                 key = ((XslStylesheet) Imports [i]).FindKey (name);
152                                 if (key != null)
153                                         return key;
154                         }
155                         return null;
156                 }
157
158                 bool countedSpaceControlExistence;
159                 bool cachedHasSpaceControls;
160                 public bool HasSpaceControls {
161                         get {
162                                 if (!countedSpaceControlExistence) {
163                                         countedSpaceControlExistence = true;
164                                         if (this.spaceControls.Count > 0)
165                                                 cachedHasSpaceControls = true;
166                                         else if (imports.Count == 0)
167                                                 cachedHasSpaceControls = false;
168                                         else {
169                                                 for (int i = 0; i < imports.Count; i++)
170                                                         if (((XslStylesheet) imports [i]).spaceControls.Count > 0)
171                                                                 countedSpaceControlExistence = true;
172                                                 cachedHasSpaceControls = false;
173                                         }
174                                 }
175                                 return cachedHasSpaceControls;
176                         }
177                 }
178
179                 public bool GetPreserveWhitespace (string localName, string ns)
180                 {
181                         if (!HasSpaceControls)
182                                 return true;
183
184                         XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
185                         object o = spaceControls [qname];
186                         if (o == null) {
187
188                                 for (int i = 0; i < imports.Count; i++) {
189                                         o = ((XslStylesheet) imports [i]).SpaceControls [qname];
190                                         if (o != null)
191                                                 break;
192                                 }
193                         }
194
195                         if (o == null) {
196                                 qname = new XmlQualifiedName ("*", ns);
197                                 o = spaceControls [qname];
198                                 if (o == null) {
199                                         for (int i = 0; i < imports.Count; i++) {
200                                                 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
201                                                 if (o != null)
202                                                         break;
203                                         }
204                                 }
205                         }
206
207                         if (o == null) {
208                                 qname = new XmlQualifiedName ("*", String.Empty);
209                                 o = spaceControls [qname];
210                                 if (o == null) {
211                                         for (int i = 0; i < imports.Count; i++) {
212                                                 o = ((XslStylesheet) imports [i]).SpaceControls [qname];
213                                                 if (o != null)
214                                                         break;
215                                         }
216                                 }
217                         }
218
219                         if (o != null) {
220                                 XmlSpace space = (XmlSpace) o;
221                                 switch ((XmlSpace) o) {
222                                 case XmlSpace.Preserve:
223                                         return true;
224                                 case XmlSpace.Default:
225                                         return false;
226                                 }
227                         }
228                         return true;
229                 }
230
231                 bool countedNamespaceAliases;
232                 bool cachedHasNamespaceAliases;
233                 public bool HasNamespaceAliases {
234                         get {
235                                 if (!countedNamespaceAliases) {
236                                         countedNamespaceAliases = true;
237                                         if (namespaceAliases.Count > 0)
238                                                 cachedHasNamespaceAliases = true;
239                                         else if (imports.Count == 0)
240                                                 cachedHasNamespaceAliases = false;
241                                         else {
242                                                 for (int i = 0; i < imports.Count; i++)
243                                                         if (((XslStylesheet) imports [i]).namespaceAliases.Count > 0)
244                                                                 countedNamespaceAliases = true;
245                                                 cachedHasNamespaceAliases = false;
246                                         }
247                                 }
248                                 return cachedHasNamespaceAliases;
249                         }
250                 }
251
252                 public string GetActualPrefix (string prefix)
253                 {
254                         if (!HasNamespaceAliases)
255                                 return prefix;
256
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];
261                                         if (result != null)
262                                                 break;
263                                 }
264                         }
265
266                         return result != null ? result : prefix;
267                 }
268
269                 private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
270                 {
271                         this.importer = importer;
272                 }
273                 
274                 private void HandleInclude (string href)
275                 {
276                         c.PushInputDocument (href);
277                         ProcessTopLevelElements ();
278                         c.PopInputDocument ();
279                 }
280                 
281                 private void HandleImport (string href)
282                 {
283                         c.PushInputDocument (href);
284                         imports.Add (new XslStylesheet (c, this));
285                         c.PopInputDocument ();
286                 }
287                 
288                 private void HandleTopLevelElement ()
289                 {
290                         XPathNavigator n = c.Input;
291                         switch (n.NamespaceURI)
292                         {
293                         case XsltNamespace:
294                                 
295                                 switch (n.LocalName)
296                                 {
297                                 case "include":
298                                         HandleInclude (c.GetAttribute ("href"));
299                                         break;
300                                 case "import":
301                                         HandleImport (c.GetAttribute ("href"));
302                                         break;
303                                 case "preserve-space":
304                                         AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
305                                         break;
306                                 
307                                 case "strip-space":
308                                         AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Default, n);
309                                         break;
310                                 
311                                 case "namespace-alias":
312                                         namespaceAliases.Add ((string) c.GetAttribute ("stylesheet-prefix", ""), (string) c.GetAttribute ("result-prefix", ""));
313                                         break;
314                                 
315                                 case "attribute-set":
316                                         c.AddAttributeSet (new XslAttributeSet (c));
317                                         break;
318
319                                 case "key":
320                                         keys.Add (c.ParseQNameAttribute ("name"), new XslKey (c));
321                                         break;
322                                         
323                                 case "output":
324                                         c.CompileOutput ();
325                                         break;
326                                 
327                                 case "decimal-format":
328                                         c.CompileDecimalFormat ();
329                                         break;
330                                         
331                                 case "template":
332                                         templates.Add (new XslTemplate (c));    
333                                         break;
334                                 case "variable":
335                                         c.AddGlobalVariable (new XslGlobalVariable (c));
336                                         break;
337                                 case "param":
338                                         c.AddGlobalVariable (new XslGlobalParam (c));
339                                         break;
340                                 default:
341                                         if (version == "1.0")
342                                                 throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
343                                         break;
344                                 }
345                                 break;
346                         case MSXsltNamespace:
347                                 switch (n.LocalName)
348                                 {
349                                 case "script":
350                                         c.ScriptManager.AddScript (c);
351                                         break;
352                                 }
353                                 break;
354                         }
355                 }
356                 
357                 private void ProcessTopLevelElements ()
358                 {
359                         if (c.Input.MoveToFirstChild ()) {
360                                 do {
361                                         if (c.Input.NodeType == XPathNodeType.Element) {                                        
362                                                 Debug.EnterNavigator (c);
363                                                 this.HandleTopLevelElement();
364                                                 Debug.ExitNavigator (c);
365                                         }
366                                 } while (c.Input.MoveToNext ());
367                                 
368                                 c.Input.MoveToParent ();
369                         }
370                 }
371
372                 private void AddSpaceControls (QName [] names, XmlSpace result, XPathNavigator styleElem)
373                 {
374                         // XSLT 3.4 - This implementation recovers from errors.
375                         foreach (QName name in names)
376                                 spaceControls [name] = result;
377                 }
378
379                 public string PrefixInEffect (string prefix, ArrayList additionalExcluded)
380                 {
381                         if (additionalExcluded != null && additionalExcluded.Contains (prefix == String.Empty ? "#default" : prefix))
382                                 return null;
383                         if (prefix == "#default")
384                                 prefix = String.Empty;
385
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) {
390                                                 exclude = true;
391                                                 break;
392                                         }
393                                 if (exclude)
394                                         return null;
395                         }
396
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) {
401                                                 exclude = true;
402                                                 break;
403                                         }
404                                 if (exclude)
405                                         return null;
406                         }
407
408                         return GetActualPrefix (prefix);
409                 }
410         }
411
412         
413         public enum XslDefaultValidation
414         {
415                 Strict,
416                 Lax,
417                 Preserve,
418                 Strip
419         }
420 }