// 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.HydraRing.Data;
using Ringtoets.HydraRing.IO.Properties;
namespace Ringtoets.HydraRing.IO
{
///
/// This class reads a SqLite database file and constructs instances from this database.
///
public class HydraulicBoundarySqLiteDatabaseReader : SqLiteDatabaseReaderBase, IRowBasedDatabaseReader
{
private SQLiteDataReader 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 .
/// - Preparing the queries to read from the database failed.
///
///
public HydraulicBoundarySqLiteDatabaseReader(string databaseFilePath)
: base(databaseFilePath)
{
InitializeReader();
}
///
/// Gets the total number of locations that can be read from the database.
///
public int Count { get; private set; }
///
/// Gets the version from the database.
///
public string Version { get; private set; }
///
/// Gets a value indicating whether or not more hydraulic boundary locations can
/// be read using the .
///
public bool HasNext { get; private set; }
///
/// Reads the next location from the database.
///
/// New instance of , based on the data read from the database or null if no data is available.
/// Thrown when the database returned incorrect values for required properties.
public HydraulicBoundaryLocation ReadLocation()
{
if (!HasNext)
{
return null;
}
try
{
return ReadHydraulicBoundaryLocation();
}
catch (InvalidCastException e)
{
var message = new FileReaderErrorMessageBuilder(Path).Build(Resources.HydraulicBoundaryDatabaseReader_Critical_Unexpected_value_on_column);
throw new CriticalFileReadException(message, e);
}
}
///
/// Disposes the reader.
///
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 = dataReader.Read() || (dataReader.NextResult() && dataReader.Read());
}
///
/// 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];
}
///
/// Reads the value in the column with name from the currently pointed row.
///
/// 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? ReadOrNull(string columnName) where T : struct
{
var valueObject = dataReader[columnName];
if (valueObject.Equals(DBNull.Value))
{
return null;
}
return (T) valueObject;
}
///
/// Reads the current row into a new instance of .
///
/// A new instance of , based upon the current row.
/// Thrown when the database returned incorrect values for required properties.
private HydraulicBoundaryLocation ReadHydraulicBoundaryLocation()
{
try
{
var id = Read(HydraulicBoundaryDatabaseColumns.LocationId);
var name = Read(HydraulicBoundaryDatabaseColumns.LocationName);
var x = Read(HydraulicBoundaryDatabaseColumns.LocationX);
var y = Read(HydraulicBoundaryDatabaseColumns.LocationY);
var designWaterLevel = "";
MoveNext();
return new HydraulicBoundaryLocation(id, name, x, y, designWaterLevel);
}
catch (InvalidCastException exception)
{
MoveNext();
throw;
}
}
///
/// Prepares a new data reader with queries for obtaining the locations and updates the reader
/// so that it points to the first row of the result set.
///
private void InitializeReader()
{
PrepareReader();
MoveNext();
}
///
/// Prepares the queries required for obtaining locations from the database.
///
private void PrepareReader()
{
var versionQuery = string.Format("SELECT (NameRegion || CreationDate) as {0} FROM General LIMIT 0,1;", HydraulicBoundaryDatabaseColumns.Version);
var countQuery = string.Format("SELECT count(*) as {0} FROM HRDLocations WHERE LocationTypeId > 1 ;", HydraulicBoundaryDatabaseColumns.LocationCount);
var locationsQuery = string.Format(
"SELECT HRDLocationId as {0}, Name as {1}, XCoordinate as {2}, YCoordinate as {3} FROM HRDLocations WHERE LocationTypeId > 1;",
HydraulicBoundaryDatabaseColumns.LocationId,
HydraulicBoundaryDatabaseColumns.LocationName,
HydraulicBoundaryDatabaseColumns.LocationX,
HydraulicBoundaryDatabaseColumns.LocationY);
CreateDataReader(string.Join(" ", versionQuery, countQuery, locationsQuery), new SQLiteParameter
{
DbType = DbType.String
});
}
///
/// Creates a new data reader to use in this class.
///
/// Thrown when
///
/// - Amount of locations in database could not be read.
/// - A query could not be executed on the database schema.
///
///
private void CreateDataReader(string queryString, params SQLiteParameter[] parameters)
{
using (var query = new SQLiteCommand(Connection)
{
CommandText = queryString
})
{
query.Parameters.AddRange(parameters);
try
{
dataReader = query.ExecuteReader();
GetVersion();
GetCount();
}
catch (SQLiteException exception)
{
Dispose();
var message = new FileReaderErrorMessageBuilder(Path).Build(Resources.Error_HydraulicBoundaryLocation_read_from_database);
throw new CriticalFileReadException(message, exception);
}
}
}
///
/// Gets the database version from the metadata table.
///
private void GetVersion()
{
if (dataReader.Read())
{
Version = Read(HydraulicBoundaryDatabaseColumns.Version);
}
dataReader.NextResult();
}
///
/// Gets the amount of locations that can be read from the database.
///
private void GetCount()
{
dataReader.Read();
Count = (int) Read(HydraulicBoundaryDatabaseColumns.LocationCount);
dataReader.NextResult();
}
}
}