Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / XmlNamespacemanager.cs
blob7cab6b1036d4c76f9f14d682f19c3d778cb3b50e
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlNamespaceManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml {
10 using System;
11 using System.IO;
12 using System.Collections;
13 using System.Diagnostics;
14 using System.Collections.Generic;
16 public class XmlNamespaceManager : IXmlNamespaceResolver, IEnumerable {
17 #if !SILVERLIGHT // EmptyResolver is not used in Silverlight
18 static volatile IXmlNamespaceResolver s_EmptyResolver;
19 #endif
21 struct NamespaceDeclaration {
22 public string prefix;
23 public string uri;
24 public int scopeId;
25 public int previousNsIndex;
27 public void Set( string prefix, string uri, int scopeId, int previousNsIndex ) {
28 this.prefix = prefix;
29 this.uri = uri;
30 this.scopeId = scopeId;
31 this.previousNsIndex = previousNsIndex;
35 // array with namespace declarations
36 NamespaceDeclaration[] nsdecls;
38 // index of last declaration
39 int lastDecl = 0;
41 // name table
42 XmlNameTable nameTable;
44 // ID (depth) of the current scope
45 int scopeId;
47 // hash table for faster lookup when there is lots of namespaces
48 Dictionary<string,int> hashTable;
49 bool useHashtable;
51 // atomized prefixes for "xml" and "xmlns"
52 string xml;
53 string xmlNs;
55 // Constants
56 const int MinDeclsCountForHashtable = 16;
58 #if !SILVERLIGHT // EmptyResolver is not used in Silverlight
59 internal static IXmlNamespaceResolver EmptyResolver {
60 get {
61 if ( s_EmptyResolver == null ) {
62 // no locking; the empty resolver is immutable so it's not a problem that it may get initialized more than once
63 s_EmptyResolver = new XmlNamespaceManager( new NameTable() );
65 return s_EmptyResolver;
68 #endif
70 #if !SILVERLIGHT // This constructor is not used in Silverlight
71 internal XmlNamespaceManager() {
73 #endif
75 public XmlNamespaceManager( XmlNameTable nameTable ) {
76 this.nameTable = nameTable;
77 xml = nameTable.Add("xml");
78 xmlNs = nameTable.Add("xmlns");
80 nsdecls = new NamespaceDeclaration[8];
81 string emptyStr = nameTable.Add( string.Empty );
82 nsdecls[0].Set( emptyStr, emptyStr, -1, -1 );
83 nsdecls[1].Set( xmlNs, nameTable.Add( XmlReservedNs.NsXmlNs ), -1, -1 );
84 nsdecls[2].Set( xml, nameTable.Add( XmlReservedNs.NsXml ), 0, -1 );
85 lastDecl = 2;
86 scopeId = 1;
89 public virtual XmlNameTable NameTable {
90 get {
91 return nameTable;
95 public virtual string DefaultNamespace {
96 get {
97 string defaultNs = LookupNamespace( string.Empty );
98 return ( defaultNs == null ) ? string.Empty : defaultNs;
102 public virtual void PushScope() {
103 scopeId++;
106 public virtual bool PopScope() {
107 int decl = lastDecl;
108 if ( scopeId == 1 ) {
109 return false;
111 while( nsdecls[decl].scopeId == scopeId ) {
112 if ( useHashtable ) {
113 hashTable[nsdecls[decl].prefix] = nsdecls[decl].previousNsIndex;
115 decl--;
116 Debug.Assert( decl >= 2 );
118 lastDecl = decl;
119 scopeId--;
120 return true;
123 public virtual void AddNamespace( string prefix, string uri ) {
124 if ( uri == null )
125 throw new ArgumentNullException( "uri" );
127 if ( prefix == null )
128 throw new ArgumentNullException( "prefix" );
130 prefix = nameTable.Add( prefix );
131 uri = nameTable.Add( uri );
133 if ( ( Ref.Equal( xml, prefix ) && !uri.Equals( XmlReservedNs.NsXml ) ) ) {
134 throw new ArgumentException( Res.GetString( Res.Xml_XmlPrefix ) );
136 if ( Ref.Equal( xmlNs, prefix ) ) {
137 throw new ArgumentException( Res.GetString( Res.Xml_XmlnsPrefix ) );
140 int declIndex = LookupNamespaceDecl( prefix );
141 int previousDeclIndex = -1;
142 if ( declIndex != -1 ) {
143 if ( nsdecls[declIndex].scopeId == scopeId ) {
144 // redefine if in the same scope
145 nsdecls[declIndex].uri = uri;
146 return;
148 else {
149 // othewise link
150 previousDeclIndex = declIndex;
154 // set new namespace declaration
155 if ( lastDecl == nsdecls.Length - 1 ) {
156 NamespaceDeclaration[] newNsdecls = new NamespaceDeclaration[nsdecls.Length * 2];
157 Array.Copy( nsdecls, 0, newNsdecls, 0, nsdecls.Length );
158 nsdecls = newNsdecls;
161 nsdecls[++lastDecl].Set( prefix, uri, scopeId, previousDeclIndex );
163 // add to hashTable
164 if ( useHashtable ) {
165 hashTable[prefix] = lastDecl;
167 // or create a new hashTable if the threashold has been reached
168 else if ( lastDecl >= MinDeclsCountForHashtable ) {
169 // add all to hash table
170 Debug.Assert( hashTable == null );
171 hashTable = new Dictionary<string,int>( lastDecl );
172 for ( int i = 0; i <= lastDecl; i++ ) {
173 hashTable[nsdecls[i].prefix] = i;
175 useHashtable = true;
179 public virtual void RemoveNamespace( string prefix, string uri ) {
180 if ( uri == null ) {
181 throw new ArgumentNullException( "uri" );
183 if ( prefix == null ) {
184 throw new ArgumentNullException( "prefix" );
187 int declIndex = LookupNamespaceDecl( prefix );
188 while ( declIndex != -1 ) {
189 if ( String.Equals( nsdecls[declIndex].uri, uri ) && nsdecls[declIndex].scopeId == scopeId ) {
190 nsdecls[declIndex].uri = null;
192 declIndex = nsdecls[declIndex].previousNsIndex;
196 public virtual IEnumerator GetEnumerator() {
197 Dictionary<string, string> prefixes = new Dictionary<string, string>(lastDecl + 1);
198 for( int thisDecl = 0; thisDecl <= lastDecl; thisDecl ++ ) {
199 if ( nsdecls[thisDecl].uri != null ) {
200 prefixes[nsdecls[thisDecl].prefix] = nsdecls[thisDecl].prefix;
203 return prefixes.Keys.GetEnumerator();
206 // This pragma disables a warning that the return type is not CLS-compliant, but generics are part of CLS in Whidbey.
207 #pragma warning disable 3002
208 public virtual IDictionary<string,string> GetNamespacesInScope( XmlNamespaceScope scope ) {
209 #pragma warning restore 3002
210 int i = 0;
211 switch ( scope ) {
212 case XmlNamespaceScope.All:
213 i = 2;
214 break;
215 case XmlNamespaceScope.ExcludeXml:
216 i = 3;
217 break;
218 case XmlNamespaceScope.Local:
219 i = lastDecl;
220 while ( nsdecls[i].scopeId == scopeId ) {
221 i--;
222 Debug.Assert( i >= 2 );
224 i++;
225 break;
228 Dictionary<string,string> dict = new Dictionary<string, string>( lastDecl - i + 1 );
229 for( ; i <= lastDecl; i++ ) {
230 string prefix = nsdecls[i].prefix;
231 string uri = nsdecls[i].uri;
232 Debug.Assert( prefix != null );
234 if ( uri != null ) {
235 if ( uri.Length > 0 || prefix.Length > 0 || scope == XmlNamespaceScope.Local ) {
236 dict[prefix] = uri;
238 else {
239 // default namespace redeclared to "" -> remove from list for all scopes other than local
240 dict.Remove( prefix );
244 return dict;
247 public virtual string LookupNamespace( string prefix ) {
248 int declIndex = LookupNamespaceDecl( prefix );
249 return ( declIndex == -1 ) ? null : nsdecls[declIndex].uri;
252 private int LookupNamespaceDecl( string prefix ) {
253 if ( useHashtable ) {
254 int declIndex;
255 if ( hashTable.TryGetValue( prefix, out declIndex ) ) {
256 while ( declIndex != -1 && nsdecls[declIndex].uri == null ) {
257 declIndex = nsdecls[declIndex].previousNsIndex;
259 return declIndex;
261 return -1;
263 else {
264 // First assume that prefix is atomized
265 for( int thisDecl = lastDecl; thisDecl >= 0; thisDecl -- ) {
266 if ( (object)nsdecls[thisDecl].prefix == (object)prefix && nsdecls[thisDecl].uri != null ) {
267 return thisDecl;
271 // Non-atomized lookup
272 for( int thisDecl = lastDecl; thisDecl >= 0; thisDecl -- ) {
273 if ( String.Equals( nsdecls[thisDecl].prefix, prefix ) && nsdecls[thisDecl].uri != null ) {
274 return thisDecl;
278 return -1;
281 public virtual string LookupPrefix( string uri ) {
282 // Don't assume that prefix is atomized
283 for( int thisDecl = lastDecl; thisDecl >= 0; thisDecl -- ) {
284 if ( String.Equals( nsdecls[thisDecl].uri, uri ) ) {
285 string prefix = nsdecls[thisDecl].prefix;
286 if ( String.Equals( LookupNamespace( prefix ), uri ) ) {
287 return prefix;
291 return null;
294 public virtual bool HasNamespace( string prefix ) {
295 // Don't assume that prefix is atomized
296 for( int thisDecl = lastDecl; nsdecls[thisDecl].scopeId == scopeId; thisDecl-- ) {
297 if ( String.Equals( nsdecls[thisDecl].prefix, prefix ) && nsdecls[thisDecl].uri != null ) {
298 if ( prefix.Length > 0 || nsdecls[thisDecl].uri.Length > 0 ) {
299 return true;
301 return false;
304 return false;
307 #if !SILVERLIGHT // This method is not used in Silverlight
308 internal bool GetNamespaceDeclaration( int idx, out string prefix, out string uri ) {
309 idx = lastDecl - idx;
310 if ( idx < 0 ) {
311 prefix = uri = null;
312 return false;
315 prefix = nsdecls[idx].prefix;
316 uri = nsdecls[idx].uri;
318 return true;
320 #endif
321 } //XmlNamespaceManager