2 // XmlNamespaceManager.cs
5 // Jason Diamond (jason@injektilo.org)
6 // Ben Maurer (bmaurer@users.sourceforge.net)
7 // Atsushi Enomoto (atsushi@ximian.com)
9 // (C) 2001 Jason Diamond http://injektilo.org/
10 // (C) 2003 Ben Maurer
11 // (C) 2004 Novell Inc.
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Collections.Specialized;
40 public class XmlNamespaceManager : IXmlNamespaceResolver, IEnumerable
44 public string Prefix, Uri;
49 public string DefaultNamespace;
58 string defaultNamespace;
63 decls = new NsDecl [10];
64 scopes = new NsScope [40];
67 // precondition declPos == nsDecl.Length
70 NsDecl [] old = decls;
71 decls = new NsDecl [declPos * 2 + 1];
73 Array.Copy (old, 0, decls, 0, declPos);
76 // precondition scopePos == scopes.Length
79 NsScope [] old = scopes;
80 scopes = new NsScope [scopePos * 2 + 1];
82 Array.Copy (old, 0, scopes, 0, scopePos);
89 private XmlNameTable nameTable;
90 internal const string XmlnsXml = "http://www.w3.org/XML/1998/namespace";
91 internal const string XmlnsXmlns = "http://www.w3.org/2000/xmlns/";
92 internal const string PrefixXml = "xml";
93 internal const string PrefixXmlns = "xmlns";
99 public XmlNamespaceManager (XmlNameTable nameTable)
101 this.nameTable = nameTable;
103 nameTable.Add (PrefixXmlns);
104 nameTable.Add (PrefixXml);
105 nameTable.Add (String.Empty);
106 nameTable.Add (XmlnsXmlns);
107 nameTable.Add (XmlnsXml);
116 public virtual string DefaultNamespace {
117 get { return defaultNamespace == null ? string.Empty : defaultNamespace; }
120 public XmlNameTable NameTable {
121 get { return nameTable; }
128 public virtual void AddNamespace (string prefix, string uri)
130 AddNamespace (prefix, uri, false);
134 public virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
136 internal virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
140 throw new ArgumentNullException ("prefix", "Value cannot be null.");
143 throw new ArgumentNullException ("uri", "Value cannot be null.");
144 if (!atomizedNames) {
145 prefix = nameTable.Add (prefix);
146 uri = nameTable.Add (uri);
149 IsValidDeclaration (prefix, uri, true);
151 if (prefix.Length == 0)
152 defaultNamespace = uri;
154 for (int i = declPos; i > declPos - count; i--) {
155 if (object.ReferenceEquals (decls [i].Prefix, prefix)) {
164 if (declPos == decls.Length)
166 decls [declPos].Prefix = prefix;
167 decls [declPos].Uri = uri;
170 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
172 string message = null;
173 if (prefix == PrefixXml && uri != XmlnsXml)
174 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
175 else if (uri == XmlnsXml)
176 message = String.Format ("Namespace URI \"{0}\" is reserved to be mapped to \"xml\" and cannot be declared.", XmlnsXml);
177 if (message == null && prefix == "xmlns")
178 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
179 if (message == null && uri == XmlnsXmlns)
180 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
181 if (message != null && throwException)
182 throw new ArgumentException (message);
187 public virtual IEnumerator GetEnumerator ()
189 // In fact it returns such table's enumerator that contains all the namespaces.
190 // while HasNamespace() ignores pushed namespaces.
192 Hashtable ht = new Hashtable ();
193 for (int i = 0; i <= declPos; i++) {
194 if (decls [i].Prefix != string.Empty && decls [i].Uri != null) {
195 ht [decls [i].Prefix] = decls [i].Uri;
199 ht [string.Empty] = DefaultNamespace;
200 ht [PrefixXml] = XmlnsXml;
201 ht [PrefixXmlns] = XmlnsXmlns;
203 return ht.Keys.GetEnumerator ();
207 public virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
209 IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
211 return GetNamespacesInScope (scope);
214 internal virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
217 Hashtable table = new Hashtable ();
219 if (scope == XmlNamespaceScope.Local) {
220 for (int i = 0; i < count; i++)
221 if (decls [declPos - i].Prefix == String.Empty && decls [declPos - i].Uri == String.Empty) {
222 if (table.Contains (String.Empty))
223 table.Remove (String.Empty);
225 else if (decls [declPos - i].Uri != null)
226 table.Add (decls [declPos - i].Prefix, decls [declPos - i].Uri);
230 for (int i = 0; i < declPos + count; i++) {
231 if (decls [i].Prefix == String.Empty) {
232 if (table.Contains (String.Empty))
233 table.Remove (String.Empty);
235 else if (decls [i].Uri != null)
236 table [decls [i].Prefix] = decls [i].Uri;
239 if (scope == XmlNamespaceScope.All)
240 table.Add ("xml", XmlNamespaceManager.XmlnsXml);
244 public virtual bool HasNamespace (string prefix)
246 return HasNamespace (prefix, false);
250 public virtual bool HasNamespace (string prefix, bool atomizedNames)
252 internal virtual bool HasNamespace (string prefix, bool atomizedNames)
255 if (prefix == null || count == 0)
258 for (int i = declPos; i > declPos - count; i--) {
259 if (decls [i].Prefix == prefix)
266 public virtual string LookupNamespace (string prefix)
269 return LookupNamespace (prefix, false);
271 return LookupNamespace (prefix, true);
276 public virtual string LookupNamespace (string prefix, bool atomizedNames)
278 string IXmlNamespaceResolver.LookupNamespace (string prefix, bool atomizedNames)
280 return LookupNamespace (prefix, atomizedNames);
283 internal virtual string LookupNamespace (string prefix, bool atomizedNames)
288 return nameTable.Get (XmlnsXmlns);
290 return nameTable.Get (XmlnsXml);
292 return DefaultNamespace;
297 for (int i = declPos; i >= 0; i--) {
298 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && decls [i].Uri != null /* null == flag for removed */)
299 return decls [i].Uri;
305 public virtual string LookupPrefix (string uri)
308 return LookupPrefix (uri, false);
310 return LookupPrefix (uri, true);
314 private bool CompareString (string s1, string s2, bool atomizedNames)
317 return object.ReferenceEquals (s1, s2);
323 public string LookupPrefix (string uri, bool atomizedName)
325 string IXmlNamespaceResolver.LookupPrefix (string uri, bool atomizedName)
327 return LookupPrefix (uri, atomizedName);
330 internal string LookupPrefix (string uri, bool atomizedName)
336 if (CompareString (uri, DefaultNamespace, atomizedName))
339 if (CompareString (uri, XmlnsXml, atomizedName))
342 if (CompareString (uri, XmlnsXmlns, atomizedName))
345 for (int i = declPos; i >= 0; i--) {
346 if (CompareString (decls [i].Uri, uri, atomizedName) && decls [i].Prefix.Length > 0) // we already looked for ""
347 return decls [i].Prefix;
350 // ECMA specifies that this method returns String.Empty
351 // in case of no match. But actually MS.NET returns null.
352 // For more information,see
353 // http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
354 //return String.Empty;
358 public virtual bool PopScope ()
364 defaultNamespace = scopes [scopePos].DefaultNamespace;
365 count = scopes [scopePos].DeclCount;
370 public virtual void PushScope ()
373 if (scopePos == scopes.Length)
376 scopes [scopePos].DefaultNamespace = defaultNamespace;
377 scopes [scopePos].DeclCount = count;
381 // It is rarely used, so we don't need NameTable optimization on it.
382 public virtual void RemoveNamespace (string prefix, string uri)
384 RemoveNamespace (prefix, uri, false);
388 public virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
390 internal virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
394 throw new ArgumentNullException ("prefix");
397 throw new ArgumentNullException ("uri");
402 for (int i = declPos; i > declPos - count; i--) {
403 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && CompareString (decls [i].Uri, uri, atomizedNames))
404 decls [i].Uri = null;