using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
namespace System.ComponentModel.DataAnnotations
{
///
/// Attribute to provide a hint to the presentation layer about what control it should use
///
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "ControlParameters is exposed, just with a different type")]
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class UIHintAttribute : Attribute
{
private UIHintImplementation _implementation;
///
/// Gets the name of the control that is most appropriate for this associated property or field
///
public string UIHint
{
get
{
return this._implementation.UIHint;
}
}
///
/// Gets the name of the presentation layer that supports the control type in
///
public string PresentationLayer
{
get
{
return this._implementation.PresentationLayer;
}
}
///
/// Gets the name-value pairs used as parameters to the control's constructor
///
/// is thrown if the current attribute is ill-formed.
public IDictionary ControlParameters
{
get
{
return this._implementation.ControlParameters;
}
}
///
/// Constructor that accepts the name of the control, without specifying which presentation layer to use
///
/// The name of the UI control.
public UIHintAttribute(string uiHint)
: this(uiHint, null, new object[0])
{
}
///
/// Constructor that accepts both the name of the control as well as the presentation layer
///
/// The name of the control to use
/// The name of the presentation layer that supports this control
public UIHintAttribute(string uiHint, string presentationLayer)
: this(uiHint, presentationLayer, new object[0])
{
}
///
/// Full constructor that accepts the name of the control, presentation layer, and optional parameters
/// to use when constructing the control
///
/// The name of the control
/// The presentation layer
/// The list of parameters for the control
public UIHintAttribute(string uiHint, string presentationLayer, params object[] controlParameters)
{
this._implementation = new UIHintImplementation(uiHint, presentationLayer, controlParameters);
}
public override int GetHashCode()
{
return this._implementation.GetHashCode();
}
public override bool Equals(object obj)
{
var otherAttribute = obj as UIHintAttribute;
if (otherAttribute == null)
{
return false;
}
return this._implementation.Equals(otherAttribute._implementation);
}
internal class UIHintImplementation
{
private IDictionary _controlParameters;
private object[] _inputControlParameters;
///
/// Gets the name of the control that is most appropriate for this associated property or field
///
public string UIHint { get; private set; }
///
/// Gets the name of the presentation layer that supports the control type in
///
public string PresentationLayer { get; private set; }
public IDictionary ControlParameters
{
get
{
if (this._controlParameters == null)
{
// Lazy load the dictionary. It's fine if this method executes multiple times in stress scenarios.
// If the method throws (indicating that the input params are invalid) this property will throw
// every time it's accessed.
this._controlParameters = this.BuildControlParametersDictionary();
}
return this._controlParameters;
}
}
public UIHintImplementation(string uiHint, string presentationLayer, params object[] controlParameters)
{
this.UIHint = uiHint;
this.PresentationLayer = presentationLayer;
if (controlParameters != null)
{
this._inputControlParameters = new object[controlParameters.Length];
Array.Copy(controlParameters, this._inputControlParameters, controlParameters.Length);
}
}
///
/// Returns the hash code for this UIHintAttribute.
///
/// A 32-bit signed integer hash code.
public override int GetHashCode()
{
var a = this.UIHint ?? String.Empty;
var b = this.PresentationLayer ?? String.Empty;
return a.GetHashCode() ^ b.GetHashCode();
}
///
/// Determines whether this instance of UIHintAttribute and a specified object,
/// which must also be a UIHintAttribute object, have the same value.
///
/// An System.Object.
/// true if obj is a UIHintAttribute and its value is the same as this instance; otherwise, false.
public override bool Equals(object obj)
{
// don't need to perform a type check on obj since this is an internal class
var otherImplementation = (UIHintImplementation)obj;
if (this.UIHint != otherImplementation.UIHint || this.PresentationLayer != otherImplementation.PresentationLayer)
{
return false;
}
IDictionary leftParams;
IDictionary rightParams;
try
{
leftParams = this.ControlParameters;
rightParams = otherImplementation.ControlParameters;
}
catch (InvalidOperationException)
{
return false;
}
Debug.Assert(leftParams != null, "leftParams shouldn't be null");
Debug.Assert(rightParams != null, "rightParams shouldn't be null");
if (leftParams.Count != rightParams.Count)
{
return false;
}
else
{
return leftParams.OrderBy(p => p.Key).SequenceEqual(rightParams.OrderBy(p => p.Key));
}
}
///
/// Validates the input control parameters and throws InvalidOperationException if they are not correct.
///
///
/// Dictionary of control parameters.
///
private IDictionary BuildControlParametersDictionary()
{
IDictionary controlParameters = new Dictionary();
object[] inputControlParameters = this._inputControlParameters;
if (inputControlParameters == null || inputControlParameters.Length == 0)
{
return controlParameters;
}
if (inputControlParameters.Length % 2 != 0)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.UIHintImplementation_NeedEvenNumberOfControlParameters));
}
for (int i = 0; i < inputControlParameters.Length; i += 2)
{
object key = inputControlParameters[i];
object value = inputControlParameters[i + 1];
if (key == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
DataAnnotationsResources.UIHintImplementation_ControlParameterKeyIsNull,
i));
}
string keyString = key as string;
if (keyString == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
DataAnnotationsResources.UIHintImplementation_ControlParameterKeyIsNotAString,
i,
inputControlParameters[i].ToString()));
}
if (controlParameters.ContainsKey(keyString))
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
DataAnnotationsResources.UIHintImplementation_ControlParameterKeyOccursMoreThanOnce,
i,
keyString));
}
controlParameters[keyString] = value;
}
return controlParameters;
}
}
}
}