/// <summary> /// Compares x and y for equality using property values and returns the difference. /// If a type implements IList the items of the list are compared /// </summary> /// <typeparam name="T">The type of <paramref name="x"/> and <paramref name="y"/></typeparam> /// <param name="x">The first instance</param> /// <param name="y">The second instance</param> /// <param name="settings">Specifies how equality is performed.</param> /// <returns>Diff.Empty if <paramref name="x"/> and <paramref name="y"/> are equal</returns> public static ValueDiff PropertyValues <T>(T x, T y, PropertiesSettings settings) { Ensure.NotNull(x, nameof(x)); Ensure.NotNull(y, nameof(y)); Ensure.NotNull(settings, nameof(settings)); EqualBy.Verify.CanEqualByMemberValues(x, y, settings, typeof(DiffBy).Name, nameof(PropertyValues)); return(TryCreateValueDiff(x, y, settings) ?? new EmptyDiff(x, y)); }
/// <summary> /// Check if the properties of <typeparamref name="T"/> can be copied. /// This method will throw an exception if copy cannot be performed for <typeparamref name="T"/> /// Read the exception message for detailed instructions about what is wrong. /// Use this to fail fast or in unit tests. /// </summary> /// <typeparam name="T">The type to get ignore properties for settings for</typeparam> /// <param name="referenceHandling"> /// If Structural is used property values for sub properties are copied for the entire graph. /// Activator.CreateInstance is used to new up references so a default constructor is required, can be private /// </param> /// <param name="bindingFlags">The binding flags to use when getting properties</param> public static void VerifyCanCopyPropertyValues <T>( ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); VerifyCanCopyPropertyValues <T>(settings); }
internal static void CanTrackType(Type type, PropertiesSettings settings, string className = null, string methodName = null) { var errors = GetOrCreateErrors(type, settings); if (errors != null) { Throw.IfHasErrors(errors, settings, className ?? typeof(Track).Name, methodName ?? nameof(Track.Changes)); } }
public ChangeTracker(INotifyPropertyChanged source, PropertiesSettings settings) { Ensure.NotNull(source, nameof(source)); Ensure.NotNull(settings, nameof(settings)); Track.Verify.CanTrackType(source.GetType(), settings); this.Settings = settings; this.node = ChangeTrackerNode.GetOrCreate(source, settings, true); this.node.Value.Changed += this.OnNodeChange; }
private static bool IsTrackablePair(object x, object y, PropertiesSettings settings) { if (IsNullOrMissing(x) || IsNullOrMissing(y)) { return(false); } return(!settings.IsImmutable(x.GetType()) && !settings.IsImmutable(y.GetType())); }
/// <summary> /// Copies property values from source to target. /// </summary> /// <typeparam name="T">The type to to copy</typeparam> /// <param name="source">The instance to copy property values from</param> /// <param name="target">The instance to copy property values to</param> /// <param name="settings">Contains configuration for how copy will be performed</param> public static void PropertyValues <T>(T source, T target, PropertiesSettings settings) where T : class { Ensure.NotNull(source, nameof(source)); Ensure.NotNull(target, nameof(target)); Ensure.SameType(source, target, nameof(source), nameof(target)); Verify.CanCopyRoot(typeof(T), settings); Sync(source, target, settings); }
/// <summary> /// Check if the properties of <typeparamref name="T"/> can be tracked. /// This method will throw an exception if copy cannot be performed for <typeparamref name="T"/> /// Read the exception message for detailed instructions about what is wrong. /// Use this to fail fast or in unit tests. /// </summary> /// <typeparam name="T">The type to get ignore properties for settings for</typeparam> /// <param name="referenceHandling"> /// If Structural is used property values for sub properties are copied for the entire graph. /// Activator.CreateInstance is sued to new up references so a default constructor is required, can be private /// </param> /// <param name="bindingFlags"> /// The binding flags to use when getting properties /// Default is BindingFlags.Instance | BindingFlags.Public /// </param> public static void VerifyCanTrackIsDirty <T>( ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) where T : class, INotifyPropertyChanged { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); VerifyCanTrackIsDirty <T>(settings); }
internal static void CanTrackValue(object value, PropertiesSettings settings) { var errors = GetOrCreateErrors(value.GetType(), settings); if (errors != null) { var typeErrors = new TypeErrors(null, errors); Throw.IfHasErrors(typeErrors, settings, typeof(Track).Name, nameof(Track.Changes)); } }
/// <summary> /// Synchronizes property values from source to target. /// </summary> /// <typeparam name="T">The type os <paramref name="source"/> and <paramref name="target"/></typeparam> /// <param name="source">The instance to copy property values from</param> /// <param name="target">The instance to copy property values to</param> /// <param name="settings">Contains configuration for how synchronization will be performed</param> /// <returns>A disposable that when disposed stops synchronizing</returns> public static IDisposable PropertyValues <T>(T source, T target, PropertiesSettings settings) where T : class, INotifyPropertyChanged { Ensure.NotNull(source, nameof(source)); Ensure.NotNull(target, nameof(target)); Ensure.NotSame(source, target, nameof(source), nameof(target)); Ensure.SameType(source, target); VerifyCanSynchronize(source.GetType(), settings, typeof(Synchronize).Name, nameof(PropertyValues)); return(TrackerCache.GetOrAdd(source, target, settings, pair => new Synchronizer(source, target, settings))); }
/// <summary> /// Creates a tracker that detects and notifies about changes of any property or subproperty of <paramref name="source"/> /// </summary> /// <param name="source">The item to track changes for.</param> /// <param name="referenceHandling"> /// - Structural tracks property values for the entire graph. /// - References tracks only one level. /// - Throw throws and exception if there are nested trackable types /// </param> /// <param name="bindingFlags"> /// The <see cref="BindingFlags"/> to use when getting properties to track /// </param> /// <returns>An <see cref="IChangeTracker"/> that signals on changes in <paramref name="source"/></returns> public static IChangeTracker Changes( INotifyPropertyChanged source, ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) { Ensure.NotNull(source, nameof(source)); var settings = PropertiesSettings.GetOrCreate(referenceHandling: referenceHandling, bindingFlags: bindingFlags); return(Changes(source, settings)); }
/// <summary> /// Creates a tracker that detects and notifies about differences for any property or subproperty of <paramref name="x"/> compared to <paramref name="y"/> /// </summary> /// <typeparam name="T"> /// The type of <paramref name="x"/> and <paramref name="y"/> /// </typeparam> /// <param name="x">The first item to track changes for and compare to <paramref name="y"/>.</param> /// <param name="y">The othe item to track changes for and compare to <paramref name="x"/>.</param> /// <param name="settings"> /// An instance of <see cref="PropertiesSettings"/> configuring how tracking will be performed /// </param> /// <returns>An <see cref="IDirtyTracker"/> that signals on differences between in <paramref name="x"/> and <paramref name="y"/></returns> public static IDirtyTracker IsDirty <T>(T x, T y, PropertiesSettings settings) where T : class, INotifyPropertyChanged { Ensure.NotNull(x, nameof(x)); Ensure.NotNull(y, nameof(y)); Ensure.NotSame(x, y, nameof(x), nameof(y)); Ensure.SameType(x, y); VerifyCanTrackIsDirty(x.GetType(), settings, typeof(Track).Name, nameof(IsDirty)); return(new DirtyTracker(x, y, settings)); }
internal static ValueDiff PropertyValuesOrNull <T>(T x, T y, PropertiesSettings settings) { Debug.Assert(settings != null, "settings == null"); if (TryGetValueDiff(x, y, settings, out var diff)) { return(diff); } return(TryCreateValueDiff(x, y, settings)); }
/// <summary> /// Compares x and y for equality using property values. /// If a type implements IList the items of the list are compared /// </summary> /// <typeparam name="T">The type to compare</typeparam> /// <param name="x">The first instance</param> /// <param name="y">The second instance</param> /// <param name="referenceHandling"> /// If Structural is used a deep equals is performed. /// Default value is Throw /// </param> /// <param name="bindingFlags">The binding flags to use when getting properties</param> /// <returns>True if <paramref name="x"/> and <paramref name="y"/> are equal</returns> public static bool PropertyValues <T>( T x, T y, ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); return(PropertyValues(x, y, settings)); }
/// <summary> /// Synchronizes property values from source to target. /// </summary> /// <typeparam name="T">The type os <paramref name="source"/> and <paramref name="target"/></typeparam> /// <param name="source">The instance to copy property values from</param> /// <param name="target">The instance to copy property values to</param> /// <param name="referenceHandling"> /// If Structural is used property values for sub properties are copied for the entire graph. /// Activator.CreateInstance is used to new up references so a default constructor is required, can be private /// </param> /// <param name="bindingFlags">The binding flags to use when getting properties</param> /// <returns>A disposable that when disposed stops synchronizing</returns> public static IDisposable PropertyValues <T>( T source, T target, ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) where T : class, INotifyPropertyChanged { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); return(PropertyValues(source, target, settings)); }
/// <summary> /// Creates a tracker that detects and notifies about differences for any property or subproperty of <paramref name="x"/> compared to <paramref name="y"/> /// </summary> /// <typeparam name="T"> /// The type of <paramref name="x"/> and <paramref name="y"/> /// </typeparam> /// <param name="x">The first item to track changes for and compare to <paramref name="y"/>.</param> /// <param name="y">The othe item to track changes for and compare to <paramref name="x"/>.</param> /// <param name="referenceHandling"> /// - Structural tracks property values for the entire graph. /// - References tracks only one level and uses reference equality. /// - Throw throws and exception if there are nested trackable types /// </param> /// <param name="bindingFlags"> /// The <see cref="BindingFlags"/> to use when getting properties to track /// </param> /// <returns>An <see cref="IDirtyTracker"/> that signals on differences between in <paramref name="x"/> and <paramref name="y"/></returns> public static IDirtyTracker IsDirty <T>( T x, T y, ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) where T : class, INotifyPropertyChanged { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); return(IsDirty(x, y, settings)); }
/// <summary> /// Copies property values from source to target. /// </summary> /// <typeparam name="T">The type of <paramref name="source" /> and <paramref name="target" /></typeparam> /// <param name="source">The instance to copy property values from</param> /// <param name="target">The instance to copy property values to</param> /// <param name="referenceHandling"> /// If Structural is used property values for sub properties are copied for the entire graph. /// Activator.CreateInstance is sued to new up references so a default constructor is required, can be private /// </param> /// <param name="bindingFlags">The binding flags to use when getting properties</param> public static void PropertyValues <T>( T source, T target, ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) where T : class { var settings = PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags); PropertyValues(source, target, settings); }
internal static IRefCounted <RootChanges> GetOrCreate(object source, PropertiesSettings settings, bool isRoot) { Debug.Assert(source != null, "Cannot track null"); if (isRoot) { Track.Verify.CanTrackType(source.GetType(), settings); } else { Track.Verify.CanTrackValue(source, settings); } return(TrackerCache.GetOrAdd(source, settings, s => new RootChanges(s, settings))); }
internal static bool TryGetOrCreate(object source, PropertiesSettings settings, bool isRoot, out IRefCounted <ChangeTrackerNode> result) { if (source is INotifyPropertyChanged inpc) { result = GetOrCreate(inpc, settings, isRoot); return(true); } if (source is INotifyCollectionChanged incc) { result = GetOrCreate(incc, settings, isRoot); return(true); } result = null; return(false); }
private ChangeTrackerNode(object source, PropertiesSettings settings, bool isRoot) { this.refcountedRootChanges = RootChanges.GetOrCreate(source, settings, isRoot); var sourceChanges = this.refcountedRootChanges.Value; this.children = ChildNodes <ChangeTrackerNode> .Borrow(); sourceChanges.PropertyChange += this.OnSourcePropertyChange; if (Is.NotifyingCollection(source)) { this.ItemType = this.SourceList.GetType() .GetItemType(); sourceChanges.Add += this.OnSourceAdd; sourceChanges.Remove += this.OnSourceRemove; sourceChanges.Replace += this.OnSourceReplace; sourceChanges.Move += this.OnSourceMove; sourceChanges.Reset += this.OnSourceReset; } }
private RootChanges(object source, PropertiesSettings settings) { this.Source = source; this.Settings = settings; this.TrackProperties = this.Source.GetType() .GetProperties() .Where(p => !this.Settings.IsIgnoringProperty(p)) .Where(p => !settings.IsImmutable(p.PropertyType)) .ToArray(); if (source is INotifyPropertyChanged inpc) { inpc.PropertyChanged += this.OnSourcePropertyChanged; } if (source is INotifyCollectionChanged incc) { incc.CollectionChanged += this.OnSourceCollectionChanged; } }
/// <summary> /// Create the settings object. /// </summary> /// <param name="referenceHandling">How references are handled.</param> /// <param name="bindingFlags">What bindingflags to use</param> /// <returns>An instance of <see cref="PropertiesSettings"/></returns> public PropertiesSettings CreateSettings( ReferenceHandling referenceHandling = ReferenceHandling.Structural, BindingFlags bindingFlags = Constants.DefaultPropertyBindingFlags) { if (this.ignoredProperties.Count == 0 && this.ignoredTypes == null && this.comparers.Count == 0 && this.copyers.Count == 0) { return(PropertiesSettings.GetOrCreate(referenceHandling, bindingFlags)); } return(new PropertiesSettings( this.ignoredProperties, this.ignoredTypes, this.immutableTypes, this.comparers, this.copyers, referenceHandling, bindingFlags)); }
/// <summary> /// Initializes a new instance of the <see cref="DirtyTrackerNode"/> class. /// A call to Initialize is needed after the ctor due to that we need to fetch child nodes and the graph can contain self /// </summary> private DirtyTrackerNode(IRefCounted <ReferencePair> refCountedPair, PropertiesSettings settings, bool isRoot) { this.refCountedPair = refCountedPair; var x = refCountedPair.Value.X; var y = refCountedPair.Value.Y; this.children = ChildNodes <DirtyTrackerNode> .Borrow(); this.xNode = RootChanges.GetOrCreate(x, settings, isRoot); this.yNode = RootChanges.GetOrCreate(y, settings, isRoot); this.xNode.Value.PropertyChange += this.OnTrackedPropertyChange; this.yNode.Value.PropertyChange += this.OnTrackedPropertyChange; this.IsTrackingCollectionItems = Is.Enumerable(x, y) && !settings.IsImmutable(x.GetType().GetItemType()) && !settings.IsImmutable(y.GetType().GetItemType()); if (Is.NotifyingCollections(x, y)) { this.xNode.Value.Add += this.OnTrackedAdd; this.xNode.Value.Remove += this.OnTrackedRemove; this.xNode.Value.Replace += this.OnTrackedReplace; this.xNode.Value.Move += this.OnTrackedMove; this.xNode.Value.Reset += this.OnTrackedReset; this.yNode.Value.Add += this.OnTrackedAdd; this.yNode.Value.Remove += this.OnTrackedRemove; this.yNode.Value.Replace += this.OnTrackedReplace; this.yNode.Value.Move += this.OnTrackedMove; this.yNode.Value.Reset += this.OnTrackedReset; } var builder = DiffBuilder.GetOrCreate(x, y, settings); builder.Value.UpdateDiffs(x, y, settings); builder.Value.Refresh(); this.refcountedDiffBuilder = builder; this.isDirty = !this.Builder.IsEmpty; }
internal static IRefCounted <ChangeTrackerNode> GetOrCreate(INotifyCollectionChanged source, PropertiesSettings settings, bool isRoot) { Debug.Assert(source != null, "Cannot track null"); return(TrackerCache.GetOrAdd((object)source, settings, s => new ChangeTrackerNode(s, settings, isRoot))); }
/// <summary> /// Compares x and y for equality using property values. /// If a type implements IList the items of the list are compared /// </summary> /// <typeparam name="T">The type of <paramref name="x"/> and <paramref name="y"/></typeparam> /// <param name="x">The first instance</param> /// <param name="y">The second instance</param> /// <param name="settings">Specifies how equality is performed.</param> /// <returns>True if <paramref name="x"/> and <paramref name="y"/> are equal</returns> public static bool PropertyValues <T>(T x, T y, PropertiesSettings settings) { return(MemberValues(x, y, settings)); }
/// <summary> /// Check if the properties of <paramref name="type"/> can be copied. /// This method will throw an exception if copy cannot be performed for <paramref name="type"/> /// Read the exception message for detailed instructions about what is wrong. /// Use this to fail fast or in unit tests. /// </summary> /// <param name="type">The type to check</param> /// <param name="settings">Contains configuration for how copy will be performed</param> public static void VerifyCanCopyPropertyValues(Type type, PropertiesSettings settings) { Verify.CanCopyRoot(type, settings); Verify.CanCopyMemberValues(type, settings, typeof(Copy).Name, nameof(VerifyCanCopyPropertyValues)); }
/// <summary> /// Check if the properties of <typeparamref name="T"/> can be copied. /// This method will throw an exception if copy cannot be performed for <typeparamref name="T"/> /// Read the exception message for detailed instructions about what is wrong. /// Use this to fail fast or in unit tests. /// </summary> /// <typeparam name="T">The type to check</typeparam> /// <param name="settings">Contains configuration for how copy will be performed</param> public static void VerifyCanCopyPropertyValues <T>(PropertiesSettings settings) { var type = typeof(T); VerifyCanCopyPropertyValues(type, settings); }
internal static IRefCounted <DirtyTrackerNode> GetOrCreate(object x, object y, PropertiesSettings settings, bool isRoot) { Debug.Assert(x != null, "Cannot track null"); Debug.Assert(x is INotifyPropertyChanged || x is INotifyCollectionChanged, "Must notify"); Debug.Assert(y != null, "Cannot track null"); Debug.Assert(y is INotifyPropertyChanged || y is INotifyCollectionChanged, "Must notify"); return(TrackerCache.GetOrAdd( x, y, settings, pair => new DirtyTrackerNode(pair, settings, isRoot))); }
internal static void VerifyCanCopyPropertyValues(Type type, PropertiesSettings settings, string classname, string methodName) { Verify.CanCopyRoot(type, settings); Verify.GetOrCreateErrors(type, settings) .ThrowIfHasErrors(settings, classname, methodName); }
internal DirtyTracker(INotifyPropertyChanged x, INotifyPropertyChanged y, PropertiesSettings settings) { this.Settings = settings; this.node = DirtyTrackerNode.GetOrCreate(x, y, settings, isRoot: true); this.node.Value.PropertyChanged += this.OnNodeChanged; }
/// <summary> /// Check if the properties of <typeparamref name="T"/> can be compared for equality /// This method will throw an exception if copy cannot be performed for <typeparamref name="T"/> /// Read the exception message for detailed instructions about what is wrong. /// Use this to fail fast or in unit tests. /// </summary> /// <typeparam name="T">The type to check.</typeparam> /// <param name="settings">The settings to use.</param> public static void VerifyCanDiffByPropertyValues <T>(PropertiesSettings settings) { EqualBy.Verify.CanEqualByMemberValues <T>(settings, typeof(DiffBy).Name, nameof(DiffBy.PropertyValues)); }