[corlib] System.Collections from CoreFX (#7478)
[mono-project.git] / mcs / class / referencesource / System / compmod / system / collections / specialized / stringdictionary.cs
blob90d5c9fdb04c47bdb6451148a8d3aeec6f09ee73
1 //------------------------------------------------------------------------------
2 // <copyright file="StringDictionary.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 /*
8 */
9 namespace System.Collections.Specialized {
10 using System.Runtime.InteropServices;
11 using System.Diagnostics;
12 using System;
13 using System.Collections;
14 using System.ComponentModel.Design.Serialization;
15 using System.Globalization;
16 using System.Collections.Generic;
18 #if !COREFX
19 /// <devdoc>
20 /// <para>Implements a hashtable with the key strongly typed to be
21 /// a string rather than an object. </para>
22 /// <para>Consider this class obsolete - use Dictionary&lt;String, String&gt; instead
23 /// with a proper StringComparer instance.</para>
24 /// </devdoc>
25 [Serializable]
26 [DesignerSerializer("System.Diagnostics.Design.StringDictionaryCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)]
27 // @
28 public class StringDictionary : IEnumerable {
30 // For compatibility, we want the Keys property to return values in lower-case.
31 // That means using ToLower in each property on this type. Also for backwards
32 // compatibility, we will be converting strings to lower-case, which has a
33 // problem for some Georgian alphabets. Can't really fix it now though...
34 internal Hashtable contents = new Hashtable();
37 /// <devdoc>
38 /// <para>Initializes a new instance of the StringDictionary class.</para>
39 /// <para>If you're using file names, registry keys, etc, you want to use
40 /// a Dictionary&lt;String, Object&gt; and use
41 /// StringComparer.OrdinalIgnoreCase.</para>
42 /// </devdoc>
43 public StringDictionary() {
46 /// <devdoc>
47 /// <para>Gets the number of key-and-value pairs in the StringDictionary.</para>
48 /// </devdoc>
49 public virtual int Count {
50 get {
51 return contents.Count;
56 /// <devdoc>
57 /// <para>Indicates whether access to the StringDictionary is synchronized (thread-safe). This property is
58 /// read-only.</para>
59 /// </devdoc>
60 public virtual bool IsSynchronized {
61 get {
62 return contents.IsSynchronized;
66 /// <devdoc>
67 /// <para>Gets or sets the value associated with the specified key.</para>
68 /// </devdoc>
69 public virtual string this[string key] {
70 get {
71 if( key == null ) {
72 throw new ArgumentNullException("key");
75 return (string) contents[key.ToLower(CultureInfo.InvariantCulture)];
77 set {
78 if( key == null ) {
79 throw new ArgumentNullException("key");
82 contents[key.ToLower(CultureInfo.InvariantCulture)] = value;
86 /// <devdoc>
87 /// <para>Gets a collection of keys in the StringDictionary.</para>
88 /// </devdoc>
89 public virtual ICollection Keys {
90 get {
91 return contents.Keys;
96 /// <devdoc>
97 /// <para>Gets an object that can be used to synchronize access to the StringDictionary.</para>
98 /// </devdoc>
99 public virtual object SyncRoot {
100 get {
101 return contents.SyncRoot;
105 /// <devdoc>
106 /// <para>Gets a collection of values in the StringDictionary.</para>
107 /// </devdoc>
108 public virtual ICollection Values {
109 get {
110 return contents.Values;
114 /// <devdoc>
115 /// <para>Adds an entry with the specified key and value into the StringDictionary.</para>
116 /// </devdoc>
117 public virtual void Add(string key, string value) {
118 if( key == null ) {
119 throw new ArgumentNullException("key");
122 contents.Add(key.ToLower(CultureInfo.InvariantCulture), value);
125 /// <devdoc>
126 /// <para>Removes all entries from the StringDictionary.</para>
127 /// </devdoc>
128 public virtual void Clear() {
129 contents.Clear();
132 /// <devdoc>
133 /// <para>Determines if the string dictionary contains a specific key</para>
134 /// </devdoc>
135 public virtual bool ContainsKey(string key) {
136 if( key == null ) {
137 throw new ArgumentNullException("key");
140 return contents.ContainsKey(key.ToLower(CultureInfo.InvariantCulture));
143 /// <devdoc>
144 /// <para>Determines if the StringDictionary contains a specific value.</para>
145 /// </devdoc>
146 public virtual bool ContainsValue(string value) {
147 return contents.ContainsValue(value);
150 /// <devdoc>
151 /// <para>Copies the string dictionary values to a one-dimensional <see cref='System.Array'/> instance at the
152 /// specified index.</para>
153 /// </devdoc>
154 public virtual void CopyTo(Array array, int index) {
155 contents.CopyTo(array, index);
158 /// <devdoc>
159 /// <para>Returns an enumerator that can iterate through the string dictionary.</para>
160 /// </devdoc>
161 public virtual IEnumerator GetEnumerator() {
162 return contents.GetEnumerator();
165 /// <devdoc>
166 /// <para>Removes the entry with the specified key from the string dictionary.</para>
167 /// </devdoc>
168 public virtual void Remove(string key) {
169 if( key == null ) {
170 throw new ArgumentNullException("key");
173 contents.Remove(key.ToLower(CultureInfo.InvariantCulture));
176 /// <summary>
177 /// Make this StringDictionary subservient to some other collection.
178 /// <para>Some code was replacing the contents field with a Hashtable created elsewhere.
179 /// While it may not have been incorrect, we don't want to encourage that pattern, because
180 /// it will replace the IEqualityComparer in the Hashtable, and creates a possibly-undesirable
181 /// link between two seemingly different collections. Let's discourage that somewhat by
182 /// making this an explicit method call instead of an internal field assignment.</para>
183 /// </summary>
184 /// <param name="useThisHashtableInstead">Replaces the backing store with another, possibly aliased Hashtable.</param>
185 internal void ReplaceHashtable(Hashtable useThisHashtableInstead) {
186 contents = useThisHashtableInstead;
189 internal IDictionary<string, string> AsGenericDictionary() {
190 return new GenericAdapter(this);
192 #endif
194 #region GenericAdapter
196 // This class is used to make StringDictionary implement IDictionary<string,string> indirectly.
197 // This is done to prevent StringDictionary be serialized as IDictionary<string,string> and break its serialization by DataContractSerializer due to a bug in the serialization code.
198 class GenericAdapter : IDictionary<string, string>
201 StringDictionary m_stringDictionary;
203 internal GenericAdapter(StringDictionary stringDictionary) {
204 m_stringDictionary = stringDictionary;
207 #region IDictionary<string, string> Members
208 public void Add(string key, string value) {
210 // GenericAdapter.Add has the semantics of Item property to make ProcessStartInfo.Environment deserializable by DataContractSerializer.
211 // ProcessStartInfo.Environment property does not have a setter
212 // and so during deserialization the serializer initializes the property by calling get_Environment and
213 // then populates it via IDictionary<,>.Add per item.
214 // However since get_Environment gives the current snapshot of environment variables we might try to insert a key that already exists.
215 // (For Example 'PATH') causing an exception. This implementation ensures that we overwrite values in case of duplication.
217 this[key] = value;
220 public bool ContainsKey(string key) {
221 return m_stringDictionary.ContainsKey(key);
224 public void Clear() {
225 m_stringDictionary.Clear();
228 public int Count {
229 get {
230 return m_stringDictionary.Count;
234 // Items added to allow StringDictionary to provide IDictionary<string, string> support.
235 ICollectionToGenericCollectionAdapter _values;
236 ICollectionToGenericCollectionAdapter _keys;
238 // IDictionary<string,string>.Item vs StringDictioanry.Item
239 // IDictionary<string,string>.get_Item i. KeyNotFoundException when the property is retrieved and key is not found.
240 // StringBuilder.get_Item i. Returns null in case the key is not found.
241 public string this[string key] {
242 get {
243 if (key == null) {
244 throw new ArgumentNullException("key");
247 if (!m_stringDictionary.ContainsKey(key)) throw new KeyNotFoundException();
249 return m_stringDictionary[key];
251 set {
252 if (key == null) {
253 throw new ArgumentNullException("key");
256 m_stringDictionary[key] = value;
260 // This method returns a read-only view of the Keys in the StringDictinary.
261 public ICollection<string> Keys {
262 get {
263 if( _keys == null ) {
264 _keys = new ICollectionToGenericCollectionAdapter(m_stringDictionary, KeyOrValue.Key);
266 return _keys;
270 // This method returns a read-only view of the Values in the StringDictionary.
271 public ICollection<string> Values {
272 get {
273 if( _values == null ) {
274 _values = new ICollectionToGenericCollectionAdapter(m_stringDictionary, KeyOrValue.Value);
276 return _values;
280 // IDictionary<string,string>.Remove vs StringDictionary.Remove.
281 // IDictionary<string,string>.Remove- i. Returns a bool status that represents success\failure.
282 // ii. Returns false in case key is not found.
283 // StringDictionary.Remove i. Does not return the status and does nothing in case key is not found.
284 public bool Remove(string key) {
286 // Check if the key is not present and return false.
287 if (!m_stringDictionary.ContainsKey(key)) return false;
289 // We call the virtual StringDictionary.Remove method to ensure any subClass gets the expected behavior.
290 m_stringDictionary.Remove(key);
292 // If the above call has succeeded we simply return true.
293 return true;
297 public bool TryGetValue(string key, out string value) {
298 if (!m_stringDictionary.ContainsKey(key)) {
299 value = null;
300 return false;
303 value = m_stringDictionary[key];
304 return true;
307 void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item) {
308 m_stringDictionary.Add(item.Key, item.Value);
311 bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item) {
312 string value;
313 return TryGetValue(item.Key, out value) && value.Equals(item.Value);
316 void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
317 if( array == null )
318 throw new ArgumentNullException("array", SR.GetString(SR.ArgumentNull_Array));
319 if( arrayIndex < 0 )
320 throw new ArgumentOutOfRangeException("arrayIndex", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNum));
321 if( array.Length - arrayIndex < Count )
322 throw new ArgumentException(SR.GetString(SR.Arg_ArrayPlusOffTooSmall));
324 int index = arrayIndex;
326 foreach (DictionaryEntry entry in m_stringDictionary) {
327 array[index++] = new KeyValuePair<string, string>((string)entry.Key, (string)entry.Value);
331 bool ICollection<KeyValuePair<string,string>>.IsReadOnly {
332 get {
333 return false;
337 // ICollection<KeyValuePair<string, string>>.Remove vs StringDictionary.Remove
338 // ICollection<KeyValuePair<string, string>>.Remove - i. Return status.
339 // ii. Returns false in case the items is not found.
340 // StringDictionary.Remove i. Does not return a status and does nothing in case the key is not found.
341 bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item) {
343 // If the item is not found return false.
344 ICollection<KeyValuePair<string, string>> iCollection = this;
345 if( !iCollection.Contains(item) ) return false;
347 // We call the virtual StringDictionary.Remove method to ensure any subClass gets the expected behavior.
348 m_stringDictionary.Remove(item.Key);
350 // If the above call has succeeded we simply return true.
351 return true;
354 IEnumerator IEnumerable.GetEnumerator() {
355 return this.GetEnumerator();
358 // The implementation asummes that this.GetEnumerator().Current can be casted to DictionaryEntry.
359 // and although StringDictionary.GetEnumerator() returns IEnumerator and is a virtual method
360 // it should be ok to take that assumption since it is an implicit contract.
361 public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
363 foreach (DictionaryEntry dictionaryEntry in m_stringDictionary)
364 yield return new KeyValuePair<string, string>((string)dictionaryEntry.Key, (string)dictionaryEntry.Value);
367 internal enum KeyOrValue // Used internally for IDictionary<string, string> support
369 Key,
370 Value
373 // This Adapter converts StringDictionary.Keys and StringDictionary.Values to ICollection<string>
374 // Since StringDictionary implements a virtual StringDictionary.Keys and StringDictionary.Values
375 private class ICollectionToGenericCollectionAdapter : ICollection<string> {
377 StringDictionary _internal;
378 KeyOrValue _keyOrValue;
380 public ICollectionToGenericCollectionAdapter(StringDictionary source, KeyOrValue keyOrValue) {
381 if (source == null) throw new ArgumentNullException("source");
383 _internal = source;
384 _keyOrValue = keyOrValue;
387 public void Add(string item) {
388 ThrowNotSupportedException();
391 public void Clear() {
392 ThrowNotSupportedException();
395 public void ThrowNotSupportedException() {
396 if( _keyOrValue == KeyOrValue.Key ) {
397 throw new NotSupportedException(SR.GetString(SR.NotSupported_KeyCollectionSet)); //Same as KeyCollection/ValueCollection
399 throw new NotSupportedException(SR.GetString(SR.NotSupported_ValueCollectionSet)); //Same as KeyCollection/ValueCollection
403 public bool Contains(string item) {
404 // The underlying backing store for the StringDictionary is a HashTable so we
405 // want to delegate Contains to respective ContainsKey/ContainsValue functionality
406 // depending upon whether we are using Keys or Value collections.
408 if( _keyOrValue == KeyOrValue.Key ) {
409 return _internal.ContainsKey(item);
411 return _internal.ContainsValue(item);
414 public void CopyTo(string[] array, int arrayIndex) {
415 var collection = GetUnderlyingCollection();
416 collection.CopyTo(array, arrayIndex);
419 public int Count {
420 get {
421 return _internal.Count; // hashtable count is same as key/value count.
425 public bool IsReadOnly {
426 get {
427 return true; //Same as KeyCollection/ValueCollection
431 public bool Remove(string item) {
432 ThrowNotSupportedException();
433 return false;
436 private ICollection GetUnderlyingCollection() {
437 if( _keyOrValue == KeyOrValue.Key ) {
438 return (ICollection) _internal.Keys;
440 return (ICollection) _internal.Values;
443 public IEnumerator<string> GetEnumerator() {
444 ICollection collection = GetUnderlyingCollection();
446 // This is doing the same as collection.Cast<string>()
447 foreach (string entry in collection) {
448 yield return entry;
452 IEnumerator IEnumerable.GetEnumerator() {
453 return GetUnderlyingCollection().GetEnumerator();
456 #endregion
458 #endregion
459 #if !COREFX
461 #endif