Index: src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs =================================================================== diff -u -r91ec76d4b8a0fcf5962f442021c43f1ffa70743e -raa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9 --- src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision 91ec76d4b8a0fcf5962f442021c43f1ffa70743e) +++ src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -71,7 +72,7 @@ { using (var reader = InitializeStreamReader(filePath)) { - ValidateHeader(reader); + ValidateHeader(reader, 1); return CountNonEmptyLines(reader, 2); } @@ -94,8 +95,11 @@ /// /// A parse error has occurred for the current row, which may be caused by: /// + /// The row doesn't contain any supported separator character. /// The row contains a coordinate value that cannot be parsed as a double. /// The row contains a number that is too big or too small to be represented with a double. + /// The row is missing an identifier value. + /// The row is missing values to form a surface line point. /// /// public PipingSurfaceLine ReadLine() @@ -104,41 +108,116 @@ { fileReader = InitializeStreamReader(filePath); - ValidateHeader(fileReader); + ValidateHeader(fileReader, 1); lineNumber = 2; } - var readText = ReadLineAndHandleIOExceptions(fileReader); + var readText = ReadLineAndHandleIOExceptions(fileReader, lineNumber); if (readText != null) { - var tokenizedString = readText.Split(separator); - var worldCoordinateValues = ParseWorldCoordinateValuesAndHandleParseErrors(tokenizedString); - - // TODO: Format Error: missing values to complete coordinate triplet - int coordinateCount = worldCoordinateValues.Length / 3; - var points = new Point3D[coordinateCount]; - for (int i = 0; i < coordinateCount; i++) + try { - points[i] = new Point3D + var tokenizedString = TokenizeString(readText); + + var surfaceLineName = GetSurfaceLineName(tokenizedString); + var points = GetSurfaceLinePoints(tokenizedString); + + var surfaceLine = new PipingSurfaceLine { - X = worldCoordinateValues[i * 3], - Y = worldCoordinateValues[i * 3 + 1], - Z = worldCoordinateValues[i * 3 + 2] + Name = surfaceLineName }; + surfaceLine.SetGeometry(points); + return surfaceLine; } + finally + { + lineNumber++; + } + } - lineNumber++; + return null; + } - var surfaceLine = new PipingSurfaceLine + public void Dispose() + { + if (fileReader != null) + { + fileReader.Dispose(); + fileReader = null; + } + } + + /// + /// Tokenizes a string using a separator character. + /// + /// The text. + /// The tokenized parts. + /// lacks separator character. + private string[] TokenizeString(string readText) + { + if (!readText.Contains(separator)) + { + var message = string.Format(Resources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_separator_2_, + filePath, lineNumber, separator); + throw new LineParseException(message); + } + return readText.Split(separator); + } + + /// + /// Gets the 3D surface line points. + /// + /// The tokenized string. + /// + /// A parse error has occurred for the current row, which may be caused by: + /// + /// contains a coordinate value that cannot be parsed as a double. + /// contains a number that is too big or too small to be represented with a double. + /// is missing coordinate values to define a proper surface line point. + /// + /// + private Point3D[] GetSurfaceLinePoints(string[] tokenizedString) + { + const int expectedValuesForPoint = 3; + + var worldCoordinateValues = ParseWorldCoordinateValuesAndHandleParseErrors(tokenizedString); + if (worldCoordinateValues.Length % expectedValuesForPoint != 0) + { + var message = string.Format(Resources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_values_for_coordinate_triplet, + filePath, lineNumber); + throw new LineParseException(message); + } + + int coordinateCount = worldCoordinateValues.Length / expectedValuesForPoint; + var points = new Point3D[coordinateCount]; + for (int i = 0; i < coordinateCount; i++) + { + points[i] = new Point3D { - // TODO: Format Error: Row identifier null, empty or whitespace - Name = tokenizedString.First() + X = worldCoordinateValues[i * expectedValuesForPoint], + Y = worldCoordinateValues[i * expectedValuesForPoint + 1], + Z = worldCoordinateValues[i * expectedValuesForPoint + 2] }; - surfaceLine.SetGeometry(points); - return surfaceLine; } + return points; + } - return null; + /// + /// Gets the name of the surface line. + /// + /// The tokenized string from which the name should be extrated. + /// The name of the surface line. + /// Id value is null or empty. + private string GetSurfaceLineName(IList tokenizedString) + { + var name = tokenizedString[0].Trim(); + if (string.IsNullOrEmpty(name)) + { + var message = string.Format(Resources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_NoId, + filePath, lineNumber); + throw new LineParseException(message); + } + return name; } /// @@ -174,15 +253,6 @@ } } - public void Dispose() - { - if (fileReader != null) - { - fileReader.Dispose(); - fileReader = null; - } - } - /// /// Initializes the stream reader for a UTF8 encoded file. /// @@ -217,10 +287,11 @@ /// Validates the header of the file. /// /// The reader, which is currently at the header row. + /// Row index used in error messaging. /// The header is not in the required format. - private void ValidateHeader(TextReader reader) + private void ValidateHeader(TextReader reader, int currentLine) { - var header = ReadLineAndHandleIOExceptions(reader); + var header = ReadLineAndHandleIOExceptions(reader, currentLine); if (header != null) { if (!IsHeaderValid(header)) @@ -245,15 +316,15 @@ /// An I/O exception occurred. private int CountNonEmptyLines(TextReader reader, int currentLine) { - var count = 0; + int count = 0, lineNumberForMessage = currentLine; string line; - while ((line = ReadLineAndHandleIOExceptions(reader)) != null) + while ((line = ReadLineAndHandleIOExceptions(reader, lineNumberForMessage)) != null) { if (!String.IsNullOrWhiteSpace(line)) { count++; } - currentLine++; + lineNumberForMessage++; } return count; } @@ -265,15 +336,15 @@ /// Row number for error messaging. /// The read line, or null when at the end of the file. /// An critical I/O exception occurred. - private string ReadLineAndHandleIOExceptions(TextReader reader) + private string ReadLineAndHandleIOExceptions(TextReader reader, int currentLine) { try { return reader.ReadLine(); } catch (OutOfMemoryException e) { - var message = string.Format(Resources.Error_File_0_contains_Line_1_too_big, filePath, lineNumber); + var message = string.Format(Resources.Error_File_0_contains_Line_1_too_big, filePath, currentLine); throw new CriticalFileReadException(message, e); } catch (IOException e) Index: src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs =================================================================== diff -u -r91ec76d4b8a0fcf5962f442021c43f1ffa70743e -raa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9 --- src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 91ec76d4b8a0fcf5962f442021c43f1ffa70743e) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -176,5 +176,33 @@ return ResourceManager.GetString("PipingSurfaceLinesCsvReader_File_0_invalid_header", resourceCulture); } } + + /// + /// Looks up a localized string similar to Het bestand '{0}' heeft op regel {1} geen verwacht scheidingsteken (het karakter: {2}).. + /// + public static string PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_separator_2_ { + get { + return ResourceManager.GetString("PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_separator_2_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Het bestand '{0}' heeft op regel {1} ontbrekende waardes om een 3D (X,Y,Z) punt aan te maken.. + /// + public static string PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_values_for_coordinate_triplet { + get { + return ResourceManager.GetString("PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_values_for_coordinate_tr" + + "iplet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Het bestand '{0}' heeft op regel {1} heeft geen ID.. + /// + public static string PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_NoId { + get { + return ResourceManager.GetString("PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_NoId", resourceCulture); + } + } } } Index: src/Plugins/Wti/Wti.IO/Properties/Resources.resx =================================================================== diff -u -r91ec76d4b8a0fcf5962f442021c43f1ffa70743e -raa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9 --- src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision 91ec76d4b8a0fcf5962f442021c43f1ffa70743e) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -156,4 +156,13 @@ Het bestand '{0}' heeft op regel {1} een waarde dat te groot/klein is om ingelezen te worden. + + Het bestand '{0}' heeft op regel {1} heeft geen ID. + + + Het bestand '{0}' heeft op regel {1} geen verwacht scheidingsteken (het karakter: {2}). + + + Het bestand '{0}' heeft op regel {1} ontbrekende waardes om een 3D (X,Y,Z) punt aan te maken. + \ No newline at end of file Index: src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs =================================================================== diff -u -r91ec76d4b8a0fcf5962f442021c43f1ffa70743e -raa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9 --- src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs (.../PipingSurfaceLinesCsvImporter.cs) (revision 91ec76d4b8a0fcf5962f442021c43f1ffa70743e) +++ src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs (.../PipingSurfaceLinesCsvImporter.cs) (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -166,7 +166,7 @@ catch (LineParseException e) { var message = string.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_ReadPipingSurfaceLines_ParseError_File_0_SurfaceLinesNumber_1_Message_2_, - path, i+1, e.Message); + path, i + 1, e.Message); log.Error(message); } NotifyProgress(stepName, i + 1, itemCount); Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_IncorrectValueSeparator.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_IncorrectValueSeparator.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_IncorrectValueSeparator.csv (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -0,0 +1,2 @@ +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn +Rotterdam1,94263.0026213,427776.654093,-1.02,94275.9126686,427811.080886,-1.04,94284.0663827,427831.918156,1.25,94294.9380015,427858.191234,1.45,94305.3566362,427889.900123,1.65,94315.0957947,427913.908281,1.66,94325.0614453,427941.766804,1.55,94331.1767309,427960.112661,1.44 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_LacksCoordinateValues.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_LacksCoordinateValues.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidRow_LacksCoordinateValues.csv (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -0,0 +1,2 @@ +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn +InvalidRow1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_IncompleteCoordinateTriplets.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_IncompleteCoordinateTriplets.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_IncompleteCoordinateTriplets.csv (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -0,0 +1,3 @@ +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn +LacksOneCoordinate;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 +LacksTwoCoordinates;2.3;0;1.0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_LacksId.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_LacksId.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoInvalidRows_LacksId.csv (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -0,0 +1,3 @@ +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn +;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 + ;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs =================================================================== diff -u -r91ec76d4b8a0fcf5962f442021c43f1ffa70743e -raa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9 --- test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision 91ec76d4b8a0fcf5962f442021c43f1ffa70743e) +++ test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision aa2e48b57a92d8f3703cb27e92bb538bfc5b6ff9) @@ -422,6 +422,102 @@ } } + [Test] + public void ReadLine_FileLacksIds_ThrowLineParseException() + { + // Setup + string path = Path.Combine(testDataPath, "TwoInvalidRows_LacksId.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.ReadLine(); + + // Assert + // 1st line has no text at all: + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_NoId, path, 2); + Assert.AreEqual(expectedMessage, exception.Message); + + // 2nd line has only whitespace text: + expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_NoId, path, 3); + exception = Assert.Throws(call); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + + [Test] + public void ReadLine_IncorrectValueSeparator_ThrowLineParseException() + { + // Setup + string path = Path.Combine(testDataPath, "InvalidRow_IncorrectValueSeparator.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.ReadLine(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_separator_2_, path, 2, ';'); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + + [Test] + public void ReadLine_FileLacksCoordinateValues_ThrowLineParseException() + { + // Setup + string path = Path.Combine(testDataPath, "InvalidRow_LacksCoordinateValues.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.ReadLine(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_separator_2_, path, 2, ';'); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + + [Test] + public void ReadLine_FileHasIncompleteCoordinateTriplets_ThrowLineParseException() + { + // Setup + string path = Path.Combine(testDataPath, "TwoInvalidRows_IncompleteCoordinateTriplets.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.ReadLine(); + + // Assert + // 1st row lacks 1 coordinate value: + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_values_for_coordinate_triplet, path, 2); + Assert.AreEqual(expectedMessage, exception.Message); + + // 2nd row lacks 2 coordinate values: + exception = Assert.Throws(call); + expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_ReadLine_File_0_Line_1_Lacks_values_for_coordinate_triplet, path, 3); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + private void DoReadLine_OpenedValidFileWithHeaderAndTwoSurfaceLines_ReturnCreatedSurfaceLine() { // Setup