// 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); } } } } }