2003-09-08 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[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 ("", "");
41                         currentScope.Namespaces.Add ("xml", XmlnsXml);
42                         currentScope.Namespaces.Add ("xmlns", XmlnsXmlns);
43                 }
44
45                 #endregion
46
47                 #region Properties
48
49                 public virtual string DefaultNamespace {
50                         get { return LookupNamespace (String.Empty); }
51                 }
52
53                 public XmlNameTable NameTable {
54                         get { return nameTable; }
55                 }
56
57                 #endregion
58
59                 #region Methods
60
61                 public virtual void AddNamespace (string prefix, string uri)
62                 {
63                         if (prefix == null)
64                                 throw new ArgumentNullException ("prefix", "Value cannot be null.");
65
66                         if (uri == null)
67                                 throw new ArgumentNullException ("uri", "Value cannot be null.");
68
69                         IsValidDeclaration (prefix, uri, true);
70
71                         if (currentScope.Namespaces == null)
72                                 currentScope.Namespaces = new Hashtable ();
73
74                         if (prefix != String.Empty)
75                                 nameTable.Add (prefix);
76                         currentScope.Namespaces [prefix] = nameTable.Add (uri);
77                 }
78
79                 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
80                 {
81                         string message = null;
82                         if (prefix == "xml" && uri != XmlnsXml)
83                                 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
84                         else if (uri == XmlnsXml)
85                                 message = String.Format ("Namespace URI \"{0}\" can only be declared with the fixed prefix \"xml\"", XmlnsXml);
86                         if (message == null && prefix == "xmlns")
87                                 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
88                         if (message == null && uri == XmlnsXmlns)
89                                 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
90                         if (message != null && throwException)
91                                 throw new ArgumentException (message);
92                         else
93                                 return message;
94                 }
95
96                 public virtual IEnumerator GetEnumerator ()
97                 {
98                         /*
99                         if (currentScope.Namespaces == null)
100                                 currentScope.Namespaces = new Hashtable ();
101
102                         return currentScope.Namespaces.Keys.GetEnumerator ();
103                         */
104
105                         // In fact it returns such table's enumerator that contains all the namespaces.
106                         // while HasNamespace() ignores pushed namespaces.
107                         Hashtable ht = new Hashtable ();
108                         NamespaceScope scope = currentScope;
109
110                         while (scope != null) {
111                                 if (scope.Namespaces != null) {
112                                         IEnumerator e = scope.Namespaces.Keys.GetEnumerator ();
113                                         while (e.MoveNext ()) {
114                                                 if (!ht.ContainsKey (e.Current))
115                                                         ht.Add (e.Current, scope.Namespaces [e.Current]);
116                                         }
117                                 }
118                                 scope = scope.Next;
119                         }
120                         return ht.Keys.GetEnumerator ();
121                 }
122
123                 public virtual bool HasNamespace (string prefix)
124                 {
125                         return currentScope != null && currentScope.Namespaces != null && currentScope.Namespaces.Contains (prefix);
126                 }
127
128                 public virtual string LookupNamespace (string prefix)
129                 {
130                         NamespaceScope scope = currentScope;
131
132                         while (scope != null) {
133                                 if (scope.Namespaces != null && scope.Namespaces.Contains (prefix))
134                                         return scope.Namespaces[prefix] as string;
135                                 scope = scope.Next;
136                         }
137
138                         switch (prefix) {
139                         case "xmlns":
140                                 return nameTable.Get (XmlnsXmlns);
141                         case "xml":
142                                 return nameTable.Get (XmlnsXml);
143                         case "":
144                                 return nameTable.Get (String.Empty);
145                         }
146
147                         return null;
148                 }
149
150                 public virtual string LookupPrefix (string uri)
151                 {
152                         if (uri == null)
153                                 return null;
154
155                         NamespaceScope scope = currentScope;
156
157                         while (scope != null) 
158                         {
159                                 if (scope.Namespaces != null && scope.Namespaces.ContainsValue (uri)) {
160                                         foreach (DictionaryEntry entry in scope.Namespaces) {
161                                                 if (entry.Value.ToString() == uri)
162                                                         return nameTable.Get (entry.Key as string) as string;
163                                         }
164                                 }
165
166                                 scope = scope.Next;
167                         }
168
169                         // ECMA specifies that this method returns String.Empty
170                         // in case of no match. But actually MS.NET returns null.
171                         // For more information,see
172                         //  http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
173                         //return String.Empty;
174                         return null;
175                 }
176
177                 public virtual bool PopScope ()
178                 {
179                         if (currentScope != null)
180                                 currentScope = currentScope.Next;
181
182                         return currentScope != null;
183                 }
184
185                 public virtual void PushScope ()
186                 {
187                         NamespaceScope newScope = new NamespaceScope ();
188                         newScope.Next = currentScope;
189                         currentScope = newScope;
190                 }
191
192                 public virtual void RemoveNamespace (string prefix, string uri)
193                 {
194                         if (prefix == null)
195                                 throw new ArgumentNullException ("prefix");
196
197                         if (uri == null)
198                                 throw new ArgumentNullException ("uri");
199
200                         if (currentScope == null || currentScope.Namespaces == null)
201                                 return;
202
203                         string p = nameTable.Get (prefix);
204                         string u = nameTable.Get (uri);
205                         if (p == null || u == null)
206                                 return;
207                                 
208                         string storedUri = currentScope.Namespaces [p] as string;
209                         if (storedUri == null || storedUri != u)
210                                 return;
211
212                         currentScope.Namespaces.Remove (p);
213                 }
214
215                 #endregion
216         }
217
218         internal class NamespaceScope
219         {
220                 internal NamespaceScope Next;
221                 internal Hashtable Namespaces;
222         }
223 }