// Copyright (C) Stichting Deltares 2016. 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 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 General Public License for more details. // // You should have received a copy of the GNU 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.ObjectModel; using System.IO; using System.Linq; using System.Text; using Application.Ringtoets.Migration.Core; using Application.Ringtoets.Migration.Properties; using Core.Common.Base.Storage; using Core.Common.Gui; using Core.Common.Gui.Settings; using Core.Common.Utils; using log4net; using Migration.Scripts.Data.Exceptions; using Ringtoets.Common.Utils; using MigrationCoreStorageResources = Migration.Core.Storage.Properties.Resources; namespace Application.Ringtoets.Migration { /// /// A GUI implementation to migrate a Ringtoets database file to a newer version. /// public class RingtoetsProjectMigrator : IMigrateProject { private static readonly string currentDatabaseVersion = RingtoetsVersionHelper.GetCurrentDatabaseVersion(); private readonly ILog log = LogManager.GetLogger(typeof(RingtoetsProjectMigrator)); private readonly RingtoetsSqLiteDatabaseFileMigrator fileMigrator; private readonly IInquiryHelper inquiryHelper; private readonly FileFilterGenerator fileFilter; private readonly string migrationLogPath; /// /// Instantiates a . /// /// Object responsible for inquiring the data. public RingtoetsProjectMigrator(IInquiryHelper inquiryHelper) { if (inquiryHelper == null) { throw new ArgumentNullException(nameof(inquiryHelper)); } migrationLogPath = Path.Combine(SettingsHelper.Instance.GetLocalUserTemporaryDirectory(), "RingtoetsMigrationLog.sqlite"); this.inquiryHelper = inquiryHelper; fileMigrator = new RingtoetsSqLiteDatabaseFileMigrator { LogPath = migrationLogPath }; fileFilter = new FileFilterGenerator(Resources.RingtoetsProject_FileExtension, Resources.RingtoetsProject_TypeDescription); } public MigrationRequired ShouldMigrate(string filePath) { if (filePath == null) { throw new ArgumentNullException(nameof(filePath)); } ValidateProjectPath(filePath, nameof(filePath), Resources.RingtoetsProjectMigrator_Source_Descriptor); var versionedFile = new RingtoetsVersionedFile(filePath); string version = versionedFile.GetVersion(); if (version.Equals(currentDatabaseVersion)) { return MigrationRequired.No; } if (!fileMigrator.IsVersionSupported(version)) { string errorMessage = string.Format(MigrationCoreStorageResources.Migrate_From_Version_0_To_Version_1_Not_Supported, version, currentDatabaseVersion); log.Error(errorMessage); return MigrationRequired.Aborted; } string query = string.Format(Resources.RingtoetsProjectMigrator_Migrate_Outdated_project_file_update_to_current_version_0_inquire, currentDatabaseVersion); if (inquiryHelper.InquireContinuation(query)) { return MigrationRequired.Yes; } GenerateMigrationCancelledLogMessage(filePath); return MigrationRequired.Aborted; } public string DetermineMigrationLocation(string originalFilePath) { if (originalFilePath == null) { throw new ArgumentNullException(nameof(originalFilePath)); } ValidateProjectPath(originalFilePath, nameof(originalFilePath), Resources.RingtoetsProjectMigrator_Source_Descriptor); string suggestedFileName = GetSuggestedFileName(originalFilePath); string migrationLocation = inquiryHelper.GetTargetFileLocation(fileFilter.Filter, suggestedFileName); if (migrationLocation == null) { GenerateMigrationCancelledLogMessage(originalFilePath); } return migrationLocation; } public bool Migrate(string sourceFilePath, string targetFilePath) { if (sourceFilePath == null) { throw new ArgumentNullException(nameof(sourceFilePath)); } if (targetFilePath == null) { throw new ArgumentNullException(nameof(targetFilePath)); } ValidateProjectPath(sourceFilePath, nameof(sourceFilePath), Resources.RingtoetsProjectMigrator_Source_Descriptor); ValidateProjectPath(targetFilePath, nameof(targetFilePath), Resources.RingtoetsProjectMigrator_Target_Descriptor); return MigrateToTargetLocation(sourceFilePath, targetFilePath); } private bool MigrateToTargetLocation(string sourceFilePath, string targetLocation) { try { if (!CreateInitializedDatabaseLogFile()) { return false; } var versionedFile = new RingtoetsVersionedFile(sourceFilePath); fileMigrator.Migrate(versionedFile, currentDatabaseVersion, targetLocation); string message = string.Format(Resources.RingtoetsProjectMigrator_MigrateToTargetLocation_Outdated_projectfile_0_succesfully_updated_to_target_filepath_1_version_2_, sourceFilePath, targetLocation, currentDatabaseVersion); log.Info(message); LogMigrationMessages(); return true; } catch (CriticalMigrationException e) { string errorMessage = string.Format(Resources.RingtoetsProjectMigrator_MigrateToTargetLocation_Updating_outdated_projectfile_0_failed_with_exception_1_, sourceFilePath, e.Message); log.Error(errorMessage, e); return false; } finally { TryCleanupDatabaseLogFile(); } } private void LogMigrationMessages() { using (var migrationLogDatabase = new MigrationLogDatabaseReader(migrationLogPath)) { ReadOnlyCollection migrationLogMessages = migrationLogDatabase.GetMigrationLogMessages(); if (!migrationLogMessages.Any()) { return; } var migrationLog = new StringBuilder(Resources.RingtoetsProjectMigrator_Project_file_modified_click_details_for_migration_report); foreach (MigrationLogMessage logMessage in migrationLogMessages) { migrationLog.AppendLine(string.Concat("- ", logMessage.Message)); } log.Info(migrationLog); } } private bool CreateInitializedDatabaseLogFile() { try { IOUtils.CreateFileIfNotExists(migrationLogPath); } catch (ArgumentException exception) { log.Error(string.Format(Resources.RingtoetsProjectMigrator_CreateInitializedDatabaseLogFile_Unable_to_create_migration_log_file_0, migrationLogPath), exception); return false; } return true; } private void TryCleanupDatabaseLogFile() { try { File.Delete(migrationLogPath); } catch (SystemException exception) when (exception is IOException || exception is UnauthorizedAccessException) { string errorMessage = string.Format(Resources.RingtoetsProjectMigrator_Deleting_migration_log_file_0_failed, migrationLogPath); log.Error(errorMessage, exception); } } private static string GetSuggestedFileName(string sourceFilePath) { string fileName = Path.GetFileNameWithoutExtension(sourceFilePath); string versionSuffix = currentDatabaseVersion.Replace(".", "-"); string suggestedFileName = $"{fileName}_{versionSuffix}"; return suggestedFileName; } private void GenerateMigrationCancelledLogMessage(string sourceFilePath) { string warningMessage = string.Format(Resources.RingtoetsProjectMigrator_GenerateMigrationCancelledLogMessage_Updating_projectfile_0_was_cancelled, sourceFilePath); log.Warn(warningMessage); } /// /// Validates a given file path. /// /// The file path to be validated. /// The name of the argument. /// Prefix, describing the type of file path is being validated. /// Thrown when the file path argument with the /// given name is not valid. private static void ValidateProjectPath(string filePath, string argumentName, string pathDescription) { if (!IOUtils.IsValidFilePath(filePath)) { string message = string.Format(Resources.RingtoetsProjectMigrator_ValidateProjectPath_TypeDescriptor_0_filepath_must_be_a_valid_path, pathDescription); throw new ArgumentException(message, argumentName); } } } }