Index: Ringtoets/Piping/src/Ringtoets.Piping.IO/PipingSoilProfileReader.cs =================================================================== diff -u -ra483049c20ef6d26addd0718d0f31ae1922e6f49 -rd514ce187a1ce571355fd92ca1edf822d943ba39 --- Ringtoets/Piping/src/Ringtoets.Piping.IO/PipingSoilProfileReader.cs (.../PipingSoilProfileReader.cs) (revision a483049c20ef6d26addd0718d0f31ae1922e6f49) +++ Ringtoets/Piping/src/Ringtoets.Piping.IO/PipingSoilProfileReader.cs (.../PipingSoilProfileReader.cs) (revision d514ce187a1ce571355fd92ca1edf822d943ba39) @@ -12,7 +12,6 @@ { /// /// This class reads a SqLite database file and constructs from this database. - /// The database is created with the DSoilModel application. /// public class PipingSoilProfileReader : IDisposable { @@ -37,64 +36,53 @@ private SQLiteConnection connection; private SQLiteDataReader dataReader; + private readonly string databaseFileName; + private readonly string databaseRequiredVersion = "15.0.5.0"; + /// - /// 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 + /// 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. - public PipingSoilProfileReader(string dbFile) + /// The path of the database file to open. + public PipingSoilProfileReader(string databaseFilePath) { - if (String.IsNullOrEmpty(dbFile)) + if (String.IsNullOrEmpty(databaseFilePath)) { throw new ArgumentException(Resources.Error_PathMustBeSpecified); } - if (!File.Exists(dbFile)) + if (!File.Exists(databaseFilePath)) { - throw new FileNotFoundException(String.Format(Resources.Error_File_0_does_not_exist, dbFile)); + throw new FileNotFoundException(String.Format(Resources.Error_File_0_does_not_exist, databaseFilePath)); } - OpenConnection(dbFile); + databaseFileName = Path.GetFileName(databaseFilePath); + OpenConnection(databaseFilePath); + SetReaderToFirstRecord(); } /// + /// Gets the value true if profiles can be read using the . + /// false otherwise. + /// + public bool HasNext { get; private set; } + + /// /// Prepares the next layer from the database. /// /// False if there are no more rows to be read. True otherwise. - /// Thrown when reading the layer entry from the database failed. /// Thrown when parsing the geometry of a 2d soil layer failed. public PipingSoilProfile ReadProfile() { - if (dataReader == null) + if (!HasNext) { - throw new InvalidOperationException(GetType() + " was not initialized using Next()"); + throw new InvalidOperationException("Reader has reached the end and cannot read more profiles."); } - return TryRead(dimensionColumn) == 1 ? ReadPipingProfile1D().Build() : ReadPipingProfile2D().Build(); + var dimensionValue = Read(dimensionColumn); + return dimensionValue == 1 ? ReadPipingProfile1D().Build() : ReadPipingProfile2D().Build(); } - /// - /// Moves the reader to the next record in the database. - /// - /// true if there was another record. Otherwise, false. - /// You should never make two calls to this method without calling in between. - public bool Next() - { - InitializeReader(); - if (!dataReader.Read()) - { - if (dataReader.NextResult()) - { - return dataReader.Read(); - } - } - else - { - return true; - } - return false; - } - public void Dispose() { if (dataReader != null) @@ -105,64 +93,90 @@ connection.Dispose(); } + /// + /// Moves the reader to the next record in the database. + /// + private void MoveNext() + { + HasNext = dataReader.Read() || (dataReader.NextResult() && dataReader.Read()); + } + private ISoilProfileBuilder ReadPipingProfile1D() { - var profileName = TryRead(profileNameColumn); - var bottom = TryRead(bottomColumn); - var layerCount = TryRead(layerCountColumn); + var profileName = Read(profileNameColumn); + var layerCount = Read(layerCountColumn); + var bottom = Read(bottomColumn); + var soilProfileBuilder = new SoilProfileBuilder1D(profileName, bottom); - for (int i = 1; i <= layerCount; i++) + for (var i = 1; i <= layerCount; i++) { soilProfileBuilder.Add(ReadPipingSoilLayer()); - if (i < layerCount) - { - Next(); - } + MoveNext(); } + return soilProfileBuilder; } private ISoilProfileBuilder ReadPipingProfile2D() { - var profileName = TryRead(profileNameColumn); - var intersectionX = TryRead(intersectionXColumn); - var layerCount = TryRead(layerCountColumn); + var profileName = Read(profileNameColumn); + var layerCount = Read(layerCountColumn); + var intersectionX = Read(intersectionXColumn); + var soilProfileBuilder = new SoilProfileBuilder2D(profileName, intersectionX); for (int i = 1; i <= layerCount; i++) { - soilProfileBuilder.Add(ReadPiping2DSoilLayer()); - if (i < layerCount) + try { - Next(); + soilProfileBuilder.Add(ReadPiping2DSoilLayer()); } + catch (XmlException e) + { + var exception = new PipingSoilProfileReadException( + string.Format(Resources.PipingSoilProfileReader_CouldNotParseGeometryOfLayer_0_InProfile_1_, i, profileName), e); + + while (i++ <= layerCount) + { + MoveNext(); + } + + throw exception; + } + MoveNext(); } + return soilProfileBuilder; } - private void InitializeReader() + private void SetReaderToFirstRecord() { - if (dataReader == null) - { - PrepareQueries(); - } + InitializeDataReader(); + MoveNext(); } - private T TryRead(string columnName) + private bool TryRead(string columnName, out T value) { try { - return (T) dataReader[columnName]; + value = (T) dataReader[columnName]; + return true; } catch (InvalidCastException e) { - throw new PipingSoilProfileReadException(String.Format(Resources.PipingSoilProfileReader_InvalidValueOnColumn, columnName), e); + value = default(T); + return false; } } + private T Read(string columnName) + { + return (T)dataReader[columnName]; + } + private void OpenConnection(string dbFile) { var connectionStringBuilder = new SQLiteConnectionStringBuilder @@ -191,28 +205,55 @@ private PipingSoilLayer ReadPipingSoilLayer() { - var columnValue = TryRead(topColumn); - var isAquiferValue = TryRead(isAquiferColumn); - var pipingSoilLayer = new PipingSoilLayer(columnValue) + double topValue; + double isAquiferValue; + + var columnValueRead = TryRead(topColumn, out topValue); + var isAquiferValueRead = TryRead(isAquiferColumn, out isAquiferValue); + + if (!columnValueRead || !isAquiferValueRead) { + return null; + } + + var pipingSoilLayer = new PipingSoilLayer(topValue) + { IsAquifer = isAquiferValue.Equals(1.0) }; return pipingSoilLayer; } private SoilLayer2D ReadPiping2DSoilLayer() { - var geometry = TryRead(layerGeometryColumn); - var isAquiferValue = TryRead(isAquiferColumn); + double isAquiferValue; + byte[] geometryValue; - SoilLayer2D pipingSoilLayer = new PipingSoilLayer2DReader(geometry).Read(); + var geometryRead = TryRead(layerGeometryColumn, out geometryValue); + var isAquiferValueRead = TryRead(isAquiferColumn, out isAquiferValue); + + if (!geometryRead || !isAquiferValueRead) + { + return null; + } + + SoilLayer2D pipingSoilLayer = new PipingSoilLayer2DReader(geometryValue).Read(); pipingSoilLayer.IsAquifer = isAquiferValue.Equals(1.0); return pipingSoilLayer; } - private void PrepareQueries() + /// + /// 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. + /// + private void InitializeDataReader() { + string versionQuery = string.Format( + "SELECT Value FROM _Metadata WHERE Key = 'VERSION' AND Value = '{0}';", + databaseRequiredVersion + ); + string materialPropertiesQuery = string.Format( string.Join(" ", "(SELECT", @@ -343,7 +384,7 @@ layer2DPropertiesQuery, mechanismParameterName); - dataReader = CreateDataReader(query2D + query1D, new SQLiteParameter + CreateDataReader(versionQuery + query2D + query1D, new SQLiteParameter { DbType = DbType.String, Value = pipingMechanismName, @@ -352,9 +393,9 @@ } /// - /// Creates a new data reader to use in this class, based on a query which returns all the known soil layers for which its profile has a X coordinate defined for piping. + /// Creates a new data reader to use in this class. /// - private SQLiteDataReader CreateDataReader(string queryString, params SQLiteParameter[] parameters) + private void CreateDataReader(string queryString, params SQLiteParameter[] parameters) { using (var query = new SQLiteCommand(connection) { @@ -365,13 +406,27 @@ try { - return query.ExecuteReader(); + dataReader = query.ExecuteReader(); + CheckVersion(); } catch (SQLiteException e) { - throw new PipingSoilProfileReadException(string.Format(Resources.Error_SoilProfileReadFromDatabase, connection.DataSource), e); + connection.Dispose(); + var exception = new PipingSoilProfileReadException(string.Format(Resources.Error_SoilProfileReadFromDatabase, databaseFileName), e); + throw exception; } } } + + private void CheckVersion() + { + if (!dataReader.HasRows) + { + throw new PipingSoilProfileReadException(String.Format( + Resources.PipingSoilProfileReader_DatabaseFileIncorrectVersions_Requires_0, + databaseRequiredVersion)); + } + dataReader.NextResult(); + } } } \ No newline at end of file