Ejemplo n.º 1
0
        internal DeepTracker(TrackRouteConfiguration configuration, object source)
        {
            _configuration             = configuration;
            _sourceReference           = new WeakReference(source);
            _propertyChangedRegistry   = new Registry <Tuple <PropertyReference, object> >();
            _collectionChangedRegistry = new Registry <Tuple <WeakReference, object[]> >();
            _collectionChildrenIds     = new ConditionalWeakTable <object, string>();
            _attachmentObjectRegistry  = new Registry <WeakReference>();

            _propertyChangedSubscriptions = new DynamicSubscription <PropertyReference, EventHandler>(
                (changed, handler) => changed.TryAddValueChanged(handler),
                (changed, handler) => changed.TryRemoveValueChanged(handler));

            _collectionChangedSubscriptions = new DynamicSubscription <INotifyCollectionChanged, NotifyCollectionChangedEventHandler>(
                (changed, handler) => changed.CollectionChanged += handler,
                (changed, handler) => changed.CollectionChanged -= handler);
        }
Ejemplo n.º 2
0
        private void AddBranch(Route visitedRoute,
                               PropertyReference reference,
                               TrackRouteConfiguration configuration,
                               IReadOnlyList <int> visitedObjects)
        {
            if (reference.Name.Contains("."))
            {
                return;
            }
            var propertyRoute = Route.Create(visitedRoute, reference.Name);

            var match = propertyRoute.MatchPartially(configuration.Route);

            if (!match)
            {
                return;
            }

            var lazyValue = new Lazy <object>(() =>
            {
                try
                {
                    if (reference.TryGetValue(out var scopedValue))
                    {
                        return(scopedValue);
                    }
                }
                catch
                {
                    //Nothing
                }

                return(NotValidValue);
            });

            if (!configuration.IsAllowed(reference, lazyValue, propertyRoute))
            {
                return;
            }

            try
            {
                var value = lazyValue.Value;
                if (value.AreEqual(NotValidValue))
                {
                    return;
                }

                if (reference.SupportsChangeEvents)
                {
                    var closureValue = value.WrapValue();

                    void PropertyChangedHandler(object sender, EventArgs e)
                    {
                        var oldValue = closureValue.UnwrapValue();

                        object newValue = null;

                        try
                        {
                            reference.TryGetValue(out newValue);
                        }
                        catch
                        {
                            //Nothing
                        }

                        if (ReferenceEquals(oldValue, newValue))
                        {
                            return;
                        }

                        RemoveBranch(propertyRoute);

                        try
                        {
                            if (newValue != null)
                            {
                                closureValue = newValue.WrapValue();
                                AddBranch(propertyRoute, newValue, configuration, visitedObjects);
                            }
                            else
                            {
                                closureValue = new WeakReference(null);
                            }

                            if (!Equals(oldValue, newValue))
                            {
                                var args = new ChangedPropertyEventArgs(propertyRoute, reference, oldValue, newValue);
                                _configuration.Raise(this, args);
                            }
                        }
                        catch
                        {
                            //Nothing
                        }
                    }

                    lock (_propertyChangedRegistry)
                    {
                        var args = new PropertyAttachedEventArgs(propertyRoute, reference, value);
                        _configuration.Raise(this, args);

                        var record = new Tuple <PropertyReference, object>(reference, value);
                        _propertyChangedRegistry.Add(propertyRoute, record);
                        _propertyChangedSubscriptions.Subscribe(reference, PropertyChangedHandler);
                    }
                }

                if (value != null)
                {
                    AddBranch(propertyRoute, value, configuration, visitedObjects);
                }
            }
            catch
            {
                //Nothing
            }
        }
Ejemplo n.º 3
0
        private void AddBranch(Route visitedRoute, object source, TrackRouteConfiguration configuration, IReadOnlyList <int> visitedObjects)
        {
            var sourceHash = source.GetHash();

            if (visitedObjects.Contains(sourceHash))
            {
                return;
            }

            lock (_attachmentObjectRegistry)
            {
                var objectAttachedEventArgs = new ObjectAttachedEventArgs(visitedRoute, source);
                _configuration.Raise(this, objectAttachedEventArgs);
                _attachmentObjectRegistry.Add(visitedRoute, new WeakReference(source));
            }

            visitedObjects = visitedObjects.UnionWith(source.GetHash()).ToList();
            if (source is IEnumerable collection && !(source is string))
            {
                var sourceItems = collection.Enumerate().ToArray();

                if (collection is INotifyCollectionChanged notifyCollection)
                {
                    var closureSourceItems = new WeakReference(sourceItems.Enumerate().ToList());

                    void NotifyCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs args)
                    {
                        var previousItems = closureSourceItems.Target as List <object>;

                        if (args.Action == NotifyCollectionChangedAction.Reset)
                        {
                            var newClosureSourceItems = sender.Enumerate().ToList();
                            foreach (var item in previousItems.Enumerate())
                            {
                                if (_collectionChildrenIds.TryGetValue(item, out var id))
                                {
                                    RemoveBranch(Route.Create(visitedRoute, id));
                                }
                            }

                            closureSourceItems = new WeakReference(newClosureSourceItems);

                            foreach (var item in newClosureSourceItems)
                            {
                                if (item == null)
                                {
                                    continue;
                                }

                                if (!_collectionChildrenIds.TryGetValue(item, out var id))
                                {
                                    id = Guid.NewGuid().CreateDynamicRoute().ToString();
                                    _collectionChildrenIds.Add(item, id);
                                }

                                AddBranch(Route.Create(visitedRoute, id), item, _configuration, visitedObjects);
                            }
                        }
                        else
                        {
                            foreach (var item in args.NewItems.Enumerate())
                            {
                                if (item == null)
                                {
                                    continue;
                                }

                                if (!_collectionChildrenIds.TryGetValue(item, out var id))
                                {
                                    id = Guid.NewGuid().CreateDynamicRoute().ToString();
                                    _collectionChildrenIds.Add(item, id);
                                }

                                AddBranch(Route.Create(visitedRoute, id), item, _configuration, visitedObjects);
                                previousItems?.Add(item);
                            }

                            foreach (var item in args.OldItems.Enumerate())
                            {
                                if (_collectionChildrenIds.TryGetValue(item, out var id))
                                {
                                    RemoveBranch(Route.Create(visitedRoute, id));
                                }

                                previousItems?.Remove(item);
                            }
                        }

                        var collectionChangedEventArgs = new ChangedCollectionEventArgs(visitedRoute, sender, args);

                        _configuration.Raise(this, collectionChangedEventArgs);
                    }

                    lock (_collectionChangedRegistry)
                    {
                        var args = new CollectionAttachedEventArgs(visitedRoute, collection);
                        _configuration.Raise(this, args);

                        var record = new Tuple <WeakReference, object[]>(new WeakReference(collection), sourceItems);
                        _collectionChangedRegistry.Add(visitedRoute, record);
                    }

                    _collectionChangedSubscriptions.Subscribe(notifyCollection, NotifyCollectionChangedHandler);
                }

                foreach (var item in sourceItems)
                {
                    if (item == null)
                    {
                        continue;
                    }

                    if (!_collectionChildrenIds.TryGetValue(item, out var id))
                    {
                        id = Guid.NewGuid().CreateDynamicRoute().ToString();
                        _collectionChildrenIds.Add(item, id);
                    }

                    AddBranch(Route.Create(visitedRoute, id), item, _configuration, visitedObjects);
                }
            }

            foreach (var reference in source.GetPropertyReferences())
            {
                AddBranch(visitedRoute, reference, configuration, visitedObjects);
            }
        }