// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Perspex
{
///
/// Tracks registered instances.
///
public class PerspexPropertyRegistry
{
///
/// The registered properties by type.
///
private readonly Dictionary> _registered =
new Dictionary>();
///
/// The registered attached properties by owner type.
///
private readonly Dictionary> _attached =
new Dictionary>();
///
/// Gets the instance
///
public static PerspexPropertyRegistry Instance { get; }
= new PerspexPropertyRegistry();
///
/// Gets all attached s registered by an owner.
///
/// The owner type.
/// A collection of definitions.
public IEnumerable GetAttached(Type ownerType)
{
Dictionary inner;
if (_attached.TryGetValue(ownerType, out inner))
{
return inner.Values;
}
return Enumerable.Empty();
}
///
/// Gets all s registered on a type.
///
/// The type.
/// A collection of definitions.
public IEnumerable GetRegistered(Type type)
{
Contract.Requires(type != null);
var i = type.GetTypeInfo();
while (type != null)
{
// Ensure the type's static ctor has been run.
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
Dictionary inner;
if (_registered.TryGetValue(type, out inner))
{
foreach (var p in inner)
{
yield return p.Value;
}
}
type = type.GetTypeInfo().BaseType;
}
}
///
/// Gets all s registered on a object.
///
/// The object.
/// A collection of definitions.
public IEnumerable GetRegistered(PerspexObject o)
{
Contract.Requires(o != null);
return GetRegistered(o.GetType());
}
///
/// Finds a registered on a type.
///
/// The type.
/// The property.
/// The registered property or null if not found.
///
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
/// different object but is equal according to .
///
public PerspexProperty FindRegistered(Type type, PerspexProperty property)
{
while (type != null)
{
Dictionary inner;
if (_registered.TryGetValue(type, out inner))
{
PerspexProperty result;
if (inner.TryGetValue(property.Id, out result))
{
return result;
}
}
type = type.GetTypeInfo().BaseType;
}
return null;
}
///
/// Finds registered on an object.
///
/// The object.
/// The property.
/// The registered property or null if not found.
///
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
/// different object but is equal according to .
///
public PerspexProperty FindRegistered(object o, PerspexProperty property)
{
return FindRegistered(o.GetType(), property);
}
///
/// Finds a registered property on a type by name.
///
/// The type.
///
/// The property name. If an attached property it should be in the form
/// "OwnerType.PropertyName".
///
///
/// The registered property or null if no matching property found.
///
public PerspexProperty FindRegistered(Type type, string name)
{
Contract.Requires(type != null);
Contract.Requires(name != null);
var parts = name.Split('.');
var types = GetImplementedTypes(type).ToList();
if (parts.Length < 1 || parts.Length > 2)
{
throw new ArgumentException("Invalid property name.");
}
string propertyName;
var results = GetRegistered(type);
if (parts.Length == 1)
{
propertyName = parts[0];
results = results.Where(x => !x.IsAttached || types.Contains(x.OwnerType.Name));
}
else
{
if (!types.Contains(parts[0]))
{
results = results.Where(x => x.OwnerType.Name == parts[0]);
}
propertyName = parts[1];
}
return results.FirstOrDefault(x => x.Name == propertyName);
}
///
/// Finds a registered property on an object by name.
///
/// The object.
///
/// The property name. If an attached property it should be in the form
/// "OwnerType.PropertyName".
///
///
/// The registered property or null if no matching property found.
///
public PerspexProperty FindRegistered(PerspexObject o, string name)
{
return FindRegistered(o.GetType(), name);
}
///
/// Returns a type and all its base types.
///
/// The type.
/// The type and all its base types.
private IEnumerable GetImplementedTypes(Type type)
{
while (type != null)
{
yield return type.Name;
type = type.GetTypeInfo().BaseType;
}
}
///
/// Checks whether a is registered on a type.
///
/// The type.
/// The property.
/// True if the property is registered, otherwise false.
public bool IsRegistered(Type type, PerspexProperty property)
{
return FindRegistered(type, property) != null;
}
///
/// Checks whether a is registered on a object.
///
/// The object.
/// The property.
/// True if the property is registered, otherwise false.
public bool IsRegistered(object o, PerspexProperty property)
{
return IsRegistered(o.GetType(), property);
}
///
/// Registers a on a type.
///
/// The type.
/// The property.
///
/// You won't usually want to call this method directly, instead use the
/// method.
///
public void Register(Type type, PerspexProperty property)
{
Contract.Requires(type != null);
Contract.Requires(property != null);
Dictionary inner;
if (!_registered.TryGetValue(type, out inner))
{
inner = new Dictionary();
_registered.Add(type, inner);
}
if (!inner.ContainsKey(property.Id))
{
inner.Add(property.Id, property);
}
if (property.IsAttached)
{
if (!_attached.TryGetValue(property.OwnerType, out inner))
{
inner = new Dictionary();
_attached.Add(property.OwnerType, inner);
}
if (!inner.ContainsKey(property.Id))
{
inner.Add(property.Id, property);
}
}
}
}
}