/// <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));
 }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
            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()));
        }
Beispiel #6
0
 /// <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);
 }
Beispiel #7
0
        /// <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);
        }
Beispiel #8
0
            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));
                }
            }
Beispiel #9
0
 /// <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)));
 }
Beispiel #10
0
        /// <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));
        }
Beispiel #11
0
 /// <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));
 }
Beispiel #12
0
        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));
        }
Beispiel #14
0
        /// <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));
        }
Beispiel #15
0
        /// <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));
        }
Beispiel #16
0
        /// <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);
        }
Beispiel #17
0
        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)));
        }
Beispiel #18
0
        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);
        }
Beispiel #19
0
        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;
            }
        }
Beispiel #20
0
        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;
        }
Beispiel #23
0
 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));
 }
Beispiel #25
0
 /// <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));
 }
Beispiel #26
0
        /// <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)));
 }
Beispiel #28
0
 internal static void VerifyCanCopyPropertyValues(Type type, PropertiesSettings settings, string classname, string methodName)
 {
     Verify.CanCopyRoot(type, settings);
     Verify.GetOrCreateErrors(type, settings)
     .ThrowIfHasErrors(settings, classname, methodName);
 }
Beispiel #29
0
 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;
 }
Beispiel #30
0
 /// <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));
 }