// Copyright (C) Stichting Deltares 2017. 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.Generic; using System.Data; using System.Data.SQLite; using Core.Common.Base.IO; using Core.Common.IO.Readers; using Core.Common.Utils.Builders; using Ringtoets.Common.IO.Exceptions; using Ringtoets.Common.IO.Properties; using Ringtoets.Common.IO.SoilProfile.Schema; namespace Ringtoets.Common.IO.SoilProfile { /// /// This class reads a D-Soil Model file and reads 1d profiles from this database. /// public class SoilProfile1DReader : SqLiteDatabaseReaderBase, IRowBasedDatabaseReader { private IDataReader dataReader; /// /// Creates a new instance of , which will use the /// as its source. /// /// The path of the database file to open. /// Thrown when: /// /// The contains invalid characters. /// No file could be found at . /// /// public SoilProfile1DReader(string databaseFilePath) : base(databaseFilePath) {} /// /// Gets a value indicating whether or not more soil profiles can be read using /// the . /// public bool HasNext { get; private set; } /// /// Initializes the database reader. /// public void Initialize() { PrepareReader(); MoveNext(); } /// /// Reads the information for the next soil profile from the database and creates a /// instance of the information. /// /// The next from the database, or null /// if no more soil profile can be read. /// Thrown when reading properties of the profile failed. /// Thrown when the database returned incorrect /// values for required properties. public SoilProfile1D ReadSoilProfile() { try { SoilProfile1D soilProfile = TryReadSoilProfile(); return soilProfile; } catch (SystemException exception) when (exception is FormatException || exception is OverflowException || exception is InvalidCastException) { string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.SoilProfileReader_Error_reading_soil_profile_from_database); throw new CriticalFileReadException(message, exception); } } public void MoveNext() { HasNext = MoveNext(dataReader); } public T Read(string columnName) { return (T) dataReader[columnName]; } public T ReadOrDefault(string columnName) { object valueObject = dataReader[columnName]; if (valueObject.Equals(DBNull.Value)) { return default(T); } return (T) valueObject; } protected override void Dispose(bool disposing) { if (dataReader != null) { dataReader.Close(); dataReader.Dispose(); dataReader = null; } base.Dispose(disposing); } /// /// Steps through the result rows until a row is read which' profile id differs from . /// /// The id of the profile to skip. private void MoveToNextProfile(long soilProfileId) { while (HasNext && Read(SoilProfileTableDefinitions.SoilProfileId).Equals(soilProfileId)) { MoveNext(); } } /// /// Tries to read and create a . /// /// The read . /// Thrown when encountering an unrecoverable error /// while reading the profile. /// Thrown when reading properties of the profile /// failed. private SoilProfile1D TryReadSoilProfile() { var criticalProperties = new CriticalProfileProperties(this); var soilLayers = new List(); RequiredProfileProperties properties; try { properties = new RequiredProfileProperties(this, criticalProperties.ProfileName); for (var i = 1; i <= criticalProperties.LayerCount; i++) { soilLayers.Add(ReadSoilLayerFrom(this, criticalProperties.ProfileName)); MoveNext(); } } catch (SoilProfileReadException) { MoveToNextProfile(criticalProperties.ProfileId); throw; } try { return new SoilProfile1D(criticalProperties.ProfileId, criticalProperties.ProfileName, properties.Bottom, soilLayers); } catch (ArgumentException exception) { MoveToNextProfile(criticalProperties.ProfileId); throw new SoilProfileReadException( Resources.SoilProfile1DReader_ReadSoilProfile_Failed_to_construct_profile_from_read_data, criticalProperties.ProfileName, exception); } } private void PrepareReader() { string soilProfile1DQuery = SoilDatabaseQueryBuilder.GetSoilProfile1DQuery(); try { dataReader = CreateDataReader(soilProfile1DQuery); } catch (SQLiteException exception) { string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.SoilProfileReader_Error_reading_soil_profile_from_database); throw new CriticalFileReadException(message, exception); } } /// /// Reads a from the given . /// /// Thrown when reading properties of the layers failed. private static SoilLayer1D ReadSoilLayerFrom(IRowBasedDatabaseReader reader, string profileName) { var properties = new Layer1DProperties(reader, profileName); var soilLayer = new SoilLayer1D(properties.Top) { MaterialName = properties.MaterialName ?? string.Empty }; SoilLayerHelper.SetSoilLayerBaseProperties(soilLayer, properties); return soilLayer; } private class Layer1DProperties : LayerProperties { /// /// Creates a new instance of which contains properties /// that are required to create a complete . If these properties /// cannot be read, then the reader can proceed to the next profile. /// /// The to read the required layer property values from. /// The profile name used in generating exceptions messages if casting failed. /// Thrown when the values in the database could not be /// casted to the expected column types. internal Layer1DProperties(IRowBasedDatabaseReader reader, string profileName) : base(reader, profileName) { const string readColumn = SoilProfileTableDefinitions.Top; try { Top = reader.Read(readColumn); } catch (InvalidCastException e) { string message = string.Format(Resources.SoilProfileReader_Profile_Name_0_has_invalid_value_on_Column_1, profileName, readColumn); throw new SoilProfileReadException(message, profileName, e); } } /// /// Gets the top level of the 1D soil layer. /// public double Top { get; } } private class RequiredProfileProperties { /// /// Creates a new instance of which contains properties /// that are required to create a complete . If these properties /// cannot be read, then the reader can proceed to the next profile. /// /// The to read the required /// profile property values from. /// The profile name used in generating exceptions messages /// if casting failed. /// Thrown when the values in the database could not be /// casted to the expected column types. internal RequiredProfileProperties(IRowBasedDatabaseReader reader, string profileName) { try { Bottom = reader.Read(SoilProfileTableDefinitions.Bottom); } catch (InvalidCastException e) { string message = string.Format(Resources.SoilProfileReader_Profile_Name_0_has_invalid_value_on_Column_1, profileName, SoilProfileTableDefinitions.Bottom); throw new SoilProfileReadException(message, profileName, e); } } /// /// The bottom of the profile. /// public double Bottom { get; } } } }