12 changed files with 355 additions and 197 deletions
@ -0,0 +1,244 @@ |
|||
// 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; |
|||
|
|||
namespace Perspex |
|||
{ |
|||
/// <summary>
|
|||
/// Tracks registered <see cref="PerspexProperty"/> instances.
|
|||
/// </summary>
|
|||
public class PerspexPropertyRegistry |
|||
{ |
|||
/// <summary>
|
|||
/// The registered properties by type.
|
|||
/// </summary>
|
|||
private readonly Dictionary<Type, List<PerspexProperty>> _registered = |
|||
new Dictionary<Type, List<PerspexProperty>>(); |
|||
|
|||
/// <summary>
|
|||
/// The registered attached properties by owner type.
|
|||
/// </summary>
|
|||
private readonly Dictionary<Type, List<PerspexProperty>> _attached = |
|||
new Dictionary<Type, List<PerspexProperty>>(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="PerspexPropertyRegistry"/> instance
|
|||
/// </summary>
|
|||
public static PerspexPropertyRegistry Instance { get; } |
|||
= new PerspexPropertyRegistry(); |
|||
|
|||
/// <summary>
|
|||
/// Gets all attached <see cref="PerspexProperty"/>s registered by an owner.
|
|||
/// </summary>
|
|||
/// <param name="ownerType">The owner type.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetAttached(Type ownerType) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_attached.TryGetValue(ownerType, out list)) |
|||
{ |
|||
return list; |
|||
} |
|||
|
|||
return Enumerable.Empty<PerspexProperty>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all <see cref="PerspexProperty"/>s registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetRegistered(Type type) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
|
|||
var i = type.GetTypeInfo(); |
|||
|
|||
while (type != null) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_registered.TryGetValue(type, out list)) |
|||
{ |
|||
foreach (PerspexProperty p in list) |
|||
{ |
|||
yield return p; |
|||
} |
|||
} |
|||
|
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all <see cref="PerspexProperty"/>s registered on a object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetRegistered(PerspexObject o) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(o != null); |
|||
|
|||
return GetRegistered(o.GetType()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds <see cref="PerspexProperty"/> registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>The registered property or null if not found.</returns>
|
|||
/// <remarks>
|
|||
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
|
|||
/// different object but is equal according to <see cref="object.Equals(object)"/>.
|
|||
/// </remarks>
|
|||
public PerspexProperty FindRegistered(Type type, PerspexProperty property) |
|||
{ |
|||
while (type != null) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_registered.TryGetValue(type, out list)) |
|||
{ |
|||
var index = list.IndexOf(property); |
|||
|
|||
if (index != -1) |
|||
{ |
|||
return list[index]; |
|||
} |
|||
} |
|||
|
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds <see cref="PerspexProperty"/> registered on an object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>The registered property or null if not found.</returns>
|
|||
/// <remarks>
|
|||
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
|
|||
/// different object but is equal according to <see cref="object.Equals(object)"/>.
|
|||
/// </remarks>
|
|||
public PerspexProperty FindRegistered(object o, PerspexProperty property) |
|||
{ |
|||
return FindRegistered(o.GetType(), property); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds a registered property on a type by name.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="name">
|
|||
/// The property name. If an attached property it should be in the form
|
|||
/// "OwnerType.PropertyName".
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The registered property or null if no matching property found.
|
|||
/// </returns>
|
|||
public PerspexProperty FindRegistered(Type type, string name) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
Contract.Requires<ArgumentNullException>(name != null); |
|||
|
|||
var parts = name.Split('.'); |
|||
|
|||
if (parts.Length < 1 || parts.Length > 2) |
|||
{ |
|||
throw new ArgumentException("Invalid property name."); |
|||
} |
|||
|
|||
if (parts.Length == 1) |
|||
{ |
|||
var result = GetRegistered(type) |
|||
.FirstOrDefault(x => !x.IsAttached && x.Name == parts[0]); |
|||
|
|||
if (result != null) |
|||
{ |
|||
return result; |
|||
} |
|||
|
|||
// A type can .AddOwner an attached property.
|
|||
return GetRegistered(type) |
|||
.FirstOrDefault(x => x.Name == parts[0]); |
|||
} |
|||
else |
|||
{ |
|||
return GetRegistered(type) |
|||
.FirstOrDefault(x => x.IsAttached && x.OwnerType.Name == parts[0] && x.Name == parts[1]); |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// Checks whether a <see cref="PerspexProperty"/> is registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>True if the property is registered, otherwise false.</returns>
|
|||
public bool IsRegistered(Type type, PerspexProperty property) |
|||
{ |
|||
return FindRegistered(type, property) != null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks whether a <see cref="PerspexProperty"/> is registered on a object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>True if the property is registered, otherwise false.</returns>
|
|||
public bool IsRegistered(object o, PerspexProperty property) |
|||
{ |
|||
return IsRegistered(o.GetType(), property); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers a <see cref="PerspexProperty"/> on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <remarks>
|
|||
/// You won't usually want to call this method directly, instead use the
|
|||
/// <see cref="PerspexProperty.Register"/> method.
|
|||
/// </remarks>
|
|||
public void Register(Type type, PerspexProperty property) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
Contract.Requires<ArgumentNullException>(property != null); |
|||
|
|||
List<PerspexProperty> list; |
|||
|
|||
if (!_registered.TryGetValue(type, out list)) |
|||
{ |
|||
list = new List<PerspexProperty>(); |
|||
_registered.Add(type, list); |
|||
} |
|||
|
|||
if (!list.Contains(property)) |
|||
{ |
|||
list.Add(property); |
|||
} |
|||
|
|||
if (property.IsAttached) |
|||
{ |
|||
if (!_attached.TryGetValue(property.OwnerType, out list)) |
|||
{ |
|||
list = new List<PerspexProperty>(); |
|||
_attached.Add(property.OwnerType, list); |
|||
} |
|||
|
|||
if (!list.Contains(property)) |
|||
{ |
|||
list.Add(property); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// 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.Linq; |
|||
using System.Reactive.Linq; |
|||
using Xunit; |
|||
|
|||
namespace Perspex.Base.UnitTests |
|||
{ |
|||
public class PerspexPropertyRegistryTests |
|||
{ |
|||
public PerspexPropertyRegistryTests() |
|||
{ |
|||
// Ensure properties are registered.
|
|||
PerspexProperty p; |
|||
p = Class1.FooProperty; |
|||
p = Class2.BarProperty; |
|||
p = AttachedOwner.AttachedProperty; |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetRegistered_Returns_Registered_Properties() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class1)) |
|||
.Select(x => x.Name) |
|||
.ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetRegistered_Returns_Registered_Properties_For_Base_Types() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class2)) |
|||
.Select(x => x.Name) |
|||
.ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetAttached_Returns_Registered_Properties_For_Base_Types() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetAttached(typeof(AttachedOwner)).Select(x => x.Name).ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Attached" }, names); |
|||
} |
|||
|
|||
private class Class1 : PerspexObject |
|||
{ |
|||
public static readonly PerspexProperty<string> FooProperty = |
|||
PerspexProperty.Register<Class1, string>("Foo"); |
|||
|
|||
public static readonly PerspexProperty<string> BazProperty = |
|||
PerspexProperty.Register<Class1, string>("Baz"); |
|||
|
|||
public static readonly PerspexProperty<int> QuxProperty = |
|||
PerspexProperty.Register<Class1, int>("Qux"); |
|||
} |
|||
|
|||
private class Class2 : Class1 |
|||
{ |
|||
public static readonly PerspexProperty<string> BarProperty = |
|||
PerspexProperty.Register<Class2, string>("Bar"); |
|||
|
|||
public static readonly PerspexProperty<double> FlobProperty = |
|||
PerspexProperty.Register<Class2, double>("Flob"); |
|||
|
|||
public static readonly PerspexProperty<double?> FredProperty = |
|||
PerspexProperty.Register<Class2, double?>("Fred"); |
|||
} |
|||
|
|||
private class AttachedOwner |
|||
{ |
|||
public static readonly PerspexProperty<string> AttachedProperty = |
|||
PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue