|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
using System.ServiceModel.Security;
namespace System.ServiceModel.Channels
{
public sealed class MessageProperties : IDictionary<string, object>, IDisposable
{
private Property[] _properties;
private int _propertyCount;
private MessageEncoder _encoder;
private Uri _via;
private object _allowOutputBatching;
private SecurityMessageProperty _security;
private bool _disposed;
private const int InitialPropertyCount = 2;
private const int MaxRecycledArrayLength = 8;
private const string ViaKey = "Via";
private const string AllowOutputBatchingKey = "AllowOutputBatching";
private const string SecurityKey = "Security";
private const string EncoderKey = "Encoder";
private const int NotFoundIndex = -1;
private const int ViaIndex = -2;
private const int AllowOutputBatchingIndex = -3;
private const int SecurityIndex = -4;
private const int EncoderIndex = -5;
private static object s_trueBool = true;
private static object s_falseBool = false;
public MessageProperties()
{
}
public MessageProperties(MessageProperties properties)
{
CopyProperties(properties);
}
internal MessageProperties(KeyValuePair<string, object>[] array)
{
if (array == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("array"));
CopyProperties(array);
}
private void ThrowDisposed()
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(string.Empty, string.Format(SRServiceModel.ObjectDisposed, this.GetType().ToString())));
}
public object this[string name]
{
get
{
if (_disposed)
ThrowDisposed();
object value;
if (!TryGetValue(name, out value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(string.Format(SRServiceModel.MessagePropertyNotFound, name)));
}
return value;
}
set
{
if (_disposed)
ThrowDisposed();
UpdateProperty(name, value, false);
}
}
internal bool CanRecycle
{
get
{
return _properties == null || _properties.Length <= MaxRecycledArrayLength;
}
}
public int Count
{
get
{
if (_disposed)
ThrowDisposed();
return _propertyCount;
}
}
public MessageEncoder Encoder
{
get
{
if (_disposed)
ThrowDisposed();
return _encoder;
}
set
{
if (_disposed)
ThrowDisposed();
AdjustPropertyCount((object)_encoder == null, (object)value == null);
_encoder = value;
}
}
public bool AllowOutputBatching
{
get
{
if (_disposed)
ThrowDisposed();
return (object)_allowOutputBatching == s_trueBool;
}
set
{
if (_disposed)
ThrowDisposed();
AdjustPropertyCount((object)_allowOutputBatching == null, false);
if (value)
{
_allowOutputBatching = s_trueBool;
}
else
{
_allowOutputBatching = s_falseBool;
}
}
}
public bool IsFixedSize
{
get
{
if (_disposed)
ThrowDisposed();
return false;
}
}
public bool IsReadOnly
{
get
{
if (_disposed)
ThrowDisposed();
return false;
}
}
public ICollection<string> Keys
{
get
{
if (_disposed)
ThrowDisposed();
List<string> keys = new List<string>();
if ((object)_via != null)
{
keys.Add(ViaKey);
}
if ((object)_allowOutputBatching != null)
{
keys.Add(AllowOutputBatchingKey);
}
if ((object)_encoder != null)
{
keys.Add(EncoderKey);
}
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
string propertyName = _properties[i].Name;
if (propertyName == null)
{
break;
}
keys.Add(propertyName);
}
}
return keys;
}
}
public SecurityMessageProperty Security
{
get
{
if (_disposed)
ThrowDisposed();
return _security;
}
set
{
if (_disposed)
ThrowDisposed();
AdjustPropertyCount((object)_security == null, (object)value == null);
_security = value;
}
}
public ICollection<object> Values
{
get
{
if (_disposed)
ThrowDisposed();
List<object> values = new List<object>();
if ((object)_via != null)
{
values.Add(_via);
}
if ((object)_allowOutputBatching != null)
{
values.Add(_allowOutputBatching);
}
if ((object)_security != null)
{
values.Add(_security);
}
if ((object)_encoder != null)
{
values.Add(_encoder);
}
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
if (_properties[i].Name == null)
{
break;
}
values.Add(_properties[i].Value);
}
}
return values;
}
}
public Uri Via
{
get
{
if (_disposed)
ThrowDisposed();
return _via;
}
set
{
if (_disposed)
ThrowDisposed();
AdjustPropertyCount((object)_via == null, (object)value == null);
_via = value;
}
}
public void Add(string name, object property)
{
if (_disposed)
ThrowDisposed();
if (property == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("property"));
UpdateProperty(name, property, true);
}
private void AdjustPropertyCount(bool oldValueIsNull, bool newValueIsNull)
{
if (newValueIsNull)
{
if (!oldValueIsNull)
{
_propertyCount--;
}
}
else
{
if (oldValueIsNull)
{
_propertyCount++;
}
}
}
public void Clear()
{
if (_disposed)
ThrowDisposed();
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
if (_properties[i].Name == null)
{
break;
}
_properties[i] = new Property();
}
}
_via = null;
_allowOutputBatching = null;
_security = null;
_encoder = null;
_propertyCount = 0;
}
public void CopyProperties(MessageProperties properties)
{
// CopyProperties behavior should be equivalent to the behavior
// of MergeProperties except that Merge supports property values that
// implement the IMergeEnabledMessageProperty. Any changes to CopyProperties
// should be reflected in MergeProperties as well.
if (properties == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties");
}
if (_disposed)
{
ThrowDisposed();
}
if (properties._properties != null)
{
for (int i = 0; i < properties._properties.Length; i++)
{
if (properties._properties[i].Name == null)
{
break;
}
Property property = properties._properties[i];
// this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here
this[property.Name] = property.Value;
}
}
this.Via = properties.Via;
this.AllowOutputBatching = properties.AllowOutputBatching;
this.Security = (properties.Security != null) ? (SecurityMessageProperty)properties.Security.CreateCopy() : null;
this.Encoder = properties.Encoder;
}
internal void MergeProperties(MessageProperties properties)
{
// MergeProperties behavior should be equivalent to the behavior
// of CopyProperties except that Merge supports property values that
// implement the IMergeEnabledMessageProperty. Any changes to CopyProperties
// should be reflected in MergeProperties as well.
if (properties == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties");
}
if (_disposed)
{
ThrowDisposed();
}
if (properties._properties != null)
{
for (int i = 0; i < properties._properties.Length; i++)
{
if (properties._properties[i].Name == null)
{
break;
}
Property property = properties._properties[i];
IMergeEnabledMessageProperty currentValue;
if (!this.TryGetValue(property.Name, out currentValue) ||
!currentValue.TryMergeWithProperty(property.Value))
{
// Merge wasn't possible so copy
// this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here
this[property.Name] = property.Value;
}
}
}
this.Via = properties.Via;
this.AllowOutputBatching = properties.AllowOutputBatching;
this.Security = (properties.Security != null) ? (SecurityMessageProperty)properties.Security.CreateCopy() : null;
this.Encoder = properties.Encoder;
}
internal void CopyProperties(KeyValuePair<string, object>[] array)
{
if (_disposed)
{
ThrowDisposed();
}
for (int i = 0; i < array.Length; i++)
{
KeyValuePair<string, object> property = array[i];
// this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here
this[property.Key] = property.Value;
}
}
public bool ContainsKey(string name)
{
if (_disposed)
ThrowDisposed();
if (name == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("name"));
int index = FindProperty(name);
switch (index)
{
case ViaIndex:
return (object)_via != null;
case AllowOutputBatchingIndex:
return (object)_allowOutputBatching != null;
case SecurityIndex:
return (object)_security != null;
case EncoderIndex:
return (object)_encoder != null;
case NotFoundIndex:
return false;
default:
return true;
}
}
private object CreateCopyOfPropertyValue(object propertyValue)
{
IMessageProperty messageProperty = propertyValue as IMessageProperty;
if (messageProperty == null)
return propertyValue;
object copy = messageProperty.CreateCopy();
if (copy == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRServiceModel.MessagePropertyReturnedNullCopy));
return copy;
}
public void Dispose()
{
if (_disposed)
return;
_disposed = true;
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
if (_properties[i].Name == null)
{
break;
}
_properties[i].Dispose();
}
}
if (_security != null)
{
_security.Dispose();
}
}
private int FindProperty(string name)
{
if (name == ViaKey)
return ViaIndex;
else if (name == AllowOutputBatchingKey)
return AllowOutputBatchingIndex;
else if (name == EncoderKey)
return EncoderIndex;
else if (name == SecurityKey)
return SecurityIndex;
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
string propertyName = _properties[i].Name;
if (propertyName == null)
{
break;
}
if (propertyName == name)
{
return i;
}
}
}
return NotFoundIndex;
}
internal void Recycle()
{
_disposed = false;
Clear();
}
public bool Remove(string name)
{
if (_disposed)
ThrowDisposed();
int originalPropertyCount = _propertyCount;
UpdateProperty(name, null, false);
return originalPropertyCount != _propertyCount;
}
public bool TryGetValue(string name, out object value)
{
if (_disposed)
ThrowDisposed();
if (name == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("name"));
int index = FindProperty(name);
switch (index)
{
case ViaIndex:
value = _via;
break;
case AllowOutputBatchingIndex:
value = _allowOutputBatching;
break;
case SecurityIndex:
value = _security;
break;
case EncoderIndex:
value = _encoder;
break;
case NotFoundIndex:
value = null;
break;
default:
value = _properties[index].Value;
break;
}
return value != null;
}
internal bool TryGetValue<TProperty>(string name, out TProperty property)
{
object o;
if (this.TryGetValue(name, out o))
{
property = (TProperty)o;
return true;
}
else
{
property = default(TProperty);
return false;
}
}
internal TProperty GetValue<TProperty>(string name) where TProperty : class
{
return this.GetValue<TProperty>(name, false);
}
internal TProperty GetValue<TProperty>(string name, bool ensureTypeMatch) where TProperty : class
{
object obj;
if (!this.TryGetValue(name, out obj))
{
return null;
}
return ensureTypeMatch ? (TProperty)obj : obj as TProperty;
}
private void UpdateProperty(string name, object value, bool mustNotExist)
{
if (name == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("name"));
int index = FindProperty(name);
if (index != NotFoundIndex)
{
if (mustNotExist)
{
bool exists;
switch (index)
{
case ViaIndex:
exists = (object)_via != null;
break;
case AllowOutputBatchingIndex:
exists = (object)_allowOutputBatching != null;
break;
case SecurityIndex:
exists = (object)_security != null;
break;
case EncoderIndex:
exists = (object)_encoder != null;
break;
default:
exists = true;
break;
}
if (exists)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(string.Format(SRServiceModel.DuplicateMessageProperty, name)));
}
}
if (index >= 0)
{
if (value == null)
{
_properties[index].Dispose();
int shiftIndex;
for (shiftIndex = index + 1; shiftIndex < _properties.Length; shiftIndex++)
{
if (_properties[shiftIndex].Name == null)
{
break;
}
_properties[shiftIndex - 1] = _properties[shiftIndex];
}
_properties[shiftIndex - 1] = new Property();
_propertyCount--;
}
else
{
_properties[index].Value = CreateCopyOfPropertyValue(value);
}
}
else
{
switch (index)
{
case ViaIndex:
Via = (Uri)value;
break;
case AllowOutputBatchingIndex:
AllowOutputBatching = (bool)value;
break;
case SecurityIndex:
if (Security != null)
Security.Dispose();
Security = (SecurityMessageProperty)CreateCopyOfPropertyValue(value);
break;
case EncoderIndex:
Encoder = (MessageEncoder)value;
break;
default:
Fx.Assert("");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
}
}
}
else if (value != null)
{
int newIndex;
if (_properties == null)
{
_properties = new Property[InitialPropertyCount];
newIndex = 0;
}
else
{
for (newIndex = 0; newIndex < _properties.Length; newIndex++)
{
if (_properties[newIndex].Name == null)
{
break;
}
}
if (newIndex == _properties.Length)
{
Property[] newProperties = new Property[_properties.Length * 2];
Array.Copy(_properties, newProperties, _properties.Length);
_properties = newProperties;
}
}
object newValue = CreateCopyOfPropertyValue(value);
_properties[newIndex] = new Property(name, newValue);
_propertyCount++;
}
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int index)
{
if (_disposed)
ThrowDisposed();
if (array == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("array"));
if (array.Length < _propertyCount)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRServiceModel.MessagePropertiesArraySize0));
if (index < 0 || index > array.Length - _propertyCount)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index", index,
string.Format(SRServiceModel.ValueMustBeInRange, 0, array.Length - _propertyCount)));
if (_via != null)
array[index++] = new KeyValuePair<string, object>(ViaKey, _via);
if (_allowOutputBatching != null)
array[index++] = new KeyValuePair<string, object>(AllowOutputBatchingKey, _allowOutputBatching);
if (_security != null)
array[index++] = new KeyValuePair<string, object>(SecurityKey, _security.CreateCopy());
if (_encoder != null)
array[index++] = new KeyValuePair<string, object>(EncoderKey, _encoder);
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
string propertyName = _properties[i].Name;
if (propertyName == null)
{
break;
}
array[index++] = new KeyValuePair<string, object>(propertyName, CreateCopyOfPropertyValue(_properties[i].Value));
}
}
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> pair)
{
if (_disposed)
ThrowDisposed();
if (pair.Value == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pair.Value"));
UpdateProperty(pair.Key, pair.Value, true);
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> pair)
{
if (_disposed)
ThrowDisposed();
if (pair.Value == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pair.Value"));
if (pair.Key == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pair.Key"));
object value;
if (!TryGetValue(pair.Key, out value))
{
return false;
}
return value.Equals(pair.Value);
}
IEnumerator IEnumerable.GetEnumerator()
{
if (_disposed)
ThrowDisposed();
return ((IEnumerable<KeyValuePair<string, object>>)this).GetEnumerator();
}
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
if (_disposed)
ThrowDisposed();
List<KeyValuePair<string, object>> pairs = new List<KeyValuePair<string, object>>(_propertyCount);
if (_via != null)
pairs.Add(new KeyValuePair<string, object>(ViaKey, _via));
if (_allowOutputBatching != null)
pairs.Add(new KeyValuePair<string, object>(AllowOutputBatchingKey, _allowOutputBatching));
if (_security != null)
pairs.Add(new KeyValuePair<string, object>(SecurityKey, _security));
if (_encoder != null)
pairs.Add(new KeyValuePair<string, object>(EncoderKey, _encoder));
if (_properties != null)
{
for (int i = 0; i < _properties.Length; i++)
{
string propertyName = _properties[i].Name;
if (propertyName == null)
{
break;
}
pairs.Add(new KeyValuePair<string, object>(propertyName, _properties[i].Value));
}
}
return pairs.GetEnumerator();
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> pair)
{
if (_disposed)
ThrowDisposed();
if (pair.Value == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pair.Value"));
if (pair.Key == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pair.Key"));
object value;
if (!TryGetValue(pair.Key, out value))
{
return false;
}
if (!value.Equals(pair.Value))
{
return false;
}
Remove(pair.Key);
return true;
}
internal struct Property : IDisposable
{
private string _name;
private object _value;
public Property(string name, object value)
{
_name = name;
_value = value;
}
public string Name
{
get { return _name; }
}
public object Value
{
get { return _value; }
set { _value = value; }
}
public void Dispose()
{
IDisposable disposable = _value as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
}
}
|