// 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 stochastic soil model from this database.
///
public class StochasticSoilModelReader : SqLiteDatabaseReaderBase
{
private readonly Dictionary soilProfile1Ds = new Dictionary();
private readonly Dictionary soilProfile2Ds = new Dictionary();
private IDataReader dataReader;
private SegmentPointReader segmentPointReader;
private long currentStochasticSoilModelId = -1;
///
/// 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 StochasticSoilModelReader(string databaseFilePath) : base(databaseFilePath) {}
///
/// Gets a value indicating whether or not more stochastic soil models can be read using
/// the .
///
public bool HasNext { get; private set; }
///
/// Gets the amount of that can be read from the database.
///
public int StochasticSoilModelCount { get; private set; }
///
/// Validates the database.
///
/// Thrown when:
///
/// - The database version could not be read;
/// - The database version is incorrect;
/// - Required information for constraint evaluation could not be read;
/// - The database segment names are not unique;
/// - The query to fetch stochastic soil models from the database failed.
///
///
public void Validate()
{
VerifyVersion(Path);
VerifyConstraints(Path);
InitializeReaders();
InitializeLookups();
}
///
/// Reads the information for the next stochastic soil model from the database and creates a
/// instance of the information.
///
/// The next from the database, or null
/// if no more soil models can be read.
/// Thrown when the database returned incorrect
/// values for required properties.
/// Thrown when:
///
/// - No stochastic soil profiles could be read;
/// - The read failure mechanism type is not supported.
///
///
public StochasticSoilModel ReadStochasticSoilModel()
{
try
{
return TryReadStochasticSoilModel();
}
catch (SystemException exception) when (exception is FormatException ||
exception is OverflowException ||
exception is InvalidCastException)
{
string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.StochasticSoilProfileDatabaseReader_StochasticSoilProfile_has_invalid_value);
throw new CriticalFileReadException(message, exception);
}
}
public void MoveNext()
{
HasNext = MoveNext(dataReader);
}
protected override void Dispose(bool disposing)
{
if (dataReader != null)
{
dataReader.Close();
dataReader.Dispose();
dataReader = null;
}
if (segmentPointReader != null)
{
segmentPointReader.Dispose();
segmentPointReader = null;
}
base.Dispose(disposing);
}
private void InitializeLookups()
{
using (var soilProfile1DReader = new SoilProfile1DReader(Path))
{
soilProfile1DReader.Initialize();
while (soilProfile1DReader.HasNext)
{
try
{
SoilProfile1D soilProfile1D = soilProfile1DReader.ReadSoilProfile();
long soilProfileId = soilProfile1D.Id;
if (!soilProfile1Ds.ContainsKey(soilProfileId))
{
soilProfile1Ds.Add(soilProfileId, soilProfile1D);
}
}
catch (SoilProfileReadException)
{
soilProfile1DReader.MoveNext();
}
}
}
using (var soilProfile2DReader = new SoilProfile2DReader(Path))
{
soilProfile2DReader.Initialize();
while (soilProfile2DReader.HasNext)
{
try
{
SoilProfile2D soilProfile2D = soilProfile2DReader.ReadSoilProfile();
long soilProfileId = soilProfile2D.Id;
if (!soilProfile2Ds.ContainsKey(soilProfileId))
{
soilProfile2Ds.Add(soilProfileId, soilProfile2D);
}
}
catch (SoilProfileReadException)
{
soilProfile2DReader.MoveNext();
}
}
}
}
///
/// Creates a new from the data reader.
///
/// The next from the database, or null
/// if no more soil models can be read.
/// Thrown when:
///
/// - No stochastic soil profiles could be read;
/// - The geometry could not be read;
/// - The read failure mechanism type is not supported.
///
///
private StochasticSoilModel TryReadStochasticSoilModel()
{
if (!HasNext)
{
return null;
}
StochasticSoilModel stochasticSoilModel = CreateStochasticSoilModel();
currentStochasticSoilModelId = ReadStochasticSoilModelId();
SetGeometry(stochasticSoilModel);
SetStochasticSoilProfiles(stochasticSoilModel);
return stochasticSoilModel;
}
///
/// Sets objects that belong to soil model.
///
/// The stochastic soil model of which the profiles to set.
/// Thrown when:
///
/// - No stochastic soil profiles could be read;
/// - The read failure mechanism type is not supported.
///
///
private void SetStochasticSoilProfiles(StochasticSoilModel stochasticSoilModel)
{
if (MoveToStochasticSoilModel(currentStochasticSoilModelId))
{
stochasticSoilModel.StochasticSoilProfiles.AddRange(ReadStochasticSoilProfiles());
}
}
///
/// Sets the geometry points of from the database.
///
/// The stochastic soil model of which the geometry to set.
/// Thrown when the geometry could not be read.
private void SetGeometry(StochasticSoilModel stochasticSoilModel)
{
if (!segmentPointReader.MoveToStochasticSoilModel(currentStochasticSoilModelId))
{
throw new StochasticSoilModelException(
string.Format(Resources.SegmentPointReader_ReadSegmentPoint_StochasticSoilModel_0_must_contain_geometry,
ReadStochasticSoilModelName()));
}
stochasticSoilModel.Geometry.AddRange(segmentPointReader.ReadSegmentPoints());
}
///
/// Moves the reader to the stochastic soil model with id .
///
/// The id of the stochastic soil model.
/// true if the reader was moved to the stochastic soil model with id
/// successfully, false otherwise.
private bool MoveToStochasticSoilModel(long stochasticSoilModelId)
{
while (HasNext && ReadStochasticSoilModelId() <= stochasticSoilModelId)
{
if (ReadStochasticSoilModelId() == stochasticSoilModelId)
{
currentStochasticSoilModelId = stochasticSoilModelId;
return true;
}
MoveNext();
}
return false;
}
///
/// Reads and returns objects that belong to soil model.
///
/// The read stochastic soil profiles.
/// Thrown when:
///
/// - No stochastic soil profiles could be read;
/// - The read failure mechanism type is not supported.
///
///
private IEnumerable ReadStochasticSoilProfiles()
{
while (HasNext && ReadStochasticSoilModelId() == currentStochasticSoilModelId)
{
double? probability = ReadStochasticSoilProfileProbability();
if (!probability.HasValue)
{
string soilModelName = ReadStochasticSoilModelName();
throw new StochasticSoilModelException(
string.Format(Resources.StochasticSoilModelReader_ReadStochasticSoilProfiles_No_profiles_found_in_stochastic_soil_model_Name_0_,
soilModelName));
}
long? soilProfile1D = ReadSoilProfile1DId();
long? soilProfile2D = ReadSoilProfile2DId();
if (soilProfile1D.HasValue && soilProfile1Ds.ContainsKey(soilProfile1D.Value))
{
yield return new StochasticSoilProfile(probability.Value, soilProfile1Ds[soilProfile1D.Value]);
}
else if (soilProfile2D.HasValue && soilProfile2Ds.ContainsKey(soilProfile2D.Value))
{
yield return new StochasticSoilProfile(probability.Value, soilProfile2Ds[soilProfile2D.Value]);
}
MoveNext();
}
}
///
/// Creates a new basic , based on the data read from the data reader.
///
/// The newly created .
/// Thrown when the read failure mechanism
/// type is not supported.
private StochasticSoilModel CreateStochasticSoilModel()
{
return new StochasticSoilModel(ReadStochasticSoilModelName(), ReadFailureMechanismType());
}
///
/// Initializes a new .
///
/// Thrown when failed to fetch stochastic soil models from the database.
private void InitializeReaders()
{
CreateDataReader();
MoveNext();
segmentPointReader = new SegmentPointReader(Path);
segmentPointReader.Initialize();
}
///
/// Creates a new .
///
/// Thrown when query to fetch stochastic soil
/// models from the database failed.
private void CreateDataReader()
{
string stochasticSoilModelCount = SoilDatabaseQueryBuilder.GetStochasticSoilModelOfMechanismCountQuery();
string stochasticSoilModelSegmentsQuery = SoilDatabaseQueryBuilder.GetStochasticSoilModelPerMechanismQuery();
try
{
dataReader = CreateDataReader(stochasticSoilModelCount + stochasticSoilModelSegmentsQuery);
}
catch (SQLiteException exception)
{
string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.StochasticSoilModelDatabaseReader_Failed_to_read_database);
throw new CriticalFileReadException(message, exception);
}
StochasticSoilModelCount = ReadStochasticSoilModelCount();
dataReader.NextResult();
}
///
/// Verifies that the database at has the required version.
///
/// The path of the database file to open.
/// Thrown when:
///
/// - The database version could not be read;
/// - The database version is incorrect.
///
///
private static void VerifyVersion(string databaseFilePath)
{
using (var reader = new SoilDatabaseVersionReader(databaseFilePath))
{
reader.VerifyVersion();
}
}
///
/// Verifies that the database at meets required constraints.
///
/// The path of the database file to open.
/// Thrown when:
///
/// - Required information for constraint evaluation could not be read;
/// - The database segment names are not unique.
///
///
private static void VerifyConstraints(string databaseFilePath)
{
using (var reader = new SoilDatabaseConstraintsReader(databaseFilePath))
{
reader.VerifyConstraints();
}
}
#region Read columns
///
/// Reads the stochastic soil profile probability from the data reader.
///
/// The 1D soil profile id.
/// The read value is not in an appropriate format.
/// The read value represents a number that is less
/// than or greater than .
private double? ReadStochasticSoilProfileProbability()
{
object probability = dataReader[StochasticSoilProfileTableDefinitions.Probability];
return probability == Convert.DBNull
? (double?) null
: Convert.ToDouble(probability);
}
///
/// Reads the 1D soil profile id from the data reader.
///
/// The 1D soil profile id.
/// The read value is not in an appropriate format.
/// The read value represents a number that is less
/// than or greater than .
private long? ReadSoilProfile1DId()
{
object soilProfileId = dataReader[StochasticSoilProfileTableDefinitions.SoilProfile1DId];
return soilProfileId == Convert.DBNull
? (long?) null
: Convert.ToInt64(soilProfileId);
}
///
/// Reads the 2D soil profile id from the data reader.
///
/// The 2D soil profile id.
/// The read value is not in an appropriate format.
/// The read value represents a number that is less
/// than or greater than .
private long? ReadSoilProfile2DId()
{
object soilProfileId = dataReader[StochasticSoilProfileTableDefinitions.SoilProfile2DId];
return soilProfileId == Convert.DBNull
? (long?) null
: Convert.ToInt64(soilProfileId);
}
private int ReadStochasticSoilModelCount()
{
return !dataReader.Read() ? 0 : Convert.ToInt32(dataReader[StochasticSoilModelTableDefinitions.Count]);
}
///
/// Reads the stochastic soil model id from the data reader.
///
/// The stochastic soil model id.
/// The read value is not in an appropriate format.
/// The read value represents a number that is less
/// than or greater than .
private long ReadStochasticSoilModelId()
{
return Convert.ToInt64(dataReader[StochasticSoilModelTableDefinitions.StochasticSoilModelId]);
}
private string ReadStochasticSoilModelName()
{
return Convert.ToString(dataReader[StochasticSoilModelTableDefinitions.StochasticSoilModelName]);
}
private string ReadMechanismName()
{
return Convert.ToString(dataReader[MechanismTableDefinitions.MechanismName]);
}
///
/// Reads the failure mechanism type from the data reader.
///
/// The failure mechanism type.
/// Thrown when the read failure mechanism type is not supported.
private FailureMechanismType ReadFailureMechanismType()
{
long mechanismId = Convert.ToInt64(dataReader[MechanismTableDefinitions.MechanismId]);
if (Enum.IsDefined(typeof(FailureMechanismType), mechanismId))
{
return (FailureMechanismType) mechanismId;
}
string message = string.Format(Resources.StochasticSoilModelReader_ReadFailureMechanismType_Failure_mechanism_0_not_supported, ReadMechanismName());
throw new StochasticSoilModelException(message);
}
#endregion
}
}