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);
135 public virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
137 internal virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
141 throw new ArgumentNullException ("prefix", "Value cannot be null.");
144 throw new ArgumentNullException ("uri", "Value cannot be null.");
145 if (!atomizedNames) {
146 prefix = nameTable.Add (prefix);
147 uri = nameTable.Add (uri);
150 IsValidDeclaration (prefix, uri, true);
152 if (prefix.Length == 0)
153 defaultNamespace = uri;
155 for (int i = declPos; i > declPos - count; i--) {
156 if (object.ReferenceEquals (decls [i].Prefix, prefix)) {
165 if (declPos == decls.Length)
167 decls [declPos].Prefix = prefix;
168 decls [declPos].Uri = uri;
171 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
173 string message = null;
174 if (prefix == PrefixXml && uri != XmlnsXml)
175 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
176 else if (uri == XmlnsXml)
177 message = String.Format ("Namespace URI \"{0}\" is reserved to be mapped to \"xml\" and cannot be declared.", XmlnsXml);
178 if (message == null && prefix == "xmlns")
179 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
180 if (message == null && uri == XmlnsXmlns)
181 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
182 if (message != null && throwException)
183 throw new ArgumentException (message);
188 public virtual IEnumerator GetEnumerator ()
190 // In fact it returns such table's enumerator that contains all the namespaces.
191 // while HasNamespace() ignores pushed namespaces.
193 Hashtable ht = new Hashtable ();
194 for (int i = 0; i <= declPos; i++) {
195 if (decls [i].Prefix != string.Empty && decls [i].Uri != null) {
196 ht [decls [i].Prefix] = decls [i].Uri;
200 ht [string.Empty] = DefaultNamespace;
201 ht [PrefixXml] = XmlnsXml;
202 ht [PrefixXmlns] = XmlnsXmlns;
204 return ht.Keys.GetEnumerator ();
208 public virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
210 IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
212 return GetNamespacesInScope (scope);
215 internal virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
218 Hashtable table = new Hashtable ();
220 if (scope == XmlNamespaceScope.Local) {
221 for (int i = 0; i < count; i++)
222 if (decls [declPos - i].Prefix == String.Empty && decls [declPos - i].Uri == String.Empty) {
223 if (table.Contains (String.Empty))
224 table.Remove (String.Empty);
226 else if (decls [declPos - i].Uri != null)
227 table.Add (decls [declPos - i].Prefix, decls [declPos - i].Uri);
230 for (int i = 0; i <= declPos; i++) {
231 if (decls [i].Prefix == String.Empty && decls [i].Uri == String.Empty) {
232 // removal of default namespace
233 if (table.Contains (String.Empty))
234 table.Remove (String.Empty);
236 else if (decls [i].Uri != null)
237 table [decls [i].Prefix] = decls [i].Uri;
240 if (scope == XmlNamespaceScope.All)
241 table.Add ("xml", XmlNamespaceManager.XmlnsXml);
246 public virtual bool HasNamespace (string prefix)
248 return HasNamespace (prefix, false);
253 public virtual bool HasNamespace (string prefix, bool atomizedNames)
255 internal virtual bool HasNamespace (string prefix, bool atomizedNames)
258 if (prefix == null || count == 0)
261 for (int i = declPos; i > declPos - count; i--) {
262 if (decls [i].Prefix == prefix)
269 public virtual string LookupNamespace (string prefix)
272 return LookupNamespace (prefix, false);
274 return LookupNamespace (prefix, true);
280 public virtual string LookupNamespace (string prefix, bool atomizedNames)
282 string IXmlNamespaceResolver.LookupNamespace (string prefix, bool atomizedNames)
284 return LookupNamespace (prefix, atomizedNames);
287 internal virtual string LookupNamespace (string prefix, bool atomizedNames)
292 return nameTable.Get (XmlnsXmlns);
294 return nameTable.Get (XmlnsXml);
296 return DefaultNamespace;
301 for (int i = declPos; i >= 0; i--) {
302 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && decls [i].Uri != null /* null == flag for removed */)
303 return decls [i].Uri;
309 public virtual string LookupPrefix (string uri)
312 return LookupPrefix (uri, false);
314 return LookupPrefix (uri, true);
318 private bool CompareString (string s1, string s2, bool atomizedNames)
321 return object.ReferenceEquals (s1, s2);
328 public string LookupPrefix (string uri, bool atomizedName)
330 string IXmlNamespaceResolver.LookupPrefix (string uri, bool atomizedName)
332 return LookupPrefix (uri, atomizedName);
335 internal string LookupPrefix (string uri, bool atomizedName)
341 if (CompareString (uri, DefaultNamespace, atomizedName))
344 if (CompareString (uri, XmlnsXml, atomizedName))
347 if (CompareString (uri, XmlnsXmlns, atomizedName))
350 for (int i = declPos; i >= 0; i--) {
351 if (CompareString (decls [i].Uri, uri, atomizedName) && decls [i].Prefix.Length > 0) // we already looked for ""
352 return decls [i].Prefix;
355 // ECMA specifies that this method returns String.Empty
356 // in case of no match. But actually MS.NET returns null.
357 // For more information,see
358 // http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
359 //return String.Empty;
363 public virtual bool PopScope ()
369 defaultNamespace = scopes [scopePos].DefaultNamespace;
370 count = scopes [scopePos].DeclCount;
375 public virtual void PushScope ()
378 if (scopePos == scopes.Length)
381 scopes [scopePos].DefaultNamespace = defaultNamespace;
382 scopes [scopePos].DeclCount = count;
386 // It is rarely used, so we don't need NameTable optimization on it.
387 public virtual void RemoveNamespace (string prefix, string uri)
389 RemoveNamespace (prefix, uri, false);
394 public virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
396 internal virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
400 throw new ArgumentNullException ("prefix");
403 throw new ArgumentNullException ("uri");
408 for (int i = declPos; i > declPos - count; i--) {
409 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && CompareString (decls [i].Uri, uri, atomizedNames))
410 decls [i].Uri = null;