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\" can only be bound to the fixed namespace URI \"{0}\". \"{1}\" is invalid.", XmlnsXml, uri);
176 else if (message == null && prefix == "xmlns")
177 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
178 else if (message == null && uri == XmlnsXmlns)
179 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
180 if (message != null && throwException)
181 throw new ArgumentException (message);
186 public virtual IEnumerator GetEnumerator ()
188 // In fact it returns such table's enumerator that contains all the namespaces.
189 // while HasNamespace() ignores pushed namespaces.
191 Hashtable ht = new Hashtable ();
192 for (int i = 0; i <= declPos; i++) {
193 if (decls [i].Prefix != string.Empty && decls [i].Uri != null) {
194 ht [decls [i].Prefix] = decls [i].Uri;
198 ht [string.Empty] = DefaultNamespace;
199 ht [PrefixXml] = XmlnsXml;
200 ht [PrefixXmlns] = XmlnsXmlns;
202 return ht.Keys.GetEnumerator ();
206 public virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
208 IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
210 return GetNamespacesInScope (scope);
213 internal virtual IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
216 Hashtable table = new Hashtable ();
218 if (scope == XmlNamespaceScope.Local) {
219 for (int i = 0; i < count; i++)
220 if (decls [declPos - i].Prefix == String.Empty && decls [declPos - i].Uri == String.Empty) {
221 if (table.Contains (String.Empty))
222 table.Remove (String.Empty);
224 else if (decls [declPos - i].Uri != null)
225 table.Add (decls [declPos - i].Prefix, decls [declPos - i].Uri);
228 for (int i = 0; i <= declPos; i++) {
229 if (decls [i].Prefix == String.Empty && decls [i].Uri == String.Empty) {
230 // removal of default namespace
231 if (table.Contains (String.Empty))
232 table.Remove (String.Empty);
234 else if (decls [i].Uri != null)
235 table [decls [i].Prefix] = decls [i].Uri;
238 if (scope == XmlNamespaceScope.All)
239 table.Add ("xml", XmlNamespaceManager.XmlnsXml);
244 public virtual bool HasNamespace (string prefix)
246 return HasNamespace (prefix, false);
251 public virtual bool HasNamespace (string prefix, bool atomizedNames)
253 internal virtual bool HasNamespace (string prefix, bool atomizedNames)
256 if (prefix == null || count == 0)
259 for (int i = declPos; i > declPos - count; i--) {
260 if (decls [i].Prefix == prefix)
267 public virtual string LookupNamespace (string prefix)
270 return LookupNamespace (prefix, false);
272 return LookupNamespace (prefix, true);
278 public virtual string LookupNamespace (string prefix, bool atomizedNames)
280 string IXmlNamespaceResolver.LookupNamespace (string prefix, bool atomizedNames)
282 return LookupNamespace (prefix, atomizedNames);
285 internal virtual string LookupNamespace (string prefix, bool atomizedNames)
290 return nameTable.Get (XmlnsXmlns);
292 return nameTable.Get (XmlnsXml);
294 return DefaultNamespace;
299 for (int i = declPos; i >= 0; i--) {
300 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && decls [i].Uri != null /* null == flag for removed */)
301 return decls [i].Uri;
307 public virtual string LookupPrefix (string uri)
310 return LookupPrefix (uri, false);
312 return LookupPrefix (uri, true);
316 private bool CompareString (string s1, string s2, bool atomizedNames)
319 return object.ReferenceEquals (s1, s2);
326 public string LookupPrefix (string uri, bool atomizedName)
328 string IXmlNamespaceResolver.LookupPrefix (string uri, bool atomizedName)
330 return LookupPrefix (uri, atomizedName);
333 internal string LookupPrefix (string uri, bool atomizedName)
339 if (CompareString (uri, DefaultNamespace, atomizedName))
342 if (CompareString (uri, XmlnsXml, atomizedName))
345 if (CompareString (uri, XmlnsXmlns, atomizedName))
348 for (int i = declPos; i >= 0; i--) {
349 if (CompareString (decls [i].Uri, uri, atomizedName) && decls [i].Prefix.Length > 0) // we already looked for ""
350 return decls [i].Prefix;
353 // ECMA specifies that this method returns String.Empty
354 // in case of no match. But actually MS.NET returns null.
355 // For more information,see
356 // http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
357 //return String.Empty;
361 public virtual bool PopScope ()
367 defaultNamespace = scopes [scopePos].DefaultNamespace;
368 count = scopes [scopePos].DeclCount;
373 public virtual void PushScope ()
376 if (scopePos == scopes.Length)
379 scopes [scopePos].DefaultNamespace = defaultNamespace;
380 scopes [scopePos].DeclCount = count;
384 // It is rarely used, so we don't need NameTable optimization on it.
385 public virtual void RemoveNamespace (string prefix, string uri)
387 RemoveNamespace (prefix, uri, false);
392 public virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
394 internal virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
398 throw new ArgumentNullException ("prefix");
401 throw new ArgumentNullException ("uri");
406 for (int i = declPos; i > declPos - count; i--) {
407 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && CompareString (decls [i].Uri, uri, atomizedNames))
408 decls [i].Uri = null;