more optimization
[mono.git] / mcs / class / System.XML / System.Xml / XmlNamespaceManager.cs
1 //
2 // XmlNamespaceManager.cs
3 //
4 // Author:
5 //   Jason Diamond (jason@injektilo.org)
6 //
7 // (C) 2001 Jason Diamond  http://injektilo.org/
8 //
9
10 using System.Collections;
11
12 namespace System.Xml
13 {
14         public class XmlNamespaceManager : IEnumerable
15         {
16                 #region Fields
17
18                 private XmlNameTable nameTable;
19                 private NamespaceScope currentScope;
20                 internal const string XmlnsXml = "http://www.w3.org/XML/1998/namespace";
21                 internal const string XmlnsXmlns = "http://www.w3.org/2000/xmlns/";
22
23                 #endregion
24
25                 #region Constructor
26
27                 internal XmlNamespaceManager () {}
28                 public XmlNamespaceManager (XmlNameTable nameTable)
29                 {
30                         this.nameTable = nameTable;
31
32                         nameTable.Add ("xmlns");
33                         nameTable.Add ("xml");
34                         nameTable.Add (String.Empty);
35                         nameTable.Add (XmlnsXmlns);
36                         nameTable.Add (XmlnsXml);
37
38                         PushScope ();
39                         currentScope.Namespaces = new Hashtable ();
40                         currentScope.Namespaces.Add ("xml", XmlnsXml);
41                         currentScope.Namespaces.Add ("xmlns", XmlnsXmlns);
42                 }
43
44                 #endregion
45
46                 #region Properties
47
48                 public virtual string DefaultNamespace {
49                         get { return LookupNamespace (String.Empty); }
50                 }
51
52                 public XmlNameTable NameTable {
53                         get { return nameTable; }
54                 }
55
56                 #endregion
57
58                 #region Methods
59
60                 public virtual void AddNamespace (string prefix, string uri)
61                 {
62                         if (prefix == null)
63                                 throw new ArgumentNullException ("prefix", "Value cannot be null.");
64
65                         if (uri == null)
66                                 throw new ArgumentNullException ("uri", "Value cannot be null.");
67
68                         IsValidDeclaration (prefix, uri, true);
69
70                         if (currentScope.Namespaces == null)
71                                 currentScope.Namespaces = new Hashtable ();
72
73                         if (prefix != String.Empty)
74                                 nameTable.Add (prefix);
75                         currentScope.Namespaces [prefix] = nameTable.Add (uri);
76                 }
77
78                 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
79                 {
80                         string message = null;
81                         if (prefix == "xml" && uri != XmlnsXml)
82                                 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
83                         else if (uri == XmlnsXml)
84                                 message = String.Format ("Namespace URI \"{0}\" can only be declared with the fixed prefix \"xml\"", XmlnsXml);
85                         if (message == null && prefix == "xmlns")
86                                 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
87                         if (message == null && uri == XmlnsXmlns)
88                                 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
89                         if (message != null && throwException)
90                                 throw new ArgumentException (message);
91                         else
92                                 return message;
93                 }
94
95                 public virtual IEnumerator GetEnumerator ()
96                 {
97                         if (currentScope.Namespaces == null)
98                                 currentScope.Namespaces = new Hashtable ();
99
100                         return currentScope.Namespaces.Keys.GetEnumerator ();
101                 }
102
103                 public virtual bool HasNamespace (string prefix)
104                 {
105                         return currentScope != null && currentScope.Namespaces != null && currentScope.Namespaces.Contains (prefix);
106                 }
107
108                 public virtual string LookupNamespace (string prefix)
109                 {
110                         NamespaceScope scope = currentScope;
111
112                         while (scope != null) {
113                                 if (scope.Namespaces != null && scope.Namespaces.Contains (prefix))
114                                         return scope.Namespaces[prefix] as string;
115                                 scope = scope.Next;
116                         }
117
118                         switch (prefix) {
119                         case "xmlns":
120                                 return nameTable.Get (XmlnsXmlns);
121                         case "xml":
122                                 return nameTable.Get (XmlnsXml);
123                         case "":
124                                 return nameTable.Get (String.Empty);
125                         }
126
127                         return null;
128                 }
129
130                 public virtual string LookupPrefix (string uri)
131                 {
132                         if (uri == null)
133                                 return null;
134
135                         NamespaceScope scope = currentScope;
136
137                         while (scope != null) 
138                         {
139                                 if (scope.Namespaces != null && scope.Namespaces.ContainsValue (uri)) {
140                                         foreach (DictionaryEntry entry in scope.Namespaces) {
141                                                 if (entry.Value.ToString() == uri)
142                                                         return nameTable.Get (entry.Key as string) as string;
143                                         }
144                                 }
145
146                                 scope = scope.Next;
147                         }
148
149                         // ECMA specifies that this method returns String.Empty
150                         // in case of no match. But actually MS.NET returns null.
151                         // For more information,see
152                         //  http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
153                         //return String.Empty;
154                         return null;
155                 }
156
157                 public virtual bool PopScope ()
158                 {
159                         if (currentScope != null)
160                                 currentScope = currentScope.Next;
161
162                         return currentScope != null;
163                 }
164
165                 public virtual void PushScope ()
166                 {
167                         NamespaceScope newScope = new NamespaceScope ();
168                         newScope.Next = currentScope;
169                         currentScope = newScope;
170                 }
171
172                 public virtual void RemoveNamespace (string prefix, string uri)
173                 {
174                         if (prefix == null)
175                                 throw new ArgumentNullException ("prefix");
176
177                         if (uri == null)
178                                 throw new ArgumentNullException ("uri");
179
180                         if (currentScope == null || currentScope.Namespaces == null)
181                                 return;
182
183                         string p = nameTable.Get (prefix);
184                         string u = nameTable.Get (uri);
185                         if (p == null || u == null)
186                                 return;
187                                 
188                         string storedUri = currentScope.Namespaces [p] as string;
189                         if (storedUri == null || storedUri != u)
190                                 return;
191
192                         currentScope.Namespaces.Remove (p);
193                 }
194
195                 #endregion
196         }
197
198         internal class NamespaceScope
199         {
200                 internal NamespaceScope Next;
201                 internal Hashtable Namespaces;
202         }
203 }