using System;
using System.Collections.Generic;
using System.Reflection;
using System.Globalization;
using Yap.Data.Client.Properties;
namespace Yap.Data.Client
{
/// <summary>
/// Simplifies access to properties of an entity. Used by entity commands to get/set entity property values.
/// </summary>
public class Accessor
{
private static readonly Object _Sync = new Object();
private static readonly Dictionary<Type, Accessor> _Instances
= new Dictionary<Type, Accessor>();
/// <summary>
/// Returns existing or initializes new <see cref="Yap.Data.Client.Accessor"/> instance for specified entity type.
/// </summary>
/// <param name="entityType">The type of entity.</param>
/// <returns>The accessor.</returns>
/// <exception cref="System.ArgumentNullException">entityType is null.</exception>
public static Accessor GetInstance(Type entityType)
{
lock (_Sync)
{
if (entityType == null)
{
throw new ArgumentNullException("entityType");
}
Accessor accessor;
if (!_Instances.TryGetValue(entityType, out accessor))
{
accessor = new Accessor(entityType);
_Instances.Add(entityType, accessor);
}
return accessor;
}
}
private readonly Type _Type;
private readonly Dictionary<String, PropertyInfo> _Properties =
new Dictionary<String, PropertyInfo>();
private Accessor(Type entityType)
{
_Type = entityType;
PropertyInfo[] properties = _Type.GetProperties(
BindingFlags.Instance |
BindingFlags.Public);
foreach (PropertyInfo property in properties)
{
if (!_Properties.ContainsKey(property.Name))
{
_Properties.Add(
property.Name, property);
}
}
}
/// <summary>
/// Gets collection of entity properties.
/// </summary>
public ICollection<PropertyInfo> Properties
{
get
{
return _Properties.Values;
}
}
/// <summary>
/// Sets value into entity property by path specified.
/// </summary>
/// <param name="entity">Property owner.</param>
/// <param name="propertyPath">The property path to set value to.</param>
/// <param name="value">The value to set.</param>
/// <exception cref="System.InvalidOperationException">Entity property was not found by specified path.</exception>
public void SetPropertyValue(Object entity, String propertyPath, Object value)
{
Int32 indexOfDelimiter = propertyPath.IndexOf('.');
String propertyName = indexOfDelimiter > 0 ? propertyPath.Substring(0, indexOfDelimiter) : propertyPath;
PropertyInfo property;
if (!TryGetProperty(propertyName, out property))
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
Resources.PropertyWasNotFoundInTypeException,
propertyName,
_Type.Name));
}
if (indexOfDelimiter == -1)
{
property.SetValue(
entity, value, null);
}
else
{
Type propertyType = property.PropertyType;
Object propertyValue = GetPropertyValue(entity, propertyName) ?? Activator.CreateInstance(propertyType);
Accessor accessor = GetInstance(propertyType);
accessor.SetPropertyValue(
propertyValue,
propertyPath.Substring(
indexOfDelimiter + 1,
propertyPath.Length - propertyName.Length - 1),
value);
SetPropertyValue(entity, propertyName, propertyValue);
}
}
/// <summary>
/// Gets entity property value.
/// </summary>
/// <param name="entity">Property owner.</param>
/// <param name="propertyPath">The property path to get value from.</param>
/// <returns>The value of entity property.</returns>
/// <exception cref="System.InvalidOperationException">Entity property was not found by specified path.</exception>
public Object GetPropertyValue(Object entity, String propertyPath)
{
Int32 indexOfDelimiter = propertyPath.IndexOf('.');
String propertyName = indexOfDelimiter > 0 ? propertyPath.Substring(0, indexOfDelimiter) : propertyPath;
PropertyInfo property;
if (!TryGetProperty(propertyName, out property))
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
Resources.PropertyWasNotFoundInTypeException,
propertyName,
_Type.Name));
}
if (indexOfDelimiter == -1)
{
return property.GetValue(
entity, null);
}
Object propertyValue = GetPropertyValue(entity, propertyName);
if (propertyValue == null)
{
return null;
}
Accessor accessor = GetInstance(property.PropertyType);
return accessor.GetPropertyValue(
propertyValue,
propertyPath.Substring(
indexOfDelimiter + 1,
propertyPath.Length - propertyName.Length - 1));
}
/// <summary>
/// Gets entity property <see cref="System.Reflection.PropertyInfo"/> by property path.
/// </summary>
/// <param name="propertyPath">The property path to get <see cref="System.Reflection.PropertyInfo"/> from.</param>
/// <returns>The <see cref="System.Reflection.PropertyInfo"/> of entity property.</returns>
/// <exception cref="System.InvalidOperationException">Entity property was not found by specified path.</exception>
public PropertyInfo GetProperty(String propertyPath)
{
PropertyInfo property;
if (!TryGetProperty(propertyPath, out property))
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
Resources.PropertyWasNotFoundInTypeException,
propertyPath,
_Type.Name));
}
return property;
}
/// <summary>
/// Gets entity property <see cref="System.Reflection.PropertyInfo"/> by property path.
/// </summary>
/// <param name="propertyPath">The property path to get <see cref="System.Reflection.PropertyInfo"/> from.</param>
/// <param name="property">When this method returns, contains the <see cref="System.Reflection.PropertyInfo"/> of entity property, if property is found; otherwise, null.</param>
/// <returns>true if entity contains an property with specified path; otherwise, false.</returns>
public Boolean TryGetProperty(String propertyPath, out PropertyInfo property)
{
Int32 indexOfDelimiter = propertyPath.IndexOf('.');
String propertyName = indexOfDelimiter > 0 ? propertyPath.Substring(0, indexOfDelimiter) : propertyPath;
if (_Properties.TryGetValue(propertyName, out property))
{
if (indexOfDelimiter == -1)
{
return property != null;
}
Accessor accessor = GetInstance(property.PropertyType);
return accessor.TryGetProperty(
propertyPath.Substring(
indexOfDelimiter + 1,
propertyPath.Length - propertyName.Length - 1),
out property);
}
return false;
}
}
}