This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / System.XML / System.Xml / XmlNamespaceManager.cs
1 //
2 // XmlNamespaceManager.cs
3 //
4 // Authors:
5 //   Jason Diamond (jason@injektilo.org)
6 //   Ben Maurer (bmaurer@users.sourceforge.net)
7 //
8 // (C) 2001 Jason Diamond  http://injektilo.org/
9 // (C) 2003 Ben Maurer
10 //
11
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System.Collections;
34 using System.Collections.Specialized;
35
36 namespace System.Xml
37 {
38         public class XmlNamespaceManager : IEnumerable
39         {
40                 #region Data
41                 struct NsDecl {
42                         public string Prefix, Uri;
43                 }
44                 
45                 struct NsScope {
46                         public int DeclCount;
47                         public string DefaultNamespace;
48                 }
49                 
50                 NsDecl [] decls;
51                 int declPos = -1;
52                 
53                 NsScope [] scopes;
54                 int scopePos = -1;
55                 
56                 string defaultNamespace;
57                 int count;
58                 
59                 void InitData ()
60                 {
61                         decls = new NsDecl [10];
62                         scopes = new NsScope [40];
63                 }
64                 
65                 // precondition declPos == nsDecl.Length
66                 void GrowDecls ()
67                 {
68                         NsDecl [] old = decls;
69                         decls = new NsDecl [declPos * 2 + 1];
70                         if (declPos > 0)
71                                 Array.Copy (old, 0, decls, 0, declPos);
72                 }
73                 
74                 // precondition scopePos == scopes.Length
75                 void GrowScopes ()
76                 {
77                         NsScope [] old = scopes;
78                         scopes = new NsScope [scopePos * 2 + 1];
79                         if (scopePos > 0)
80                                 Array.Copy (old, 0, scopes, 0, scopePos);
81                 }
82                 
83                 #endregion
84                 
85                 #region Fields
86
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";
92
93                 #endregion
94
95                 #region Constructor
96
97                 public XmlNamespaceManager (XmlNameTable nameTable)
98                 {
99                         this.nameTable = nameTable;
100
101                         nameTable.Add (PrefixXmlns);
102                         nameTable.Add (PrefixXml);
103                         nameTable.Add (String.Empty);
104                         nameTable.Add (XmlnsXmlns);
105                         nameTable.Add (XmlnsXml);
106                         
107                         InitData ();
108                 }
109
110                 #endregion
111
112                 #region Properties
113
114                 public virtual string DefaultNamespace {
115                         get { return defaultNamespace == null ? string.Empty : defaultNamespace; }
116                 }
117
118                 public XmlNameTable NameTable {
119                         get { return nameTable; }
120                 }
121
122                 #endregion
123
124                 #region Methods
125
126                 public virtual void AddNamespace (string prefix, string uri)
127                 {
128                         AddNamespace (prefix, uri, false);
129                 }
130
131 #if NET_2_0
132                 public virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
133 #else
134                 internal virtual void AddNamespace (string prefix, string uri, bool atomizedNames)
135 #endif
136                 {
137                         if (prefix == null)
138                                 throw new ArgumentNullException ("prefix", "Value cannot be null.");
139
140                         if (uri == null)
141                                 throw new ArgumentNullException ("uri", "Value cannot be null.");
142                         if (!atomizedNames) {
143                                 prefix = nameTable.Add (prefix);
144                                 uri = nameTable.Add (uri);
145                         }
146
147                         IsValidDeclaration (prefix, uri, true);
148
149                         if (prefix.Length == 0)
150                                 defaultNamespace = uri;
151                         
152                         for (int i = declPos; i > declPos - count; i--) {
153                                 if (object.ReferenceEquals (decls [i].Prefix, prefix)) {
154                                         decls [i].Uri = uri;
155                                         return;
156                                 }
157                         }
158                         
159                         declPos ++;
160                         count ++;
161                         
162                         if (declPos == decls.Length)
163                                 GrowDecls ();
164                         decls [declPos].Prefix = prefix;
165                         decls [declPos].Uri = uri;
166                 }
167
168                 internal static string IsValidDeclaration (string prefix, string uri, bool throwException)
169                 {
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);
181                         else
182                                 return message;
183                 }
184
185                 public virtual IEnumerator GetEnumerator ()
186                 {
187                         // In fact it returns such table's enumerator that contains all the namespaces.
188                         // while HasNamespace() ignores pushed namespaces.
189                         
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;
194                                 }
195                         }
196                         
197                         ht [string.Empty] = DefaultNamespace;
198                         ht [PrefixXml] = XmlnsXml;
199                         ht [PrefixXmlns] = XmlnsXmlns;
200                         
201                         return ht.Keys.GetEnumerator ();
202                 }
203 #if NET_2_0
204                 [MonoTODO]
205                 public virtual StringDictionary GetNamespacesInScope (XmlNamespaceScope scope)
206                 {
207                         throw new NotImplementedException ();
208                 }
209 #endif
210
211                 public virtual bool HasNamespace (string prefix)
212                 {
213                         if (prefix == null || count == 0)
214                                 return false;
215
216                         for (int i = declPos; i > declPos - count; i--) {
217                                 if (decls [i].Prefix == prefix)
218                                         return true;
219                         }
220                         
221                         return false;
222                 }
223
224                 public virtual string LookupNamespace (string prefix)
225                 {
226                         return LookupNamespace (prefix, true);
227                 }
228
229 #if NET_2_0
230                 public virtual string LookupNamespace (string prefix, bool atomizedName)
231 #else
232                 internal virtual string LookupNamespace (string prefix, bool atomizedName)
233 #endif
234                 {
235                         switch (prefix) {
236                         case PrefixXmlns:
237                                 return nameTable.Get (XmlnsXmlns);
238                         case PrefixXml:
239                                 return nameTable.Get (XmlnsXml);
240                         case "":
241                                 return DefaultNamespace;
242                         case null:
243                                 return null;
244                         }
245
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;
249                         }
250                         
251                         return null;
252                 }
253
254                 public virtual string LookupPrefix (string uri)
255                 {
256                         return LookupPrefix (uri, true);
257                 }
258
259                 private bool CompareString (string s1, string s2, bool atomizedNames)
260                 {
261                         if (atomizedNames)
262                                 return object.ReferenceEquals (s1, s2);
263                         else
264                                 return s1 == s2;
265                 }
266
267 #if NET_2_0
268                 public string LookupPrefix (string uri, bool atomizedName)
269 #else
270                 internal string LookupPrefix (string uri, bool atomizedName)
271 #endif
272                 {
273                         if (uri == null)
274                                 return null;
275
276                         if (CompareString (uri, DefaultNamespace, atomizedName))
277                                 return string.Empty;
278
279                         if (CompareString (uri, XmlnsXml, atomizedName))
280                                 return PrefixXml;
281                         
282                         if (CompareString (uri, XmlnsXmlns, atomizedName))
283                                 return PrefixXmlns;
284
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;
288                         }
289
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;
295                         return null;
296                 }
297
298                 public virtual bool PopScope ()
299                 {
300                         if (scopePos == -1)
301                                 return false;
302
303                         declPos -= count;
304                         defaultNamespace = scopes [scopePos].DefaultNamespace;
305                         count = scopes [scopePos].DeclCount;
306                         scopePos --;
307                         return true;
308                 }
309
310                 public virtual void PushScope ()
311                 {
312                         scopePos ++;
313                         if (scopePos == scopes.Length)
314                                 GrowScopes ();
315                         
316                         scopes [scopePos].DefaultNamespace = defaultNamespace;
317                         scopes [scopePos].DeclCount = count;
318                         count = 0;
319                 }
320
321                 // It is rarely used, so we don't need NameTable optimization on it.
322                 public virtual void RemoveNamespace (string prefix, string uri)
323                 {
324                         RemoveNamespace (prefix, uri, false);
325                 }
326
327 #if NET_2_0
328                 public virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
329 #else
330                 internal virtual void RemoveNamespace (string prefix, string uri, bool atomizedNames)
331 #endif
332                 {
333                         if (prefix == null)
334                                 throw new ArgumentNullException ("prefix");
335
336                         if (uri == null)
337                                 throw new ArgumentNullException ("uri");
338                         
339                         if (count == 0)
340                                 return;
341
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;
345                         }
346                 }
347
348                 #endregion
349         }
350 }