2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="StringDictionary.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/ *
* /
namespace System.Collections.Specialized {
using System.Runtime.InteropServices ;
using System.Diagnostics ;
using System ;
using System.Collections ;
using System.ComponentModel.Design.Serialization ;
using System.Globalization ;
using System.Collections.Generic ;
2018-05-10 08:37:03 +00:00
#if ! COREFX
2016-08-03 10:59:49 +00:00
/// <devdoc>
/// <para>Implements a hashtable with the key strongly typed to be
/// a string rather than an object. </para>
/// <para>Consider this class obsolete - use Dictionary<String, String> instead
/// with a proper StringComparer instance.</para>
/// </devdoc>
[Serializable]
[DesignerSerializer("System.Diagnostics.Design.StringDictionaryCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)]
// @
public class StringDictionary : IEnumerable {
// For compatibility, we want the Keys property to return values in lower-case.
// That means using ToLower in each property on this type. Also for backwards
// compatibility, we will be converting strings to lower-case, which has a
// problem for some Georgian alphabets. Can't really fix it now though...
internal Hashtable contents = new Hashtable ( ) ;
/// <devdoc>
/// <para>Initializes a new instance of the StringDictionary class.</para>
/// <para>If you're using file names, registry keys, etc, you want to use
/// a Dictionary<String, Object> and use
/// StringComparer.OrdinalIgnoreCase.</para>
/// </devdoc>
public StringDictionary ( ) {
}
/// <devdoc>
/// <para>Gets the number of key-and-value pairs in the StringDictionary.</para>
/// </devdoc>
public virtual int Count {
get {
return contents . Count ;
}
}
/// <devdoc>
/// <para>Indicates whether access to the StringDictionary is synchronized (thread-safe). This property is
/// read-only.</para>
/// </devdoc>
public virtual bool IsSynchronized {
get {
return contents . IsSynchronized ;
}
}
/// <devdoc>
/// <para>Gets or sets the value associated with the specified key.</para>
/// </devdoc>
public virtual string this [ string key ] {
get {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
return ( string ) contents [ key . ToLower ( CultureInfo . InvariantCulture ) ] ;
}
set {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
contents [ key . ToLower ( CultureInfo . InvariantCulture ) ] = value ;
}
}
/// <devdoc>
/// <para>Gets a collection of keys in the StringDictionary.</para>
/// </devdoc>
public virtual ICollection Keys {
get {
return contents . Keys ;
}
}
/// <devdoc>
/// <para>Gets an object that can be used to synchronize access to the StringDictionary.</para>
/// </devdoc>
public virtual object SyncRoot {
get {
return contents . SyncRoot ;
}
}
/// <devdoc>
/// <para>Gets a collection of values in the StringDictionary.</para>
/// </devdoc>
public virtual ICollection Values {
get {
return contents . Values ;
}
}
/// <devdoc>
/// <para>Adds an entry with the specified key and value into the StringDictionary.</para>
/// </devdoc>
public virtual void Add ( string key , string value ) {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
contents . Add ( key . ToLower ( CultureInfo . InvariantCulture ) , value ) ;
}
/// <devdoc>
/// <para>Removes all entries from the StringDictionary.</para>
/// </devdoc>
public virtual void Clear ( ) {
contents . Clear ( ) ;
}
/// <devdoc>
/// <para>Determines if the string dictionary contains a specific key</para>
/// </devdoc>
public virtual bool ContainsKey ( string key ) {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
return contents . ContainsKey ( key . ToLower ( CultureInfo . InvariantCulture ) ) ;
}
/// <devdoc>
/// <para>Determines if the StringDictionary contains a specific value.</para>
/// </devdoc>
public virtual bool ContainsValue ( string value ) {
return contents . ContainsValue ( value ) ;
}
/// <devdoc>
/// <para>Copies the string dictionary values to a one-dimensional <see cref='System.Array'/> instance at the
/// specified index.</para>
/// </devdoc>
public virtual void CopyTo ( Array array , int index ) {
contents . CopyTo ( array , index ) ;
}
/// <devdoc>
/// <para>Returns an enumerator that can iterate through the string dictionary.</para>
/// </devdoc>
public virtual IEnumerator GetEnumerator ( ) {
return contents . GetEnumerator ( ) ;
}
/// <devdoc>
/// <para>Removes the entry with the specified key from the string dictionary.</para>
/// </devdoc>
public virtual void Remove ( string key ) {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
contents . Remove ( key . ToLower ( CultureInfo . InvariantCulture ) ) ;
}
/// <summary>
/// Make this StringDictionary subservient to some other collection.
/// <para>Some code was replacing the contents field with a Hashtable created elsewhere.
/// While it may not have been incorrect, we don't want to encourage that pattern, because
/// it will replace the IEqualityComparer in the Hashtable, and creates a possibly-undesirable
/// link between two seemingly different collections. Let's discourage that somewhat by
/// making this an explicit method call instead of an internal field assignment.</para>
/// </summary>
/// <param name="useThisHashtableInstead">Replaces the backing store with another, possibly aliased Hashtable.</param>
internal void ReplaceHashtable ( Hashtable useThisHashtableInstead ) {
contents = useThisHashtableInstead ;
}
internal IDictionary < string , string > AsGenericDictionary ( ) {
return new GenericAdapter ( this ) ;
}
2018-05-10 08:37:03 +00:00
#endif
2016-08-03 10:59:49 +00:00
#region GenericAdapter
//
// This class is used to make StringDictionary implement IDictionary<string,string> indirectly.
// 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.
2018-05-10 08:37:03 +00:00
class GenericAdapter : IDictionary < string , string >
2016-08-03 10:59:49 +00:00
{
StringDictionary m_stringDictionary ;
internal GenericAdapter ( StringDictionary stringDictionary ) {
m_stringDictionary = stringDictionary ;
}
#region IDictionary < string , string > Members
public void Add ( string key , string value ) {
// GenericAdapter.Add has the semantics of Item property to make ProcessStartInfo.Environment deserializable by DataContractSerializer.
// ProcessStartInfo.Environment property does not have a setter
// and so during deserialization the serializer initializes the property by calling get_Environment and
// then populates it via IDictionary<,>.Add per item.
// However since get_Environment gives the current snapshot of environment variables we might try to insert a key that already exists.
// (For Example 'PATH') causing an exception. This implementation ensures that we overwrite values in case of duplication.
this [ key ] = value ;
}
public bool ContainsKey ( string key ) {
return m_stringDictionary . ContainsKey ( key ) ;
}
public void Clear ( ) {
m_stringDictionary . Clear ( ) ;
}
public int Count {
get {
return m_stringDictionary . Count ;
}
}
// Items added to allow StringDictionary to provide IDictionary<string, string> support.
ICollectionToGenericCollectionAdapter _values ;
ICollectionToGenericCollectionAdapter _keys ;
// IDictionary<string,string>.Item vs StringDictioanry.Item
// IDictionary<string,string>.get_Item i. KeyNotFoundException when the property is retrieved and key is not found.
// StringBuilder.get_Item i. Returns null in case the key is not found.
public string this [ string key ] {
get {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
if ( ! m_stringDictionary . ContainsKey ( key ) ) throw new KeyNotFoundException ( ) ;
return m_stringDictionary [ key ] ;
}
set {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
m_stringDictionary [ key ] = value ;
}
}
// This method returns a read-only view of the Keys in the StringDictinary.
public ICollection < string > Keys {
get {
if ( _keys = = null ) {
_keys = new ICollectionToGenericCollectionAdapter ( m_stringDictionary , KeyOrValue . Key ) ;
}
return _keys ;
}
}
// This method returns a read-only view of the Values in the StringDictionary.
public ICollection < string > Values {
get {
if ( _values = = null ) {
_values = new ICollectionToGenericCollectionAdapter ( m_stringDictionary , KeyOrValue . Value ) ;
}
return _values ;
}
}
// IDictionary<string,string>.Remove vs StringDictionary.Remove.
// IDictionary<string,string>.Remove- i. Returns a bool status that represents success\failure.
// ii. Returns false in case key is not found.
// StringDictionary.Remove i. Does not return the status and does nothing in case key is not found.
public bool Remove ( string key ) {
// Check if the key is not present and return false.
if ( ! m_stringDictionary . ContainsKey ( key ) ) return false ;
// We call the virtual StringDictionary.Remove method to ensure any subClass gets the expected behavior.
m_stringDictionary . Remove ( key ) ;
// If the above call has succeeded we simply return true.
return true ;
}
public bool TryGetValue ( string key , out string value ) {
if ( ! m_stringDictionary . ContainsKey ( key ) ) {
value = null ;
return false ;
}
value = m_stringDictionary [ key ] ;
return true ;
}
void ICollection < KeyValuePair < string , string > > . Add ( KeyValuePair < string , string > item ) {
m_stringDictionary . Add ( item . Key , item . Value ) ;
}
bool ICollection < KeyValuePair < string , string > > . Contains ( KeyValuePair < string , string > item ) {
string value ;
return TryGetValue ( item . Key , out value ) & & value . Equals ( item . Value ) ;
}
void ICollection < KeyValuePair < string , string > > . CopyTo ( KeyValuePair < string , string > [ ] array , int arrayIndex ) {
if ( array = = null )
throw new ArgumentNullException ( "array" , SR . GetString ( SR . ArgumentNull_Array ) ) ;
if ( arrayIndex < 0 )
throw new ArgumentOutOfRangeException ( "arrayIndex" , SR . GetString ( SR . ArgumentOutOfRange_NeedNonNegNum ) ) ;
if ( array . Length - arrayIndex < Count )
throw new ArgumentException ( SR . GetString ( SR . Arg_ArrayPlusOffTooSmall ) ) ;
int index = arrayIndex ;
foreach ( DictionaryEntry entry in m_stringDictionary ) {
array [ index + + ] = new KeyValuePair < string , string > ( ( string ) entry . Key , ( string ) entry . Value ) ;
}
}
bool ICollection < KeyValuePair < string , string > > . IsReadOnly {
get {
return false ;
}
}
// ICollection<KeyValuePair<string, string>>.Remove vs StringDictionary.Remove
// ICollection<KeyValuePair<string, string>>.Remove - i. Return status.
// ii. Returns false in case the items is not found.
// StringDictionary.Remove i. Does not return a status and does nothing in case the key is not found.
bool ICollection < KeyValuePair < string , string > > . Remove ( KeyValuePair < string , string > item ) {
// If the item is not found return false.
ICollection < KeyValuePair < string , string > > iCollection = this ;
if ( ! iCollection . Contains ( item ) ) return false ;
// We call the virtual StringDictionary.Remove method to ensure any subClass gets the expected behavior.
m_stringDictionary . Remove ( item . Key ) ;
// If the above call has succeeded we simply return true.
return true ;
}
IEnumerator IEnumerable . GetEnumerator ( ) {
return this . GetEnumerator ( ) ;
}
// The implementation asummes that this.GetEnumerator().Current can be casted to DictionaryEntry.
// and although StringDictionary.GetEnumerator() returns IEnumerator and is a virtual method
// it should be ok to take that assumption since it is an implicit contract.
public IEnumerator < KeyValuePair < string , string > > GetEnumerator ( )
{
foreach ( DictionaryEntry dictionaryEntry in m_stringDictionary )
yield return new KeyValuePair < string , string > ( ( string ) dictionaryEntry . Key , ( string ) dictionaryEntry . Value ) ;
}
internal enum KeyOrValue // Used internally for IDictionary<string, string> support
{
Key ,
Value
}
// This Adapter converts StringDictionary.Keys and StringDictionary.Values to ICollection<string>
// Since StringDictionary implements a virtual StringDictionary.Keys and StringDictionary.Values
private class ICollectionToGenericCollectionAdapter : ICollection < string > {
StringDictionary _internal ;
KeyOrValue _keyOrValue ;
public ICollectionToGenericCollectionAdapter ( StringDictionary source , KeyOrValue keyOrValue ) {
if ( source = = null ) throw new ArgumentNullException ( "source" ) ;
_internal = source ;
_keyOrValue = keyOrValue ;
}
public void Add ( string item ) {
ThrowNotSupportedException ( ) ;
}
public void Clear ( ) {
ThrowNotSupportedException ( ) ;
}
public void ThrowNotSupportedException ( ) {
if ( _keyOrValue = = KeyOrValue . Key ) {
throw new NotSupportedException ( SR . GetString ( SR . NotSupported_KeyCollectionSet ) ) ; //Same as KeyCollection/ValueCollection
}
throw new NotSupportedException ( SR . GetString ( SR . NotSupported_ValueCollectionSet ) ) ; //Same as KeyCollection/ValueCollection
}
public bool Contains ( string item ) {
// The underlying backing store for the StringDictionary is a HashTable so we
// want to delegate Contains to respective ContainsKey/ContainsValue functionality
// depending upon whether we are using Keys or Value collections.
if ( _keyOrValue = = KeyOrValue . Key ) {
return _internal . ContainsKey ( item ) ;
}
return _internal . ContainsValue ( item ) ;
}
public void CopyTo ( string [ ] array , int arrayIndex ) {
var collection = GetUnderlyingCollection ( ) ;
collection . CopyTo ( array , arrayIndex ) ;
}
public int Count {
get {
return _internal . Count ; // hashtable count is same as key/value count.
}
}
public bool IsReadOnly {
get {
return true ; //Same as KeyCollection/ValueCollection
}
}
public bool Remove ( string item ) {
ThrowNotSupportedException ( ) ;
return false ;
}
private ICollection GetUnderlyingCollection ( ) {
if ( _keyOrValue = = KeyOrValue . Key ) {
return ( ICollection ) _internal . Keys ;
}
return ( ICollection ) _internal . Values ;
}
public IEnumerator < string > GetEnumerator ( ) {
ICollection collection = GetUnderlyingCollection ( ) ;
// This is doing the same as collection.Cast<string>()
foreach ( string entry in collection ) {
yield return entry ;
}
}
IEnumerator IEnumerable . GetEnumerator ( ) {
return GetUnderlyingCollection ( ) . GetEnumerator ( ) ;
}
}
#endregion
}
#endregion
2018-05-10 08:37:03 +00:00
#if ! COREFX
2016-08-03 10:59:49 +00:00
}
2018-05-10 08:37:03 +00:00
#endif
2016-08-03 10:59:49 +00:00
}