2 // XmlNamespaceManager.cs
5 // Jason Diamond (jason@injektilo.org)
6 // Ben Maurer (bmaurer@users.sourceforge.net)
8 // (C) 2001 Jason Diamond http://injektilo.org/
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.
33 using System.Collections;
34 using System.Collections.Specialized;
38 public class XmlNamespaceManager : IEnumerable
42 public string Prefix, Uri;
47 public string DefaultNamespace;
56 string defaultNamespace;
61 decls = new NsDecl [10];
62 scopes = new NsScope [40];
65 // precondition declPos == nsDecl.Length
68 NsDecl [] old = decls;
69 decls = new NsDecl [declPos * 2 + 1];
71 Array.Copy (old, 0, decls, 0, declPos);
74 // precondition scopePos == scopes.Length
77 NsScope [] old = scopes;
78 scopes = new NsScope [scopePos * 2 + 1];
80 Array.Copy (old, 0, scopes, 0, scopePos);
87 private XmlNameTable nameTable;
88 internal const string XmlnsXml = "http://www.w3.org/XML/1998/namespace";
89 internal const string XmlnsXmlns = "http://www.w3.org/2000/xmlns/";
90 internal const string PrefixXml = "xml";
91 internal const string PrefixXmlns = "xmlns";
97 public XmlNamespaceManager (XmlNameTable nameTable)
99 this.nameTable = nameTable;
101 nameTable.Add (PrefixXmlns);
102 nameTable.Add (PrefixXml);
103 nameTable.Add (String.Empty);
104 nameTable.Add (XmlnsXmlns);
105 nameTable.Add (XmlnsXml);
114 public virtual string DefaultNamespace {
115 get { return defaultNamespace == null ? string.Empty : defaultNamespace; }
118 public XmlNameTable NameTable {
119 get { return nameTable; }
126 public virtual void AddNamespace (string prefix, string uri)
128 AddNamespace (prefix, uri, false);
132 public virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
134 internal virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
138 throw new ArgumentNullException ("prefix", "Value cannot be null.");
141 throw new ArgumentNullException ("uri", "Value cannot be null.");
142 if (!atomizedNames) {
143 prefix = nameTable.Add (prefix);
144 uri = nameTable.Add (uri);
147 IsValidDeclaration (prefix, uri, true);
149 if (prefix.Length == 0)
150 defaultNamespace = uri;
152 for (int i = declPos; i > declPos - count; i--) {
153 if (object.ReferenceEquals (decls [i].Prefix, prefix)) {
162 if (declPos == decls.Length)
164 decls [declPos].Prefix = prefix;
165 decls [declPos].Uri = uri;
168 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
170 string message = null;
171 if (prefix == PrefixXml && uri != XmlnsXml)
172 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
173 else if (uri == XmlnsXml)
174 message = String.Format ("Namespace URI \"{0}\" is reserved to be mapped to \"xml\" and cannot be declared.", XmlnsXml);
175 if (message == null && prefix == "xmlns")
176 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
177 if (message == null && uri == XmlnsXmlns)
178 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
179 if (message != null && throwException)
180 throw new ArgumentException (message);
185 public virtual IEnumerator GetEnumerator ()
187 // In fact it returns such table's enumerator that contains all the namespaces.
188 // while HasNamespace() ignores pushed namespaces.
190 Hashtable ht = new Hashtable ();
191 for (int i = 0; i <= declPos; i++) {
192 if (decls [i].Prefix != string.Empty && decls [i].Uri != null) {
193 ht [decls [i].Prefix] = decls [i].Uri;
197 ht [string.Empty] = DefaultNamespace;
198 ht [PrefixXml] = XmlnsXml;
199 ht [PrefixXmlns] = XmlnsXmlns;
201 return ht.Keys.GetEnumerator ();
205 public virtual StringDictionary GetNamespacesInScope (XmlNamespaceScope scope)
207 throw new NotImplementedException ();
211 public virtual bool HasNamespace (string prefix)
213 if (prefix == null || count == 0)
216 for (int i = declPos; i > declPos - count; i--) {
217 if (decls [i].Prefix == prefix)
224 public virtual string LookupNamespace (string prefix)
226 return LookupNamespace (prefix, true);
230 public virtual string LookupNamespace (string prefix, bool atomizedName)
232 internal virtual string LookupNamespace (string prefix, bool atomizedName)
237 return nameTable.Get (XmlnsXmlns);
239 return nameTable.Get (XmlnsXml);
241 return DefaultNamespace;
246 for (int i = declPos; i >= 0; i--) {
247 if (CompareString (decls [i].Prefix, prefix, atomizedName) && decls [i].Uri != null /* null == flag for removed */)
248 return decls [i].Uri;
254 public virtual string LookupPrefix (string uri)
256 return LookupPrefix (uri, true);
259 private bool CompareString (string s1, string s2, bool atomizedNames)
262 return object.ReferenceEquals (s1, s2);
268 public string LookupPrefix (string uri, bool atomizedName)
270 internal string LookupPrefix (string uri, bool atomizedName)
276 if (CompareString (uri, DefaultNamespace, atomizedName))
279 if (CompareString (uri, XmlnsXml, atomizedName))
282 if (CompareString (uri, XmlnsXmlns, atomizedName))
285 for (int i = declPos; i >= 0; i--) {
286 if (CompareString (decls [i].Uri, uri, atomizedName) && decls [i].Prefix.Length > 0) // we already looked for ""
287 return decls [i].Prefix;
290 // ECMA specifies that this method returns String.Empty
291 // in case of no match. But actually MS.NET returns null.
292 // For more information,see
293 // http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
294 //return String.Empty;
298 public virtual bool PopScope ()
304 defaultNamespace = scopes [scopePos].DefaultNamespace;
305 count = scopes [scopePos].DeclCount;
310 public virtual void PushScope ()
313 if (scopePos == scopes.Length)
316 scopes [scopePos].DefaultNamespace = defaultNamespace;
317 scopes [scopePos].DeclCount = count;
321 // It is rarely used, so we don't need NameTable optimization on it.
322 public virtual void RemoveNamespace (string prefix, string uri)
324 RemoveNamespace (prefix, uri, false);
328 public virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
330 internal virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
334 throw new ArgumentNullException ("prefix");
337 throw new ArgumentNullException ("uri");
342 for (int i = declPos; i > declPos - count; i--) {
343 if (CompareString (decls [i].Prefix, prefix, atomizedNames) && CompareString (decls [i].Uri, uri, atomizedNames))
344 decls [i].Uri = null;