Index: Core/Common/src/Core.Common.Gui/Commands/OpenProjectCommand.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Gui/Commands/OpenProjectCommand.cs (revision 0) +++ Core/Common/src/Core.Common.Gui/Commands/OpenProjectCommand.cs (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -0,0 +1,106 @@ +using System; +using System.IO; + +using Core.Common.Base.Data; +using Core.Common.Base.Storage; +using Core.Common.Gui.Properties; + +using log4net; + +namespace Core.Common.Gui.Commands +{ + /// + /// Opens an existing project file from hard disk and loads that into the application. + /// + public class OpenProjectCommand : IGuiCommand + { + private static readonly ILog Log = LogManager.GetLogger(typeof(GuiCommandHandler)); + + /// + /// Initializes a new instance of the class. + /// + public OpenProjectCommand() + { + Enabled = true; + Checked = false; + } + + /// + /// Gets a value indicating whether a has successfully been loaded. + /// + public bool LoadWasSuccesful { get; private set; } + + public bool Enabled { get; private set; } + public bool Checked { get; private set; } + + public IGui Gui { get; set; } + + /// + /// Start the operation of opening a specified project file. + /// + /// The file path of the project to open. + public void Execute(string filePath) + { + Execute(new object[] + { + filePath + }); + } + + public void Execute(params object[] arguments) + { + LoadWasSuccesful = false; + Log.Info(Resources.Project_existing_opening_project); + + var filePath = (string)arguments[0]; + Project loadedProject = TryReadProjectFromPath(filePath); + if (loadedProject == null) + { + Log.Warn(Resources.Project_existing_project_opening_failed); + return; + } + + // Project loaded successfully, close current project + if (Gui.Project != null) + { + // remove views before closing project. + Gui.CommandHandler.RemoveAllViewsForItem(Gui.Project); + } + + Gui.ProjectFilePath = filePath; + Gui.Project = loadedProject; + Gui.Project.Name = Path.GetFileNameWithoutExtension(filePath); + + // Set the gui selection to the current project + Gui.Selection = Gui.Project; + + // Update the window title + Gui.UpdateTitle(); + + LoadWasSuccesful = true; + Log.Info(Resources.Project_existing_successfully_opened); + } + + private Project TryReadProjectFromPath(string filePath) + { + IStoreProject storage = Gui.Storage; + try + { + return storage.LoadProject(filePath); + } + catch (ArgumentException e) + { + Log.Warn(e.Message); + } + catch (CouldNotConnectException e) + { + Log.Warn(e.Message); + } + catch (StorageValidationException e) + { + Log.Warn(e.Message); + } + return null; + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj =================================================================== diff -u -r30b1d22e83a751ea0ac8a5924db199b67c36a623 -r173fe221a98b3d8dfb9e0aeee0d93504ad899730 --- Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj (.../Core.Common.Gui.csproj) (revision 30b1d22e83a751ea0ac8a5924db199b67c36a623) +++ Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj (.../Core.Common.Gui.csproj) (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -120,6 +120,7 @@ + Form Index: Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs =================================================================== diff -u -r30b1d22e83a751ea0ac8a5924db199b67c36a623 -r173fe221a98b3d8dfb9e0aeee0d93504ad899730 --- Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision 30b1d22e83a751ea0ac8a5924db199b67c36a623) +++ Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -63,6 +63,7 @@ private IEnumerable ribbonCommandHandlers; private readonly CreateNewProjectCommand newProjectCommand; + private readonly OpenProjectCommand openProjectCommand; /// /// This is used when user selects non-contextual tab explicitly. Then we won't activate contextual tab on the next view activation. @@ -85,6 +86,7 @@ windowInteropHelper = new WindowInteropHelper(this); newProjectCommand = new CreateNewProjectCommand { Gui = gui }; + openProjectCommand = new OpenProjectCommand { Gui = gui }; log.Info(Properties.Resources.MainWindow_MainWindow_Main_window_created_); } @@ -536,7 +538,7 @@ { try { - Gui.CommandHandler.OpenExistingProject(path); + openProjectCommand.Execute(path); RecentProjectsTabControl.Items.Remove(newItem); RecentProjectsTabControl.Items.Insert(1, newItem); } Index: Core/Common/src/Core.Common.Gui/GuiCommandHandler.cs =================================================================== diff -u -r30b1d22e83a751ea0ac8a5924db199b67c36a623 -r173fe221a98b3d8dfb9e0aeee0d93504ad899730 --- Core/Common/src/Core.Common.Gui/GuiCommandHandler.cs (.../GuiCommandHandler.cs) (revision 30b1d22e83a751ea0ac8a5924db199b67c36a623) +++ Core/Common/src/Core.Common.Gui/GuiCommandHandler.cs (.../GuiCommandHandler.cs) (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -68,7 +68,7 @@ /// /// Location of the storage file. /// true if an existing has been loaded, false otherwise. - public bool OpenExistingProject(string filePath) + private bool OpenExistingProject(string filePath) { Log.Info(Resources.Project_existing_opening_project); Index: Core/Common/src/Core.Common.Gui/IGuiCommandHandler.cs =================================================================== diff -u -r30b1d22e83a751ea0ac8a5924db199b67c36a623 -r173fe221a98b3d8dfb9e0aeee0d93504ad899730 --- Core/Common/src/Core.Common.Gui/IGuiCommandHandler.cs (.../IGuiCommandHandler.cs) (revision 30b1d22e83a751ea0ac8a5924db199b67c36a623) +++ Core/Common/src/Core.Common.Gui/IGuiCommandHandler.cs (.../IGuiCommandHandler.cs) (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -25,16 +25,6 @@ bool OpenExistingProject(); /// - /// Opens an existing project from file. - /// - /// The path to the existing project file. - /// - /// The opening action might be cancelled (due to user interaction). - /// - /// Whether or not an existing project was correctly opened. - bool OpenExistingProject(string filePath); - - /// /// Closes the current project. /// void CloseProject(); Index: Core/Common/test/Core.Common.Gui.Test/Commands/OpenProjectCommandTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Gui.Test/Commands/OpenProjectCommandTest.cs (revision 0) +++ Core/Common/test/Core.Common.Gui.Test/Commands/OpenProjectCommandTest.cs (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -0,0 +1,179 @@ +using System; + +using Core.Common.Base.Data; +using Core.Common.Base.Storage; +using Core.Common.Gui.Commands; +using Core.Common.TestUtil; + +using NUnit.Framework; + +using Rhino.Mocks; + +namespace Core.Common.Gui.Test.Commands +{ + [TestFixture] + public class OpenProjectCommandTest + { + [Test] + public void DefaultConstructor_ExpectedValues() + { + // Call + var command = new OpenProjectCommand(); + + // Assert + Assert.IsInstanceOf(command); + Assert.IsTrue(command.Enabled); + Assert.IsFalse(command.Checked); + Assert.IsFalse(command.LoadWasSuccesful); + Assert.IsNull(command.Gui); + } + + [Test] + public void Execute_ValidFilePath_SetProjectAndReturnLoadWasSuccesfulTrue() + { + // Setup + var expectedProject = new Project(); + const string path = "cool file.bro"; + + var mocks = new MockRepository(); + var storageMock = mocks.StrictMock(); + storageMock.Expect(s => s.LoadProject(path)).Return(expectedProject); + + var guiMock = mocks.Stub(); + guiMock.Project = null; + guiMock.Stub(g => g.Storage).Return(storageMock); + guiMock.Expect(g => g.UpdateTitle()); + mocks.ReplayAll(); + + var command = new OpenProjectCommand + { + Gui = guiMock + }; + + // Call + Action call = () => command.Execute(path); + + // Assert + var expectedMessages = new[] + { + "Openen van bestaand Ringtoets project.", + "Bestaand Ringtoets project succesvol geopend." + }; + TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages, 2); + Assert.AreSame(expectedProject, guiMock.Project); + Assert.AreEqual("cool file", expectedProject.Name); + Assert.AreEqual(path, guiMock.ProjectFilePath); + Assert.AreSame(expectedProject, guiMock.Selection); + Assert.True(command.LoadWasSuccesful); + } + + [Test] + public void Execute_ValidFilePathAndAlreadyHasProject_CloseViewsOldProjectAndSetProjectAndReturnLoadWasSuccesfulTrue() + { + // Setup + var originalProject = new Project(); + var expectedProject = new Project(); + const string path = "cool file.bro"; + + var mocks = new MockRepository(); + var storageMock = mocks.StrictMock(); + storageMock.Expect(s => s.LoadProject(path)).Return(expectedProject); + + var commandHandlerMock = mocks.StrictMock(); + commandHandlerMock.Expect(ch => ch.RemoveAllViewsForItem(originalProject)); + + var guiMock = mocks.Stub(); + guiMock.Project = originalProject; + guiMock.Stub(g => g.Storage).Return(storageMock); + guiMock.Expect(g => g.UpdateTitle()); + guiMock.CommandHandler = commandHandlerMock; + mocks.ReplayAll(); + + var command = new OpenProjectCommand + { + Gui = guiMock + }; + + // Call + command.Execute(path); + + // Assert + Assert.AreSame(expectedProject, guiMock.Project); + Assert.AreEqual("cool file", expectedProject.Name); + Assert.AreEqual(path, guiMock.ProjectFilePath); + Assert.AreSame(expectedProject, guiMock.Selection); + Assert.True(command.LoadWasSuccesful); + } + + [Test] + public void Execute_StorageThrowsArgumentException_DoNotAffectProjectAndViewAndLogError() + { + ExecuteOpenProjectCommandAndThrowExceptionDuringLoad(new ArgumentException("")); + } + + [Test] + public void Execute_StorageThrowsCouldNotConnectException_DoNotAffectProjectAndViewAndLogError() + { + ExecuteOpenProjectCommandAndThrowExceptionDuringLoad(new CouldNotConnectException("")); + } + + [Test] + public void Execute_StorageThrowsStorageValidationException_DoNotAffectProjectAndViewAndLogError() + { + ExecuteOpenProjectCommandAndThrowExceptionDuringLoad(new StorageValidationException("")); + } + + private static void ExecuteOpenProjectCommandAndThrowExceptionDuringLoad(Exception exception) + { + // Setup + const string originalName = "original name"; + const string originalPath = originalName + ".tof"; + + var originalProject = new Project + { + Name = originalName + }; + const string path = "cool file.bro"; + string errorMessage = exception.Message; + + var mocks = new MockRepository(); + var storageMock = mocks.StrictMock(); + storageMock.Expect(s => s.LoadProject(path)).Throw(exception); + + var commandHandlerMock = mocks.StrictMock(); + commandHandlerMock.Expect(ch => ch.RemoveAllViewsForItem(originalProject)); + + var guiMock = mocks.Stub(); + guiMock.Project = originalProject; + guiMock.Stub(g => g.Storage).Return(storageMock); + guiMock.Expect(g => g.UpdateTitle()); + guiMock.CommandHandler = commandHandlerMock; + guiMock.ProjectFilePath = originalPath; + mocks.ReplayAll(); + + var command = new OpenProjectCommand + { + Gui = guiMock + }; + + // Call + Action call = () => command.Execute(path); + + // Assert + var expectedMessages = new[] + { + "Openen van bestaand Ringtoets project.", + errorMessage, + "Het is niet gelukt om het Ringtoets project te laden." + }; + TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages, 3); + Assert.AreSame(originalProject, guiMock.Project, + "Original project should still exist."); + Assert.AreEqual(originalName, originalProject.Name, + "Name should not be updated"); + Assert.AreEqual(originalPath, guiMock.ProjectFilePath, + "Project file path should remain unchanged."); + Assert.False(command.LoadWasSuccesful); + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj =================================================================== diff -u -r30b1d22e83a751ea0ac8a5924db199b67c36a623 -r173fe221a98b3d8dfb9e0aeee0d93504ad899730 --- Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision 30b1d22e83a751ea0ac8a5924db199b67c36a623) +++ Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision 173fe221a98b3d8dfb9e0aeee0d93504ad899730) @@ -57,6 +57,7 @@ +