1 //------------------------------------------------------------------------------
2 // <copyright file="XmlNamespaceManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
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
;
21 struct NamespaceDeclaration
{
25 public int previousNsIndex
;
27 public void Set( string prefix
, string uri
, int scopeId
, int previousNsIndex
) {
30 this.scopeId
= scopeId
;
31 this.previousNsIndex
= previousNsIndex
;
35 // array with namespace declarations
36 NamespaceDeclaration
[] nsdecls
;
38 // index of last declaration
42 XmlNameTable nameTable
;
44 // ID (depth) of the current scope
47 // hash table for faster lookup when there is lots of namespaces
48 Dictionary
<string,int> hashTable
;
51 // atomized prefixes for "xml" and "xmlns"
56 const int MinDeclsCountForHashtable
= 16;
58 #if !SILVERLIGHT // EmptyResolver is not used in Silverlight
59 internal static IXmlNamespaceResolver EmptyResolver
{
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
;
70 #if !SILVERLIGHT // This constructor is not used in Silverlight
71 internal XmlNamespaceManager() {
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 );
89 public virtual XmlNameTable NameTable
{
95 public virtual string DefaultNamespace
{
97 string defaultNs
= LookupNamespace( string.Empty
);
98 return ( defaultNs
== null ) ? string.Empty
: defaultNs
;
102 public virtual void PushScope() {
106 public virtual bool PopScope() {
108 if ( scopeId
== 1 ) {
111 while( nsdecls
[decl
].scopeId
== scopeId
) {
112 if ( useHashtable
) {
113 hashTable
[nsdecls
[decl
].prefix
] = nsdecls
[decl
].previousNsIndex
;
116 Debug
.Assert( decl
>= 2 );
123 public virtual void AddNamespace( string prefix
, string uri
) {
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
;
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
);
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
;
179 public virtual void RemoveNamespace( string prefix
, string uri
) {
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
212 case XmlNamespaceScope
.All
:
215 case XmlNamespaceScope
.ExcludeXml
:
218 case XmlNamespaceScope
.Local
:
220 while ( nsdecls
[i
].scopeId
== scopeId
) {
222 Debug
.Assert( i
>= 2 );
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 );
235 if ( uri
.Length
> 0 || prefix
.Length
> 0 || scope
== XmlNamespaceScope
.Local
) {
239 // default namespace redeclared to "" -> remove from list for all scopes other than local
240 dict
.Remove( prefix
);
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
) {
255 if ( hashTable
.TryGetValue( prefix
, out declIndex
) ) {
256 while ( declIndex
!= -1 && nsdecls
[declIndex
].uri
== null ) {
257 declIndex
= nsdecls
[declIndex
].previousNsIndex
;
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 ) {
271 // Non-atomized lookup
272 for( int thisDecl
= lastDecl
; thisDecl
>= 0; thisDecl
-- ) {
273 if ( String
.Equals( nsdecls
[thisDecl
].prefix
, prefix
) && nsdecls
[thisDecl
].uri
!= null ) {
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
) ) {
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 ) {
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
;
315 prefix
= nsdecls
[idx
].prefix
;
316 uri
= nsdecls
[idx
].uri
;
321 } //XmlNamespaceManager