// Copyright (C) Stichting Deltares 2018. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
namespace Core.Common.Base
{
///
/// Class that implements in a way that a hierarchy of objects can be observed.
///
///
/// The root container () being observed by instances of this class can be dynamically changed.
///
/// The type of the item containers that specify the object hierarchy.
/// The type of items (in the containers) that should be observed.
public class RecursiveObserver : IObserver, IDisposable
where TContainer : class, IObservable
where TObservable : class, IObservable
{
private readonly Action updateObserverAction;
private readonly Func> getChildren;
private readonly List observedContainers = new List();
private readonly List observedChildren = new List();
private readonly Observer containerObserver;
private TContainer rootContainer;
///
/// Creates a new instance of the class.
///
/// The action to perform on notifications coming from one of the items of the hierarchy.
/// The method used for recursively obtaining the children of objects in the hierarchy.
public RecursiveObserver(Action updateObserverAction, Func> getChildren)
{
this.updateObserverAction = updateObserverAction;
this.getChildren = getChildren;
// Ensure subscriptions are updated (detach/attach) on changes in the hierarchy
containerObserver = new Observer(UpdateObservedObjects);
}
///
/// Gets or sets the root container.
///
public TContainer Observable
{
get
{
return rootContainer;
}
set
{
rootContainer = value;
UpdateObservedObjects();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void UpdateObserver()
{
updateObserverAction();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Observable = null;
}
}
private void UpdateObservedObjects()
{
// Detach from the currently observed containers
foreach (TContainer observedObject in observedContainers)
{
observedObject.Detach(containerObserver);
}
// Detach from the currently observed children
foreach (TObservable observedObject in observedChildren)
{
observedObject.Detach(this);
}
// Clear the lists of observed objects
observedContainers.Clear();
observedChildren.Clear();
// If relevant, start observing objects again
if (rootContainer != null)
{
ObserveObjectsRecursively(rootContainer);
}
}
private void ObserveObjectsRecursively(TContainer container)
{
container.Attach(containerObserver);
observedContainers.Add(container);
var observable = container as TObservable;
if (observable != null)
{
observable.Attach(this);
observedChildren.Add(observable);
}
foreach (object child in getChildren(container))
{
var childContainer = child as TContainer;
if (childContainer != null)
{
ObserveObjectsRecursively(childContainer);
}
else if (child is TObservable)
{
observable = (TObservable) child;
observable.Attach(this);
observedChildren.Add(observable);
}
}
}
}
}