// 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; }
}
}
}