// 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.Data;
using System.Data.SQLite;
using Core.Common.IO.Exceptions;
using Core.Common.IO.Readers;
using Core.Common.Utils.Builders;
using Ringtoets.Piping.IO.Builders;
using Ringtoets.Piping.IO.Exceptions;
using Ringtoets.Piping.IO.Properties;
using Ringtoets.Piping.Primitives;
using UtilsResources = Core.Common.Utils.Properties.Resources;
namespace Ringtoets.Piping.IO.SoilProfile
{
///
/// This class reads a SqLite database file and constructs instances from this database.
///
public class PipingSoilProfileReader : SqLiteDatabaseReaderBase, IRowBasedDatabaseReader
{
private const string pipingMechanismName = "Piping";
private const string mechanismParameterName = "mechanism";
private SQLiteDataReader dataReader;
///
/// Creates a new instance of which will use the
/// as its source. The reader will not point to any record at the start. Use to start reading
/// profiles.
///
/// The path of the database file to open.
/// Thrown when:
///
/// - The contains invalid characters.
/// - No file could be found at .
/// - Preparing the queries to read from the database failed.
///
///
public PipingSoilProfileReader(string databaseFilePath) : base(databaseFilePath)
{
VerifyVersion(databaseFilePath);
InitializeReader();
}
///
/// Gets the total number of profiles that can be read from the database.
///
public int Count { get; private set; }
///
/// Gets a value indicating whether or not more soil profiles can be read using
/// the .
///
public bool HasNext { get; private set; }
///
/// Reads the information for the next profile from the database and creates a
/// instance of the information.
///
/// The next from the database, or null if no more profiles can be read.
/// Thrown when reading the profile in the database contained a non-parsable geometry.
/// Thrown when the database returned incorrect values for required properties.
public PipingSoilProfile ReadProfile()
{
if (!HasNext)
{
return null;
}
try
{
return ReadPipingSoilProfile();
}
catch (InvalidCastException e)
{
var message = new FileReaderErrorMessageBuilder(Path).Build(Resources.PipingSoilProfileReader_Critical_Unexpected_value_on_column);
throw new CriticalFileReadException(message, e);
}
}
public override void Dispose()
{
if (dataReader != null)
{
dataReader.Dispose();
}
base.Dispose();
}
///
/// Moves the reader to the next record in the database.
///
public void MoveNext()
{
HasNext = MoveNext(dataReader);
}
///
/// Reads the value in the column with name from the
/// current row that's being pointed at.
///
/// The type of object to read.
/// The name of the column to read from.
/// The value in the column, or null if the value was .
/// Thrown when the value in the column could not be casted to type .
public T ReadOrDefault(string columnName)
{
var valueObject = dataReader[columnName];
if (valueObject.Equals(DBNull.Value))
{
return default(T);
}
return (T)valueObject;
}
///
/// Reads a value at column from the database.
///
/// The expected type of value in the column with name .
/// The name of the column to read from.
/// The read value from the column with name .
/// Thrown when the value in the column was not of type .
public T Read(string columnName)
{
return (T)dataReader[columnName];
}
private void VerifyVersion(string databaseFilePath)
{
using (var versionReader = new SoilDatabaseVersionReader(databaseFilePath))
{
try
{
versionReader.VerifyVersion();
}
catch (CriticalFileReadException)
{
CloseConnection();
throw;
}
}
}
///
/// Reads a from the database.
///
/// A new .
/// Thrown when a recoverable error occurred while reading from the database.
/// Thrown when recovering from the failed.
private PipingSoilProfile ReadPipingSoilProfile()
{
try
{
var dimensionValue = Read(SoilProfileDatabaseColumns.Dimension);
return dimensionValue == 1 ? SoilProfile1DReader.ReadFrom(this) : SoilProfile2DReader.ReadFrom(this);
}
catch (PipingSoilProfileReadException e)
{
MoveToNextProfile(e.ProfileName);
throw;
}
}
///
/// Steps through the result rows until a row is read which' profile name differs from .
///
/// The name of the profile to skip.
private void MoveToNextProfile(string profileName)
{
while (HasNext && Read(SoilProfileDatabaseColumns.ProfileName).Equals(profileName))
{
MoveNext();
}
}
///
/// Prepares a new data reader with queries for obtaining the profiles and updates the reader
/// so that it points to the first row of the result set.
///
/// Thrown when the amount of profiles in database could not be read.
/// A query could not be executed on the database schema.
private void InitializeReader()
{
try
{
PrepareReader();
GetCount();
}
catch (SQLiteException e)
{
Dispose();
var message = new FileReaderErrorMessageBuilder(Path).Build(Resources.Error_SoilProfile_read_from_database);
throw new CriticalFileReadException(message, e);
}
MoveNext();
}
///
/// Prepares the two queries required for obtaining all the SoilProfile1D and SoilProfile2D with an x defined
/// to take an intersection from. Since two separate queries are used, the will
/// have two result sets which the method takes into account.
///
/// A query could not be executed on the database schema.
private void PrepareReader()
{
string countQuery = SoilDatabaseQueryBuilder.GetPipingSoilProfileCountQuery();
string subQueryGetNumberOfLayerProfile1D =
string.Format("SELECT SP1D_ID, COUNT(*) as {0} " +
"FROM SoilLayer1D " +
"GROUP BY SP1D_ID",
SoilProfileDatabaseColumns.LayerCount);
string subQueryGetNumberOfLayerProfile2D =
string.Format("SELECT SP2D_ID, COUNT(*) as {0} " +
"FROM SoilLayer2D " +
"GROUP BY SP2D_ID",
SoilProfileDatabaseColumns.LayerCount);
string subQueryGetMaterialPropertiesOfLayer =
String.Format(
"SELECT " +
"mat.MA_ID, " +
"mat.MA_Name as {4}, " +
"MAX(case when pn.PN_NAME = '{0}' then pv.PV_Value end) {0}, " +
"MAX(case when pn.PN_NAME = '{1}' then pv.PV_Value end) {1}, " +
"MAX(case when pn.PN_NAME = '{2}' then pv.PV_Value end) {2}, " +
"MAX(case when pn.PN_NAME = '{3}' then pv.PV_Value end) {3} " +
"FROM ParameterNames as pn " +
"JOIN ParameterValues as pv USING(PN_ID) " +
"JOIN Materials as mat USING(MA_ID) " +
"GROUP BY mat.MA_ID",
SoilProfileDatabaseColumns.AbovePhreaticLevel,
SoilProfileDatabaseColumns.BelowPhreaticLevel,
SoilProfileDatabaseColumns.DryUnitWeight,
SoilProfileDatabaseColumns.Color,
SoilProfileDatabaseColumns.MaterialName);
string subQueryGetLayerPropertiesOfLayer1D =
String.Format(
"SELECT " +
"SL1D_ID, " +
"MAX(case when pn.PN_NAME = '{0}' then pv.PV_Value end) {0} " +
"FROM ParameterNames as pn " +
"JOIN LayerParameterValues as pv USING(PN_ID) " +
"GROUP BY pv.SL1D_ID",
SoilProfileDatabaseColumns.IsAquifer);
string subQueryGetLayerPropertiesOfLayer2D =
String.Format(
"SELECT " +
"SL2D_ID, " +
"MAX(case when pn.PN_NAME = '{0}' then pv.PV_Value end) {0} " +
"FROM ParameterNames as pn " +
"JOIN LayerParameterValues as pv USING(PN_ID) " +
"GROUP BY pv.SL2D_ID",
SoilProfileDatabaseColumns.IsAquifer);
var query1D = string.Format(
"SELECT " +
"1 AS {0}, " +
"sp1d.SP1D_Name AS {1}, " +
"layerCount.{2}, " +
"sp1d.BottomLevel AS {3}, " +
"sl1d.TopLevel AS {4}, " +
"{5}, " +
"{6}, " +
"{7}, " +
"{8}, " +
"{9}, " +
"{10}, " +
"sp1d.SP1D_ID AS {11} " +
"FROM Mechanism AS m " +
"JOIN Segment AS segment USING(ME_ID) " +
"JOIN StochasticSoilProfile ssp USING(SSM_ID) " +
"JOIN SoilProfile1D sp1d USING (SP1D_ID) " +
"JOIN (" +
subQueryGetNumberOfLayerProfile1D +
") layerCount USING (SP1D_ID) " +
"JOIN SoilLayer1D sl1d USING (SP1D_ID) " +
"LEFT JOIN (" +
subQueryGetMaterialPropertiesOfLayer +
") materialProperties USING(MA_ID) " +
"LEFT JOIN (" +
subQueryGetLayerPropertiesOfLayer1D +
") layerProperties USING(SL1D_ID) " +
"WHERE m.{12} = @{12};",
SoilProfileDatabaseColumns.Dimension,
SoilProfileDatabaseColumns.ProfileName,
SoilProfileDatabaseColumns.LayerCount,
SoilProfileDatabaseColumns.Bottom,
SoilProfileDatabaseColumns.Top,
SoilProfileDatabaseColumns.MaterialName,
SoilProfileDatabaseColumns.AbovePhreaticLevel,
SoilProfileDatabaseColumns.BelowPhreaticLevel,
SoilProfileDatabaseColumns.DryUnitWeight,
SoilProfileDatabaseColumns.IsAquifer,
SoilProfileDatabaseColumns.Color,
SoilProfileDatabaseColumns.SoilProfileId,
MechanismDatabaseColumns.MechanismName);
var query2D = string.Format(
"Select " +
"2 as {0}, " +
"sp2d.SP2D_Name as {1}, " +
"layerCount.{2}, " +
"sl2d.GeometrySurface as {3}, " +
"mpl.X as {4}, " +
"{5}, " +
"{6}, " +
"{7}, " +
"{8}, " +
"{9}, " +
"{10}, " +
"sp2d.SP2D_ID as {11} " +
"FROM Mechanism AS m " +
"JOIN Segment AS segment USING(ME_ID) " +
"JOIN StochasticSoilProfile ssp USING(SSM_ID) " +
"JOIN SoilProfile2D sp2d USING (SP2D_ID) " +
"JOIN (" +
subQueryGetNumberOfLayerProfile2D +
") layerCount USING (SP2D_ID) " +
"JOIN SoilLayer2D sl2d USING (SP2D_ID) " +
"JOIN MechanismPointLocation mpl USING(ME_ID, SP2D_ID) " +
"LEFT JOIN (" +
subQueryGetMaterialPropertiesOfLayer +
") materialProperties USING(MA_ID) " +
"LEFT JOIN (" +
subQueryGetLayerPropertiesOfLayer2D +
") layerProperties USING(SL2D_ID) " +
"WHERE m.{12} = @{12};",
SoilProfileDatabaseColumns.Dimension,
SoilProfileDatabaseColumns.ProfileName,
SoilProfileDatabaseColumns.LayerCount,
SoilProfileDatabaseColumns.LayerGeometry,
SoilProfileDatabaseColumns.IntersectionX,
SoilProfileDatabaseColumns.MaterialName,
SoilProfileDatabaseColumns.AbovePhreaticLevel,
SoilProfileDatabaseColumns.BelowPhreaticLevel,
SoilProfileDatabaseColumns.DryUnitWeight,
SoilProfileDatabaseColumns.IsAquifer,
SoilProfileDatabaseColumns.Color,
SoilProfileDatabaseColumns.SoilProfileId,
MechanismDatabaseColumns.MechanismName);
dataReader = CreateDataReader(countQuery + query2D + query1D, new SQLiteParameter
{
DbType = DbType.String,
Value = pipingMechanismName,
ParameterName = mechanismParameterName
}, new SQLiteParameter
{
DbType = DbType.String,
ParameterName = String.Format("@{0}", MechanismDatabaseColumns.MechanismName),
Value = pipingMechanismName
});
}
private void GetCount()
{
dataReader.Read();
Count = (int)Read(SoilProfileDatabaseColumns.ProfileCount);
dataReader.NextResult();
}
}
}