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 // below are newly introduced in XSLT 2.0
76 // xsl:import-schema should be interpreted into it.
77 XmlSchemaCollection schemas
= new XmlSchemaCollection ();
78 // [QName]=>XslCharacterMap
79 Hashtable characterMap
= new Hashtable ();
80 // [QName]=>XslDateFormat
81 Hashtable dateFormats
= new Hashtable ();
82 // [QName]=>XslFunction
83 Hashtable functions
= new Hashtable ();
84 // [QName]=>XslSortKey
85 Hashtable sortKeys
= new Hashtable ();
87 string xpathDefaultNamespace
= "";
88 XslDefaultValidation defaultValidation
= XslDefaultValidation
.Lax
;
90 public string BaseUri
{
91 get { return c.Input.BaseURI; }
94 public XmlQualifiedName
[] ExtensionElementPrefixes
{
95 get { return extensionElementPrefixes; }
98 public XmlQualifiedName
[] ExcludeResultPrefixes
{
99 get { return excludeResultPrefixes; }
102 public ArrayList StylesheetNamespaces
{
103 get { return stylesheetNamespaces; }
106 public ArrayList Imports
{
107 get { return imports; }
110 public Hashtable SpaceControls
{
111 get { return spaceControls; }
114 public NameValueCollection NamespaceAliases
{
115 get { return namespaceAliases; }
118 public Hashtable Parameters
{
119 get { return parameters; }
122 public XPathNavigator StyleDocument
{
123 get { return c.Input; }
126 public XslTemplateTable Templates
{
127 get { return templates; }
130 public Hashtable Keys
{
134 public string Version
{
135 get { return version; }
138 public XslStylesheet (Compiler c
)
141 c
.PushStylesheet (this);
143 templates
= new XslTemplateTable (this);
145 // move to root element
146 while (c
.Input
.NodeType
!= XPathNodeType
.Element
)
147 if (!c
.Input
.MoveToNext ())
148 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c
.Input
);
150 if (c
.Input
.NamespaceURI
!= XsltNamespace
) {
151 if (c
.Input
.GetAttribute ("version", XsltNamespace
) == null)
152 throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c
.Input
);
153 // then it is simplified stylesheet.
154 Templates
.Add (new XslTemplate (c
));
156 if (c
.Input
.LocalName
!= "stylesheet" &&
157 c
.Input
.LocalName
!= "transform")
158 throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c
.Input
);
160 version
= c
.Input
.GetAttribute ("version", "");
162 throw new XsltCompileException ("Mandatory attribute version is missing.", null, c
.Input
);
164 extensionElementPrefixes
= c
.ParseQNameListAttribute ("extension-element-prefixes");
165 excludeResultPrefixes
= c
.ParseQNameListAttribute ("exclude-result-prefixes");
166 if (c
.Input
.MoveToFirstNamespace (XPathNamespaceScope
.Local
)) {
168 if (c
.Input
.Value
== XsltNamespace
)
170 this.stylesheetNamespaces
.Insert (0, new QName (c
.Input
.Name
, c
.Input
.Value
));
171 } while (c
.Input
.MoveToNextNamespace (XPathNamespaceScope
.Local
));
172 c
.Input
.MoveToParent ();
174 ProcessTopLevelElements ();
180 public XslKey
FindKey (QName name
)
182 XslKey key
= Keys
[name
] as XslKey
;
185 for (int i
= Imports
.Count
- 1; i
>= 0; i
--) {
186 key
= ((XslStylesheet
) Imports
[i
]).FindKey (name
);
193 bool countedSpaceControlExistence
;
194 bool cachedHasSpaceControls
;
195 public bool HasSpaceControls
{
197 if (!countedSpaceControlExistence
) {
198 countedSpaceControlExistence
= true;
199 if (this.spaceControls
.Count
> 0)
200 cachedHasSpaceControls
= true;
201 else if (imports
.Count
== 0)
202 cachedHasSpaceControls
= false;
204 for (int i
= 0; i
< imports
.Count
; i
++)
205 if (((XslStylesheet
) imports
[i
]).spaceControls
.Count
> 0)
206 countedSpaceControlExistence
= true;
207 cachedHasSpaceControls
= false;
210 return cachedHasSpaceControls
;
214 public bool GetPreserveWhitespace (string localName
, string ns
)
216 if (!HasSpaceControls
)
219 XmlQualifiedName qname
= new XmlQualifiedName (localName
, ns
);
220 object o
= spaceControls
[qname
];
223 for (int i
= 0; i
< imports
.Count
; i
++) {
224 o
= ((XslStylesheet
) imports
[i
]).SpaceControls
[qname
];
231 qname
= new XmlQualifiedName ("*", ns
);
232 o
= spaceControls
[qname
];
234 for (int i
= 0; i
< imports
.Count
; i
++) {
235 o
= ((XslStylesheet
) imports
[i
]).SpaceControls
[qname
];
243 qname
= new XmlQualifiedName ("*", String
.Empty
);
244 o
= spaceControls
[qname
];
246 for (int i
= 0; i
< imports
.Count
; i
++) {
247 o
= ((XslStylesheet
) imports
[i
]).SpaceControls
[qname
];
255 XmlSpace space
= (XmlSpace
) o
;
256 switch ((XmlSpace
) o
) {
257 case XmlSpace
.Preserve
:
259 case XmlSpace
.Default
:
266 bool countedNamespaceAliases
;
267 bool cachedHasNamespaceAliases
;
268 public bool HasNamespaceAliases
{
270 if (!countedNamespaceAliases
) {
271 countedNamespaceAliases
= true;
272 if (namespaceAliases
.Count
> 0)
273 cachedHasNamespaceAliases
= true;
274 else if (imports
.Count
== 0)
275 cachedHasNamespaceAliases
= false;
277 for (int i
= 0; i
< imports
.Count
; i
++)
278 if (((XslStylesheet
) imports
[i
]).namespaceAliases
.Count
> 0)
279 countedNamespaceAliases
= true;
280 cachedHasNamespaceAliases
= false;
283 return cachedHasNamespaceAliases
;
287 public string GetActualPrefix (string prefix
)
289 if (!HasNamespaceAliases
)
292 string result
= namespaceAliases
[prefix
];
293 if (result
== null) {
294 for (int i
= 0; i
< imports
.Count
; i
++) {
295 result
= ((XslStylesheet
) imports
[i
]).namespaceAliases
[prefix
];
301 return result
!= null ? result
: prefix
;
304 private XslStylesheet (Compiler c
, XslStylesheet importer
) : this (c
)
306 this.importer
= importer
;
309 private void HandleInclude (string href
)
311 c
.PushInputDocument (href
);
312 ProcessTopLevelElements ();
313 c
.PopInputDocument ();
316 private void HandleImport (string href
)
318 c
.PushInputDocument (href
);
319 imports
.Add (new XslStylesheet (c
, this));
320 c
.PopInputDocument ();
323 private void HandleTopLevelElement ()
325 XPathNavigator n
= c
.Input
;
326 switch (n
.NamespaceURI
)
333 HandleInclude (c
.GetAttribute ("href"));
336 HandleImport (c
.GetAttribute ("href"));
338 case "preserve-space":
339 AddSpaceControls (c
.ParseQNameListAttribute ("elements"), XmlSpace
.Preserve
, n
);
343 AddSpaceControls (c
.ParseQNameListAttribute ("elements"), XmlSpace
.Default
, n
);
346 case "namespace-alias":
347 namespaceAliases
.Add ((string) c
.GetAttribute ("stylesheet-prefix", ""), (string) c
.GetAttribute ("result-prefix", ""));
350 case "attribute-set":
351 c
.AddAttributeSet (new XslAttributeSet (c
));
355 keys
.Add (c
.ParseQNameAttribute ("name"), new XslKey (c
));
362 case "decimal-format":
363 c
.CompileDecimalFormat ();
367 templates
.Add (new XslTemplate (c
));
370 c
.AddGlobalVariable (new XslGlobalVariable (c
));
373 c
.AddGlobalVariable (new XslGlobalParam (c
));
376 if (version
== "1.0")
377 throw new XsltCompileException ("Unrecognized top level element.", null, c
.Input
);
381 case MSXsltNamespace
:
385 c
.ScriptManager
.AddScript (c
);
392 private void ProcessTopLevelElements ()
394 if (c
.Input
.MoveToFirstChild ()) {
396 if (c
.Input
.NodeType
== XPathNodeType
.Element
) {
397 Debug
.EnterNavigator (c
);
398 this.HandleTopLevelElement();
399 Debug
.ExitNavigator (c
);
401 } while (c
.Input
.MoveToNext ());
403 c
.Input
.MoveToParent ();
407 private void AddSpaceControls (QName
[] names
, XmlSpace result
, XPathNavigator styleElem
)
409 // XSLT 3.4 - This implementation recovers from errors.
410 foreach (QName name
in names
)
411 spaceControls
[name
] = result
;
414 public string PrefixInEffect (string prefix
, ArrayList additionalExcluded
)
416 if (additionalExcluded
!= null && additionalExcluded
.Contains (prefix
== String
.Empty
? "#default" : prefix
))
418 if (prefix
== "#default")
419 prefix
= String
.Empty
;
421 if (ExcludeResultPrefixes
!= null) {
422 bool exclude
= false;
423 foreach (XmlQualifiedName exc
in ExcludeResultPrefixes
)
424 if (exc
.Name
== "#default" && prefix
== String
.Empty
|| exc
.Name
== prefix
) {
432 if (ExtensionElementPrefixes
!= null) {
433 bool exclude
= false;
434 foreach (XmlQualifiedName exc
in ExtensionElementPrefixes
)
435 if (exc
.Name
== "#default" && prefix
== String
.Empty
|| exc
.Name
== prefix
) {
443 return GetActualPrefix (prefix
);
448 internal enum XslDefaultValidation