2003-08-19 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                         
20                 HighWaterStack decls = new HighWaterStack (50);
21                 HighWaterStack scopes = new HighWaterStack (50);
22                 Namespace defaultNamespace;
23                 int count = 0;
24                 
25                 internal const string XmlnsXml = "http://www.w3.org/XML/1998/namespace";
26                 internal const string XmlnsXmlns = "http://www.w3.org/2000/xmlns/";
27
28                 string XMLNS, XML, XMLNS_URL, XML_URL;
29                 #endregion
30
31                 #region Constructor
32
33                 public XmlNamespaceManager (XmlNameTable nameTable)
34                 {
35                         this.nameTable = nameTable;
36
37                         XMLNS = nameTable.Add ("xmlns");
38                         XML = nameTable.Add ("xml");
39                         XMLNS_URL = nameTable.Add (XmlnsXmlns);
40                         XML_URL = nameTable.Add (XmlnsXml);
41                 }
42
43                 #endregion
44
45                 #region Properties
46
47                 public virtual string DefaultNamespace {
48                         get { return (defaultNamespace == null) ? String.Empty : defaultNamespace.Uri; }
49                 }
50
51                 public XmlNameTable NameTable {
52                         get { return nameTable; }
53                 }
54
55                 #endregion
56
57                 #region Methods
58
59                 public virtual void AddNamespace (string prefix, string uri)
60                 {
61                         if (prefix == null)
62                                 throw new ArgumentNullException ("prefix", "Value cannot be null.");
63
64                         if (uri == null)
65                                 throw new ArgumentNullException ("uri", "Value cannot be null.");
66
67                         IsValidDeclaration (prefix, uri, true);
68
69                         prefix = nameTable.Add (prefix);
70                         uri = nameTable.Add (uri);
71
72                         // Is it already in the table?
73                         for (int i = decls.Length - 1; i >= decls.Length - count; i--) {
74                                 Namespace decl = (Namespace)decls [i];
75                                 if (AtomicStringEquals (decl.Prefix, prefix)) {
76                                         // Then redefine it
77                                         decl.Uri = uri;
78                                         return;
79                                 }
80                         }
81                         
82                         // Otherwise, we are going to add it as a new object
83                         Namespace newDecl = (Namespace) decls.Push ();
84                         if (newDecl == null) {
85                                 newDecl = new Namespace ();
86                                 decls.AddToTop (newDecl);
87                         }
88                         newDecl.Prefix = prefix;
89                         newDecl.Uri = uri;
90                         count++;
91                         if (prefix == String.Empty)
92                                 defaultNamespace = newDecl;
93                 }
94
95                 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
96                 {
97                         string message = null;
98                         if (prefix == "xml" && uri != XmlnsXml)
99                                 message = String.Format ("Prefix \"xml\" is only allowed to the fixed uri \"{0}\"", XmlnsXml);
100                         else if (uri == XmlnsXml)
101                                 message = String.Format ("Namespace URI \"{0}\" can only be declared with the fixed prefix \"xml\"", XmlnsXml);
102                         if (message == null && prefix == "xmlns")
103                                 message = "Declaring prefix named \"xmlns\" is not allowed to any namespace.";
104                         if (message == null && uri == XmlnsXmlns)
105                                 message = String.Format ("Namespace URI \"{0}\" cannot be declared with any namespace.", XmlnsXmlns);
106                         if (message != null && throwException)
107                                 throw new ArgumentException (message);
108                         else
109                                 return message;
110                 }
111
112                 public virtual IEnumerator GetEnumerator ()
113                 {
114                         Hashtable p = new Hashtable (count);
115                         for (int i = decls.Length - 1; i >= decls.Length - count; i--) {
116                                 Namespace decl = (Namespace)decls [i];
117                                 if (decl.Prefix != String.Empty && decl.Uri != null)
118                                         p [decl.Prefix] = decl.Uri;
119                         }
120                         p [String.Empty] = DefaultNamespace;
121                         p [XML] = XML_URL;
122                         p [XMLNS] = XMLNS_URL;
123                         return p.Keys.GetEnumerator ();
124                 }
125
126                 public virtual bool HasNamespace (string prefix)
127                 {
128                         if (prefix == null) return false;
129                         
130                         for (int i = decls.Length - 1; i >= decls.Length - count; i--) {
131                                 Namespace decl = (Namespace)decls [i];
132                                 if (AtomicStringEquals (decl.Prefix, prefix) && decl.Uri != null)
133                                         return true;
134                         }
135                         return false;
136                 }
137
138                 public virtual string LookupNamespace (string prefix)
139                 {
140                         if (prefix == null)
141                                 return null;
142
143                         if (prefix == String.Empty)
144                                 return DefaultNamespace;
145
146                         if (AtomicStringEquals (XML, prefix))
147                                 return XML_URL;
148
149                         if (AtomicStringEquals (XMLNS, prefix))
150                                 return XMLNS_URL;
151
152                         for (int i = decls.Length - 1; i >= 0; i--) {
153                                 Namespace decl = (Namespace)decls [i];
154                                 if (AtomicStringEquals (decl.Prefix, prefix) && decl.Uri != null)
155                                         return decl.Uri;
156                         }
157                         return null;
158                 }
159
160
161                 public virtual string LookupPrefix (string uri)
162                 {
163                         if (uri == null)
164                                 return null;
165
166                         if (AtomicStringEquals (DefaultNamespace, uri))
167                                 return String.Empty;
168                         
169                         if (AtomicStringEquals (XML_URL, uri))
170                                 return XML;
171                         
172                         if (AtomicStringEquals (XMLNS_URL, uri))
173                                 return XMLNS;
174
175
176                         for (int i = decls.Length - 1; i >= 0; i--) {
177                                 Namespace decl = (Namespace)decls [i];
178                                 if (AtomicStringEquals (decl.Uri, uri) && decl.Uri != null)
179                                         return decl.Prefix;
180                         }
181
182                         // ECMA specifies that this method returns String.Empty
183                         // in case of no match. But actually MS.NET returns null.
184                         // For more information,see
185                         //  http://lists.ximian.com/archives/public/mono-list/2003-January/005071.html
186                         //return String.Empty;
187                         return null;
188                 }
189
190                 public virtual bool PopScope ()
191                 {
192                         Scope current = (Scope)scopes.Pop ();
193                         if (current == null) {
194                                 return false;
195                         } else {
196                                 for (int i = 0; i < count; i++)
197                                         decls.Pop ();
198                                         
199                                 defaultNamespace = current.DefaultNamespace;
200                                 count = current.Count;
201                                 return true;
202                         }
203                 }
204
205                 public virtual void PushScope ()
206                 {
207                         Scope current = (Scope)scopes.Push ();
208                         if (current == null) {
209                                 current = new Scope ();
210                                 scopes.AddToTop (current);
211                         }
212                         current.DefaultNamespace = defaultNamespace;
213                         current.Count = count;
214                         count = 0;
215                 }
216
217                 public virtual void RemoveNamespace (string prefix, string uri)
218                 {
219                         if (prefix == null)
220                                 throw new ArgumentNullException ("prefix");
221
222                         if (uri == null)
223                                 throw new ArgumentNullException ("uri");
224
225                         string p = nameTable.Get (prefix);
226                         string u = nameTable.Get (uri);
227                         if (p == null || u == null)
228                                 return;
229                                 
230                         for (int i = decls.Length - 1; i >= decls.Length - count; i--) {
231                                 Namespace n = (Namespace)decls [i];
232                                 if (AtomicStringEquals (n.Prefix, p) && AtomicStringEquals (n.Uri, u))
233                                         n.Uri = null;
234                         }
235                 }
236                 
237                 bool AtomicStringEquals (string a, string b) {
238                         if (String.Equals (a, b) && !Object.ReferenceEquals (a, b)) {
239 //                              Console.Error.WriteLine ("WARNING: {0} not interned", a);
240                         }
241                         
242                         return String.Equals (a, b);
243
244                 }
245
246                 #endregion
247                 class Namespace {
248                         public string Prefix, Uri;
249         }
250
251                 class Scope {
252                         public Namespace DefaultNamespace;
253                         public int Count;
254                 }
255         }
256 }