Index: Core/Common/src/Core.Common.Controls/Views/IView.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/src/Core.Common.Controls/Views/IView.cs (.../IView.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Controls/Views/IView.cs (.../IView.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -24,7 +24,7 @@ namespace Core.Common.Controls.Views { /// - /// Interface for graphical user interface views. + /// Interface for graphical user interface views. Class should have a default constructor. /// public interface IView : IDisposable { Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/IViewResolver.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/IViewResolver.cs (.../IViewResolver.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/IViewResolver.cs (.../IViewResolver.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -27,71 +27,45 @@ namespace Core.Common.Gui.Forms.ViewManager { + /// + /// Interface for an object capable of finding a view for a given data object. + /// public interface IViewResolver { /// - /// Default view types registered for data object types. + /// Default view types registered for data object types, that can be used to automatically + /// resolve to a particular view when multiple candidates are available. /// /// The keys in this dictionary are the object types and the values the - /// corresponding view object types. + /// corresponding view types. IDictionary DefaultViewTypes { get; } /// - /// List of view info objects used for resolving views + /// Opens a view for specified data. /// - IList ViewInfos { get; } - - /// - /// Opens a view for specified data. Using viewprovider to resolve the correct view. - /// - /// Data to open a view for - /// Always present the user with a dialog to choose from + /// Data to open a view for. + /// Always present the user with a dialog to choose + /// the view that has to be opened. bool OpenViewForData(object data, bool alwaysShowDialog = false); /// - /// Creates a view for the - /// - /// The data to create a view for - /// Function to filter the view infos to use - /// A view for data - IView CreateViewForData(object data, Func selectViewInfo = null); - - /// - /// Check if a view can be created for the . - /// - /// The data to check for - /// - bool CanOpenViewFor(object data); - - /// - /// Returns all currently opened views for the same data. - /// - /// - /// - IList GetViewsForData(object data); - - /// /// Closes all views for . /// - /// - /// + /// The data object to close all views for. void CloseAllViewsFor(object data); /// - /// Gives the default viewtype for the given data object. + /// Gets the view info objects for the . /// - /// - /// - Type GetDefaultViewType(object dataObject); + /// Data used for finding matches. + /// The matching view info object. + IEnumerable GetViewInfosFor(object data); /// - /// Gets the view info objects for the + /// Gets the name of the view. /// - /// Data used for searching the view infos - /// The viewType of the view info - /// The matching view infos for data and view type - IEnumerable GetViewInfosFor(object data, Type viewType = null); - + /// The view. + /// The name of the view. string GetViewName(IView view); } } \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewList.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewList.cs (.../ViewList.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewList.cs (.../ViewList.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -103,7 +103,7 @@ private bool Close(IView view, bool removeTabFromDockingManager, bool activateNextView) { - if (ViewResolver.IsViewOpening) + if (ViewResolver.IsOpeningView) { throw new InvalidOperationException(Resources.ViewList_Close_View_is_being_closed_while_it_is_being_opened); } Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewResolver.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewResolver.cs (.../ViewResolver.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewResolver.cs (.../ViewResolver.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -23,28 +23,40 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; + using Core.Common.Controls.Views; using Core.Common.Gui.Plugin; using Core.Common.Gui.Properties; using Core.Common.Utils.Reflection; + using log4net; namespace Core.Common.Gui.Forms.ViewManager { + /// + /// Object responsible for finding a view given some data-object. + /// public class ViewResolver : IViewResolver { - private static readonly ILog Log = LogManager.GetLogger(typeof(ViewResolver)); + private static readonly ILog log = LogManager.GetLogger(typeof(ViewResolver)); + private readonly IDictionary defaultViewTypes = new Dictionary(); private readonly ViewList viewList; - private readonly IList viewInfos; - private readonly IWin32Window owner; + private readonly ViewInfo[] viewInfos; + private readonly IWin32Window dialogParent; - public ViewResolver(ViewList viewList, IEnumerable viewInfos, IWin32Window owner) + /// + /// Initializes a new instance of the class. + /// + /// The view list. + /// The sequence of available view info objects. + /// The parent object for which dialogs should be shown on top. + public ViewResolver(ViewList viewList, IEnumerable viewInfos, IWin32Window dialogParent) { this.viewList = viewList; - this.viewInfos = viewInfos.ToList(); - this.owner = owner; + this.viewInfos = viewInfos.ToArray(); + this.dialogParent = dialogParent; } public IDictionary DefaultViewTypes @@ -55,38 +67,30 @@ } } - public IList ViewInfos - { - get - { - return viewInfos; - } - } - public bool OpenViewForData(object data, bool alwaysShowDialog = false) { try { - IsViewOpening = true; + IsOpeningView = true; if (data == null) { return false; } - var viewInfoList = FilterOnInheritance(GetViewInfosFor(data)).ToList(); - - if (viewInfoList.Count == 0) + var viewInfoList = FilterOnInheritance(GetViewInfosFor(data)).ToArray(); + if (viewInfoList.Length == 0) { - Log.DebugFormat(Resources.ViewResolver_OpenViewForData_No_view_registered_for_0_, data); + log.DebugFormat(Resources.ViewResolver_OpenViewForData_No_view_registered_for_0_, data); return false; } if (!alwaysShowDialog) { - if (viewInfoList.Count == 1) + if (viewInfoList.Length == 1) { - return CreateViewFromViewInfo(data, viewInfoList[0]); + CreateViewFromViewInfo(data, viewInfoList[0]); + return true; } // Create default view @@ -95,100 +99,50 @@ if (defaultViewInfo != null) { - if (CreateViewFromViewInfo(data, defaultViewInfo)) - { - return true; - } - - viewInfoList.Remove(defaultViewInfo); + CreateViewFromViewInfo(data, defaultViewInfo); + return true; } } - if (viewInfoList.Count == 0) + // Create chosen view + var chosenViewInfo = GetViewInfoUsingDialog(data, viewInfoList); + if (chosenViewInfo == null) { return false; } - - // Create chosen view - var chosenViewInfo = GetViewInfoUsingDialog(data, viewInfoList); - - return chosenViewInfo != null && CreateViewFromViewInfo(data, chosenViewInfo); + else + { + CreateViewFromViewInfo(data, chosenViewInfo); + return true; + } } finally { - IsViewOpening = false; + IsOpeningView = false; } } - public IView CreateViewForData(object data, Func selectViewInfo = null) + public void CloseAllViewsFor(object data) { - var viewInfoList = ((selectViewInfo == null) - ? GetViewInfosFor(data) - : GetViewInfosFor(data).Where(selectViewInfo)) - .ToList(); - - if (viewInfoList.Count == 0) + if (data == null) { - return null; + return; } - if (viewInfoList.Count > 1) + foreach (var view in viewList.ToArray()) { - throw new Exception(Resources.ViewResolver_CreateViewForData_More_than_one_view_for_data); + if (ShouldRemoveViewForData(view, data)) + { + viewList.Remove(view); + } } - - return CreateViewForData(data, viewInfoList[0]); } - public bool CanOpenViewFor(object data) + public IEnumerable GetViewInfosFor(object data) { - return data != null && GetViewInfosFor(data).Any(); + return viewInfos.Where(vi => data.GetType().Implements(vi.DataType) && vi.AdditionalDataCheck(data)); } - public IList GetViewsForData(object data) - { - return GetViewsForData(viewList, data).ToList(); - } - - public void CloseAllViewsFor(object data) - { - DoWithMatchingViews(data, viewList, - v => viewList.Remove(v), - (v, o) => - { - var viewInfo = GetViewInfoForView(v); - if (viewInfo != null) - { - return viewInfo.CloseForData(v, o); - } - - return false; - }); - } - - public Type GetDefaultViewType(object dataObject) - { - if (dataObject == null) - { - return null; - } - - var selectionType = dataObject.GetType(); - - return defaultViewTypes.Keys.Contains(selectionType) - ? defaultViewTypes[selectionType] - : null; - } - - public IEnumerable GetViewInfosFor(object data, Type viewType = null) - { - var infos = ViewInfos.Where(vi => data.GetType().Implements(vi.DataType) && vi.AdditionalDataCheck(data)); - - return viewType != null - ? infos.Where(vi => viewType.Implements(vi.ViewType)) - : infos; - } - public string GetViewName(IView view) { var viewInfo = GetViewInfoForView(view); @@ -201,39 +155,42 @@ } /// - /// Checks consistency of ViewList / ViewResolver logic. Sometimes views are closed while being opened. + /// Indicates if the view resolver is opening a view. Can be used to ensure consistency + /// of / logic. Sometimes views + /// are closed while being opened. /// - internal static bool IsViewOpening { get; private set; } + internal static bool IsOpeningView { get; private set; } - private void DoWithMatchingViews(object data, IEnumerable views, Action viewAction, Func extraCheck = null) + private bool ShouldRemoveViewForData(IView view, object data) { - var viewsToCheck = views.ToList(); - foreach (var view in viewsToCheck) + if (IsViewData(view, data)) { - if (IsViewData(view, data) || (extraCheck != null && extraCheck(view, data))) - { - viewAction(view); - } + return true; } + + var viewInfo = GetViewInfoForView(view); + return viewInfo != null && viewInfo.CloseForData(view, data); } - private IEnumerable GetViewsForData(IEnumerable viewsToCheck, object data) + private Type GetDefaultViewType(object dataObject) { - return data != null - ? GetOpenViewsFor(viewsToCheck, data) - : Enumerable.Empty(); + var selectionType = dataObject.GetType(); + + return defaultViewTypes.ContainsKey(selectionType) + ? defaultViewTypes[selectionType] + : null; } private static IEnumerable FilterOnInheritance(IEnumerable compatibleStandaloneViewInfos) { - var viewInfos = compatibleStandaloneViewInfos.ToList(); + var viewInfos = compatibleStandaloneViewInfos.ToArray(); // filter on inheritance - var dataTypes = viewInfos.Select(i => i.DataType).ToList(); + var dataTypes = viewInfos.Select(i => i.DataType).ToArray(); return viewInfos.Where(i => !dataTypes.Any(t => t != i.DataType && t.Implements(i.DataType))); } - private bool CreateViewFromViewInfo(object data, ViewInfo viewInfo) + private void CreateViewFromViewInfo(object data, ViewInfo viewInfo) { var viewData = viewInfo.GetViewData(data); var view = (viewInfo.DataType == viewInfo.ViewDataType @@ -246,20 +203,18 @@ viewInfo.OnActivateView(view, data); viewList.ActiveView = view; - return true; + return; } var newView = CreateViewForData(data, viewInfo); viewList.Add(newView); viewList.SetImage(newView, viewInfo.Image); - - return true; } private static IView CreateViewForData(object data, ViewInfo viewInfo) { - var view = (IView) Activator.CreateInstance(viewInfo.ViewType); + var view = (IView)Activator.CreateInstance(viewInfo.ViewType); view.Data = viewInfo.GetViewData(data); @@ -280,7 +235,7 @@ : null; var viewTypeDictionary = viewInfoList.ToDictionary(vi => vi.Description ?? vi.ViewType.Name); - using (var viewSelector = new SelectViewDialog(owner) + using (var viewSelector = new SelectViewDialog(dialogParent) { DefaultViewName = defaultViewName, Items = viewTypeDictionary.Keys.ToList() @@ -306,39 +261,20 @@ } } - private IEnumerable GetOpenViewsFor(IEnumerable viewsToCheck, object data, Func extraCheck = null) + private IEnumerable GetOpenViewsFor(IEnumerable viewsToCheck, object data) { - if (data == null) - { - yield break; - } - - foreach (var view in viewsToCheck) - { - var viewInfo = GetViewInfoForView(view); - - if (IsViewData(view, data) && (extraCheck == null || extraCheck(view, viewInfo))) - { - yield return view; - } - } + return viewsToCheck.Where(view => IsViewData(view, data)); } private bool IsViewData(IView view, object data) { var viewInfo = GetViewInfoForView(view); - return data.Equals(view.Data) || (IsDataForView(view, data) && Equals(viewInfo.GetViewData(data), view.Data)); + return data.Equals(view.Data) || (IsDataForView(data, GetViewInfoForView(view)) && Equals(viewInfo.GetViewData(data), view.Data)); } - private bool IsDataForView(IView view, object data) + private bool IsDataForView(object data, ViewInfo info) { - if (data == null) - { - return false; - } - - var viewInfo = GetViewInfoForView(view); - return viewInfo != null && data.GetType().Implements(viewInfo.DataType) && viewInfo.AdditionalDataCheck(data); + return info != null && data.GetType().Implements(info.DataType) && info.AdditionalDataCheck(data); } private ViewInfo GetViewInfoForView(IView view) @@ -350,7 +286,7 @@ { var selectedItemType = data.GetType(); - if (defaultViewTypes.Keys.Contains(selectedItemType)) + if (defaultViewTypes.ContainsKey(selectedItemType)) { defaultViewTypes.Remove(selectedItemType); } @@ -360,7 +296,7 @@ { var selectedItemType = data.GetType(); - if (defaultViewTypes.Keys.Contains(selectedItemType)) + if (defaultViewTypes.ContainsKey(selectedItemType)) { defaultViewTypes[selectedItemType] = selectedViewType; } @@ -374,7 +310,7 @@ { var selectionType = dataObject.GetType(); - return defaultViewTypes.Keys.Contains(selectionType) ? defaultViewTypes[selectionType] : null; + return defaultViewTypes.ContainsKey(selectionType) ? defaultViewTypes[selectionType] : null; } } } \ No newline at end of file Index: Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.Designer.cs =================================================================== diff -u -r1e9f415cb615bf84526faeecb51f9a70498f9ffc -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.Designer.cs (.../TestView.Designer.cs) (revision 1e9f415cb615bf84526faeecb51f9a70498f9ffc) +++ Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.Designer.cs (.../TestView.Designer.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -1,4 +1,4 @@ -namespace Core.Common.Test.TestObjects +namespace Core.Common.Gui.Test.Forms.ViewManager { partial class TestView { Index: Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.cs =================================================================== diff -u -r1e9f415cb615bf84526faeecb51f9a70498f9ffc -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.cs (.../TestView.cs) (revision 1e9f415cb615bf84526faeecb51f9a70498f9ffc) +++ Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/TestView.cs (.../TestView.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -1,7 +1,8 @@ using System.Windows.Forms; + using Core.Common.Controls.Views; -namespace Core.Common.Test.TestObjects +namespace Core.Common.Gui.Test.Forms.ViewManager { public partial class TestView : UserControl, IView { Index: Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/ViewResolverTest.cs =================================================================== diff -u -r1e9f415cb615bf84526faeecb51f9a70498f9ffc -r6c6c9b6eb17d92348c51b05a7e862c860c3d018a --- Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/ViewResolverTest.cs (.../ViewResolverTest.cs) (revision 1e9f415cb615bf84526faeecb51f9a70498f9ffc) +++ Core/Common/test/Core.Common.Gui.Test/Forms/ViewManager/ViewResolverTest.cs (.../ViewResolverTest.cs) (revision 6c6c9b6eb17d92348c51b05a7e862c860c3d018a) @@ -1,10 +1,10 @@ using System; using System.Linq; +using System.Windows.Forms; using Core.Common.Controls.Views; using Core.Common.Gui.Forms.ViewManager; using Core.Common.Gui.Plugin; -using Core.Common.Test.TestObjects; using Core.Common.Utils; using NUnit.Framework; @@ -13,134 +13,830 @@ namespace Core.Common.Gui.Test.Forms.ViewManager { + [TestFixture] public class ViewResolverTest { [Test] - public void OpeningAViewForAObjectShouldUseNewView() + public void ParameteredConstructor_ExpectedValues() { // Setup var mocks = new MockRepository(); var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); mocks.ReplayAll(); - var viewList = new ViewList(dockingManager, ViewLocation.Left); - var viewResolver = new ViewResolver(viewList, new ViewInfo[] + using (var viewList = new ViewList(dockingManager, ViewLocation.Bottom)) { + // Call + var viewResolver = new ViewResolver(viewList, Enumerable.Empty(), dialogParent); + + // Assert + Assert.IsInstanceOf(viewResolver); + CollectionAssert.IsEmpty(viewResolver.DefaultViewTypes); + + mocks.VerifyAll(); + } + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void OpenViewForData_DataIsNull_ReturnFalse(bool forceShowDialog) + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var viewInfos = new ViewInfo[] + { new ViewInfo() - }, null); + }; - var testObject = new object(); - viewResolver.OpenViewForData(testObject); - var viewForTestObject = viewList[0]; - Assert.AreEqual(testObject, viewForTestObject.Data); + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); - var otherObject = new object(); - viewResolver.OpenViewForData(otherObject); + // Call + var result = viewResolver.OpenViewForData(null, forceShowDialog); - // extra views. the first view now renders the other object - Assert.AreEqual(2, viewList.Count); + // Assert + Assert.IsFalse(result); + } + } - // no change in original view - Assert.AreEqual(testObject, viewForTestObject.Data); + [Test] + public void OpenViewForData_DataHasSingleMatch_ReturnTrueAndAddToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + var data = new object(); + const string viewData = ""; + const string viewName = ""; + bool afterCreateCalled = false; + bool activateViewCalled = false; + + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo + { + GetViewData = o => + { + Assert.AreSame(data, o); + return viewData; + }, + AfterCreate = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data, o); + afterCreateCalled = true; + }, + GetViewName = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(viewData, o); + return viewName; + }, + OnActivateView = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data, o); + activateViewCalled = true; + } + }, + new ViewInfo() + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestView)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreEqual(viewData, view.Data); + Assert.AreEqual(viewName, view.Text); + + Assert.IsTrue(afterCreateCalled); + Assert.IsTrue(activateViewCalled); + } + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void OpenViewForData_NoViewInfoRegistered_ReturnFalse(bool forceShowDialog) + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var viewInfos = new ViewInfo[0]; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Call + var result = viewResolver.OpenViewForData(new object(), forceShowDialog); + + // Assert + Assert.IsFalse(result); + } + } + + [Test] + public void OpenViewForData_DataHasSingleMatchOnBaseType_ReturnTrueAndAddToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var data = new InheritedFromA(); + const string viewName = ""; + bool afterCreateCalled = false; + bool activateViewCalled = false; + + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo + { + AfterCreate = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data, o); + afterCreateCalled = true; + }, + GetViewName = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data, o); + return viewName; + }, + OnActivateView = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data, o); + activateViewCalled = true; + } + }, + new ViewInfo() + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestView)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreEqual(data, view.Data); + Assert.AreEqual(viewName, view.Text); + + Assert.IsTrue(afterCreateCalled); + Assert.IsTrue(activateViewCalled); + } + } + + [Test] + public void OpenViewForData_DataHasMultipleMatchesOnType_ResolveToMostSpecializedAndReturnTrueAndAddToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var data = new InheritedFromA(); + + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo() + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestViewDerivative)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreEqual(data, view.Data); + Assert.AreEqual(string.Empty, view.Text); + } + } + + [Test] + public void OpenViewForData_ViewInfosForInheritedData_ResolveToMostSpecializedForDataAndReturnTrueAndAddToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var data = new A(); + + var viewInfos = new ViewInfo[] + { + new ViewInfo(), // Should not be matched as A does not inherit from InheritedFromA! + new ViewInfo() + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestView)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreEqual(data, view.Data); + Assert.AreEqual(string.Empty, view.Text); + } + } + + [Test] + public void OpenViewForData_DataHasMultipleSingleMatches_UseAdditionalDataCheckAndReturnTrueAndAddToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var data = new object(); + + var viewInfos = new ViewInfo[] + { + new ViewInfo + { + AdditionalDataCheck = o => true + }, + new ViewInfo + { + AdditionalDataCheck = o => false + } + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestViewDerivative)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreSame(data, view.Data); + } + } + + [Test] + public void OpenViewForData_DataHasMultipleMatchesAndRegisteredDefaultView_ReturnTrueAndAddDefaultViewToViewList() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + var data = new object(); + + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo() + }; + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + viewResolver.DefaultViewTypes[typeof(object)] = typeof(TestViewDerivative); + + // Call + var result = viewResolver.OpenViewForData(data); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(1, viewList.Count); + var view = (TestViewDerivative)viewList[0]; + Assert.AreSame(view, viewList.ActiveView); + Assert.AreEqual(data, view.Data); + Assert.AreEqual(string.Empty, view.Text); + } + } + + [Test] + public void OpenViewForData_OpenSameViewForTwoDifferentDataInstances_OpenTwoViews() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Left)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo() + }; + + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + var data1 = new object(); + var data2 = new object(); + + // Call + viewResolver.OpenViewForData(data1); + viewResolver.OpenViewForData(data2); + + // Assert + CollectionAssert.AllItemsAreInstancesOfType(viewList, typeof(TestView)); + Assert.AreEqual(2, viewList.Count, + "Should have opened 2 views for 2 different data instances."); + + Assert.AreSame(data1, viewList[0].Data); + Assert.AreSame(data2, viewList[1].Data); + } mocks.VerifyAll(); } [Test] [RequiresSTA] public void OpeningViewForDataTwiceShouldOnlySetActiveView() { + // Setup + var url = new WebLink("Deltares", new Uri("http://www.deltares.nl")); + var mocks = new MockRepository(); var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); mocks.ReplayAll(); - var url = new WebLink("Deltares", new Uri("http://www.deltares.nl")); + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo() + }; - var viewList = new ViewList(dockingManager, ViewLocation.Document); - var viewResolver = new ViewResolver(viewList, new ViewInfo[] + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Open view... + viewResolver.OpenViewForData(url); + + var originallyActiveView = viewList.ActiveView; + + // ... then make it inactive. + viewList.ActiveView = null; + + // Call + viewResolver.OpenViewForData(url); + + // Assert + Assert.AreEqual(1, viewList.Count); + Assert.AreSame(originallyActiveView, viewList[0]); + Assert.AreEqual(originallyActiveView, viewList.ActiveView); + } + mocks.VerifyAll(); + } + + [Test] + public void GetViewInfosFor_NoViewInfosRegistered_ReturnEmpty() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Bottom)) { - new ViewInfo() - }, null); + var viewResolver = new ViewResolver(viewList, Enumerable.Empty(), dialogParent); - viewResolver.OpenViewForData(url); + var data = new object(); - var activeView = viewList.ActiveView; + // Call + var matchedViewInfos = viewResolver.GetViewInfosFor(data); - viewList.ActiveView = null; - viewResolver.OpenViewForData(url); + // Assert + CollectionAssert.IsEmpty(matchedViewInfos); + } + mocks.VerifyAll(); + } - Assert.AreEqual(activeView, viewList.ActiveView); + [Test] + public void GetViewInfosFor_SingleDirectMatch_ReturnSingleMatchingViewInfo() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + using (var viewList = new ViewList(dockingManager, ViewLocation.Bottom)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo(), + new ViewInfo() + }; + + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + var data = default(int); + + // Call + var matchedViewInfos = viewResolver.GetViewInfosFor(data).ToArray(); + + // Assert + CollectionAssert.AreEqual(new[]{viewInfos[1]}, matchedViewInfos); + } mocks.VerifyAll(); } [Test] - public void GetViewsForDataWithCustomData() + public void GetViewInfosFor_ViewInfosWithInheritance_ReturnMatchesBasedOnInheritaceDataType() { + // Setup var mocks = new MockRepository(); var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); mocks.ReplayAll(); - var viewList = new ViewList(dockingManager, ViewLocation.Left); - var viewResolver = new ViewResolver(viewList, new ViewInfo[] + using (var viewList = new ViewList(dockingManager, ViewLocation.Bottom)) { - new ViewInfo + var viewInfos = new ViewInfo[] { - GetViewData = o => o.RealData - } - }, null); + new ViewInfo(), + new ViewInfo(), + new ViewInfo() + }; - var someDataObject = "some data object"; - var wrapper = new TestWrapper + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + var data = new A(); + + // Call + var matchedViewInfos = viewResolver.GetViewInfosFor(data).ToArray(); + + // Assert + var expected = new[] + { + viewInfos[0], + viewInfos[2] + }; + CollectionAssert.AreEqual(expected, matchedViewInfos); + } + mocks.VerifyAll(); + } + + [Test] + public void GetViewInfosFor_ViewInfosWithAdditionalDataCheck_ReturnMatchesWithAdditionalDataCheckTrue() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Bottom)) { - RealData = someDataObject - }; + var viewInfos = new ViewInfo[] + { + new ViewInfo + { + AdditionalDataCheck = a => true + }, + new ViewInfo(), + new ViewInfo + { + AdditionalDataCheck = o => false + } + }; - var view = new TestView + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + var data = new InheritedFromA(); + + // Call + var matchedViewInfos = viewResolver.GetViewInfosFor(data).ToArray(); + + // Assert + var expected = new[] + { + viewInfos[0], + viewInfos[1] + }; + CollectionAssert.AreEqual(expected, matchedViewInfos); + } + mocks.VerifyAll(); + } + + [Test] + public void GetViewName_ViewNotRegistered_ReturnEmptyString() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var view = new TestView()) + using (var viewList = new ViewList(dockingManager, ViewLocation.Floating)) { - Data = someDataObject - }; + var viewResolver = new ViewResolver(viewList, Enumerable.Empty(), dialogParent); - viewList.Add(view); + // Call + var name = viewResolver.GetViewName(view); - var returnedViews = viewResolver.GetViewsForData(wrapper); // <-- must trigger IViewProvider.IsViewForData() + // Assert + Assert.AreEqual(string.Empty, name); + } + mocks.VerifyAll(); + } - Assert.AreEqual(1, returnedViews.Count, "number of compatible views"); + [Test] + public void GetViewName_RegisteredViewHasNoGetViewNameImplementation_ReturnNull() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); - Assert.AreSame(view, returnedViews.First(), "correct view is returned back"); + using (var view = new TestView()) + using (var viewList = new ViewList(dockingManager, ViewLocation.Floating)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo() + }; + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + // Call + var name = viewResolver.GetViewName(view); + + // Assert + Assert.IsNull(name); + } mocks.VerifyAll(); } [Test] - public void OpenViewForItemShouldNotReturnSubClassedViews() + public void GetViewName_RegisteredViewWithGetViewNameImplementation_ReturnName() { + // Setup var mocks = new MockRepository(); var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); mocks.ReplayAll(); - var viewList = new ViewList(dockingManager, ViewLocation.Left); - var viewResolver = new ViewResolver(viewList, new ViewInfo[] + var data = new A(); + const string viewName = ""; + + using (var view = new TestView { Data = data }) + using (var viewList = new ViewList(dockingManager, ViewLocation.Floating)) { - new ViewInfo + var viewInfos = new ViewInfo[] { - Description = "Object view" - }, - new ViewInfo + new ViewInfo + { + GetViewName = (testView, o) => + { + Assert.AreSame(view, testView); + Assert.AreSame(data, o); + return viewName; + } + } + }; + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + + // Call + var name = viewResolver.GetViewName(view); + + // Assert + Assert.AreEqual(viewName, name); + } + mocks.VerifyAll(); + } + + [Test] + public void CloseAllViewsFor_DataDoesNotCorrespondToOpenedViews_DoNothing() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewInfos = new ViewInfo[] { - Description = "String view" - } // string inherits from object - }, null); + new ViewInfo(), + new ViewInfo(), + }; - var data = "string data"; - viewResolver.OpenViewForData(data); + var data1 = new A(); + var data2 = new InheritedFromA(); + var unusedViewData = new object(); - var view = viewList.ActiveView; + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + viewResolver.OpenViewForData(data1); + viewResolver.OpenViewForData(data2); - Assert.AreEqual(data, view.Data); - Assert.AreEqual(typeof(TestViewDerivative), view.GetType()); + // Precondition + Assert.AreEqual(2, viewList.Count); + // Call + viewResolver.CloseAllViewsFor(unusedViewData); + + // Assert + Assert.AreEqual(2, viewList.Count, + "No elements should have been removed."); + } mocks.VerifyAll(); } + + [Test] + public void CloseAllViewsFor_DataDoesNotCorrespondToOpenedViewsButCloseForDataReturnsTrue_RemoveViews() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var data1 = new A(); + var data2 = new InheritedFromA(); + var unusedViewData = new object(); + + var viewInfos = new ViewInfo[] + { + new ViewInfo + { + CloseForData = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data1, view.Data); + Assert.AreSame(unusedViewData, o); + return true; + } + }, + new ViewInfo + { + CloseForData = (view, o) => + { + Assert.IsInstanceOf(view); + Assert.AreSame(data2, view.Data); + Assert.AreSame(unusedViewData, o); + return true; + } + } + }; + + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + viewResolver.OpenViewForData(data1); + viewResolver.OpenViewForData(data2); + + // Precondition + Assert.AreEqual(2, viewList.Count); + + // Call + viewResolver.CloseAllViewsFor(unusedViewData); + + // Assert + Assert.AreEqual(0, viewList.Count, + "Views should be closed due to CloseForData returning true."); + } + mocks.VerifyAll(); + } + + [Test] + public void CloseAllViewsFor_DataIsNull_DoNothing() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo(), + }; + + var data1 = new A(); + var data2 = new InheritedFromA(); + + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + viewResolver.OpenViewForData(data1); + viewResolver.OpenViewForData(data2); + + // Precondition + Assert.AreEqual(2, viewList.Count); + + // Call + viewResolver.CloseAllViewsFor(null); + + // Assert + Assert.AreEqual(2, viewList.Count, + "No elements should have been removed."); + } + mocks.VerifyAll(); + } + + [Test] + public void CloseAllViewsFor_DataCorrespondsToOpenedView_RemoveThatView() + { + // Setup + var mocks = new MockRepository(); + var dockingManager = mocks.Stub(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + using (var viewList = new ViewList(dockingManager, ViewLocation.Document)) + { + var viewInfos = new ViewInfo[] + { + new ViewInfo(), + new ViewInfo(), + }; + + var data1 = new A(); + var data2 = new InheritedFromA(); + + var viewResolver = new ViewResolver(viewList, viewInfos, dialogParent); + viewResolver.OpenViewForData(data1); + viewResolver.OpenViewForData(data2); + + // Precondition + Assert.AreEqual(2, viewList.Count); + + // Call + viewResolver.CloseAllViewsFor(data1); + + // Assert + Assert.AreEqual(1, viewList.Count); + Assert.IsInstanceOf(viewList[0]); + Assert.AreSame(data2, viewList[0].Data); + } + mocks.VerifyAll(); + } + + private class A + { + + } + + private class InheritedFromA : A + { + + } } } \ No newline at end of file