Index: src/Plugins/Wti/Wti.IO/Exceptions/CriticalFileReadException.cs =================================================================== diff -u --- src/Plugins/Wti/Wti.IO/Exceptions/CriticalFileReadException.cs (revision 0) +++ src/Plugins/Wti/Wti.IO/Exceptions/CriticalFileReadException.cs (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -0,0 +1,32 @@ +using System; + +namespace Wti.IO.Exceptions +{ + /// + /// The exception that is thrown when a file reader class encounters a critical error + /// during the read. + /// + public class CriticalFileReadException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public CriticalFileReadException(){} + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public CriticalFileReadException(string message) : base(message){} + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is + /// the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public CriticalFileReadException(string message, Exception inner) : base(message, inner) { } + } +} \ No newline at end of file Index: src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -4,6 +4,7 @@ using System.Linq; using Wti.Data; +using Wti.IO.Exceptions; using Wti.IO.Properties; namespace Wti.IO @@ -18,7 +19,11 @@ /// public class PipingSurfaceLinesCsvReader : IDisposable { - private readonly string[] acceptableIdNames = { "Profielnaam, LocationID" }; + private readonly string[] acceptableIdNames = + { + "Profielnaam, LocationID" + }; + private readonly string filePath; private StreamReader fileReader; @@ -31,70 +36,76 @@ public PipingSurfaceLinesCsvReader(string path) { CheckIfPathIsValid(path); - + filePath = path; } - private void CheckIfPathIsValid(string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentException(Resources.Error_PathMustBeSpecified); - } - - string name; - try - { - name = Path.GetFileName(path); - } - catch (ArgumentException e) - { - throw new ArgumentException(String.Format(Resources.Error_PathCannotContainCharacters_0_, - String.Join(", ", Path.GetInvalidFileNameChars())), e); - } - if (string.Empty == name) - { - throw new ArgumentException(Resources.Error_PathMustNotPointToFolder); - } - } - - public void Dispose() - { - if (fileReader != null) - { - fileReader.Dispose(); - fileReader = null; - } - } - /// /// Reads the file to determine the number of available /// data rows. /// /// A value greater than or equal to 0. - /// The file cannot be found. - /// The specified path is invalid, such as being on an unmapped drive. - /// Filepath includes an incorrect or invalid syntax for file name, directory name, or volume label. - /// There is insufficient memory to allocate a buffer for the returned string. + /// A critical error has occurred, which may be caused by: + /// + /// File cannot be found at specified path. + /// The specified path is invalid, such as being on an unmapped drive. + /// Some other I/O related issue occurred, such as: path includes an incorrect + /// or invalid syntax for file name, directory name, or volume label. + /// There is insufficient memory to allocate a buffer for the returned string. + /// + /// /// The file in not properly formatted. public int GetSurfaceLinesCount() { - var count = 0; - using (var reader = new StreamReader(filePath)) + int count = 0, lineNumber = 0; + StreamReader reader = null; + try { + reader = new StreamReader(filePath); + // Skip header: + // TODO: Make sure header is valid reader.ReadLine(); + lineNumber++; // Count SurfaceLines: string line; while ((line = reader.ReadLine()) != null) { + lineNumber++; if (!String.IsNullOrWhiteSpace(line)) { count++; } } } + catch (FileNotFoundException e) + { + var message = string.Format(Resources.Error_File_0_does_not_exist, filePath); + throw new CriticalFileReadException(message, e); + } + catch (DirectoryNotFoundException e) + { + var message = string.Format(Resources.Error_Directory_in_path_0_missing, filePath); + throw new CriticalFileReadException(message, e); + } + catch (OutOfMemoryException e) + { + var message = string.Format(Resources.Error_File_0_contains_Line_1_too_big, filePath, lineNumber); + throw new CriticalFileReadException(message, e); + } + catch (IOException e) + { + var message = string.Format(Resources.Error_General_IO_File_0_ErrorMessage_1_, filePath, e.Message); + throw new CriticalFileReadException(message, e); + } + finally + { + if (reader != null) + { + reader.Dispose(); + } + } return count; } @@ -124,15 +135,15 @@ var worldCoordinateValues = tokenizedString.Skip(1) .Select(ts => Double.Parse(ts, CultureInfo.InvariantCulture)) .ToArray(); - int coordinateCount = worldCoordinateValues.Length/3; + int coordinateCount = worldCoordinateValues.Length / 3; var points = new Point3D[coordinateCount]; for (int i = 0; i < coordinateCount; i++) { points[i] = new Point3D { - X = worldCoordinateValues[i*3], - Y = worldCoordinateValues[i*3+1], - Z = worldCoordinateValues[i*3+2] + X = worldCoordinateValues[i * 3], + Y = worldCoordinateValues[i * 3 + 1], + Z = worldCoordinateValues[i * 3 + 2] }; } @@ -146,5 +157,37 @@ return null; } + + public void Dispose() + { + if (fileReader != null) + { + fileReader.Dispose(); + fileReader = null; + } + } + + private void CheckIfPathIsValid(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException(Resources.Error_PathMustBeSpecified); + } + + string name; + try + { + name = Path.GetFileName(path); + } + catch (ArgumentException e) + { + throw new ArgumentException(String.Format(Resources.Error_PathCannotContainCharacters_0_, + String.Join(", ", Path.GetInvalidFileNameChars())), e); + } + if (string.Empty == name) + { + throw new ArgumentException(Resources.Error_PathMustNotPointToFolder); + } + } } } \ No newline at end of file Index: src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -61,6 +61,42 @@ } /// + /// Looks up a localized string similar to Het bestandspad '{0}' verwijst naar een map die niet bestaat.. + /// + public static string Error_Directory_in_path_0_missing { + get { + return ResourceManager.GetString("Error_Directory_in_path_0_missing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Het bestand op '{0}' heeft op regel {1} teveel tekst om in het RAM geheugen opgeslagen te worden.. + /// + public static string Error_File_0_contains_Line_1_too_big { + get { + return ResourceManager.GetString("Error_File_0_contains_Line_1_too_big", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Het bestand op '{0}' bestaat niet.. + /// + public static string Error_File_0_does_not_exist { + get { + return ResourceManager.GetString("Error_File_0_does_not_exist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Er is een onverwachte inleesfout opgetreden tijdens het lezen van het bestand '{0}': {1}. + /// + public static string Error_General_IO_File_0_ErrorMessage_1_ { + get { + return ResourceManager.GetString("Error_General_IO_File_0_ErrorMessage_1_", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Bestandspad mag niet de volgende tekens bevatten: {0}. /// public static string Error_PathCannotContainCharacters_0_ { @@ -86,5 +122,14 @@ return ResourceManager.GetString("Error_PathMustNotPointToFolder", resourceCulture); } } + + /// + /// Looks up a localized string similar to Het bestand op '{0}' heeft op regel {1} teveel tekst om in het RAM geheugen opgeslagen te worden.. + /// + public static string Error_Unexpected_IOError_File_0_Line_1_ { + get { + return ResourceManager.GetString("Error_Unexpected_IOError_File_0_Line_1_", resourceCulture); + } + } } } Index: src/Plugins/Wti/Wti.IO/Properties/Resources.resx =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -126,4 +126,19 @@ Bestandspad mag niet de volgende tekens bevatten: {0} + + Het bestand op '{0}' bestaat niet. + + + Het bestandspad '{0}' verwijst naar een map die niet bestaat. + + + Het bestand op '{0}' heeft op regel {1} teveel tekst om in het RAM geheugen opgeslagen te worden. + + + Het bestand op '{0}' heeft op regel {1} teveel tekst om in het RAM geheugen opgeslagen te worden. + + + Er is een onverwachte inleesfout opgetreden tijdens het lezen van het bestand '{0}': {1} + \ No newline at end of file Index: src/Plugins/Wti/Wti.IO/Wti.IO.csproj =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- src/Plugins/Wti/Wti.IO/Wti.IO.csproj (.../Wti.IO.csproj) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ src/Plugins/Wti/Wti.IO/Wti.IO.csproj (.../Wti.IO.csproj) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -50,6 +50,7 @@ Properties\GlobalAssembly.cs + Index: src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs (.../PipingSurfaceLinesCsvImporter.cs) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ src/Plugins/Wti/Wti.Plugin/FileImporter/PipingSurfaceLinesCsvImporter.cs (.../PipingSurfaceLinesCsvImporter.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -9,6 +9,7 @@ using Wti.Data; using Wti.IO; +using Wti.IO.Exceptions; using WtiFormsResources = Wti.Forms.Properties.Resources; using ApplicationResources = Wti.Plugin.Properties.Resources; @@ -137,9 +138,20 @@ var stepName = String.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_ReadPipingSurfaceLines_0_, Path.GetFileName(path)); - var itemCount = reader.GetSurfaceLinesCount(); - NotifyProgress(stepName, 0, itemCount); + int itemCount; + try + { + itemCount = reader.GetSurfaceLinesCount(); + NotifyProgress(stepName, 0, itemCount); + } + catch (CriticalFileReadException e) + { + var message = string.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_CriticalErrorReading_0_Cause_1_, + path, e.Message); + log.Error(message, e); + return new SurfaceLinesFileReadResult(true); + } var readSurfaceLines = new List(itemCount); for (int i = 0; i < itemCount && !ShouldCancel; i++) Index: test/Plugins/Wti/Wti.IO.Test/Exceptions/CriticalFileReadExceptionTest.cs =================================================================== diff -u --- test/Plugins/Wti/Wti.IO.Test/Exceptions/CriticalFileReadExceptionTest.cs (revision 0) +++ test/Plugins/Wti/Wti.IO.Test/Exceptions/CriticalFileReadExceptionTest.cs (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -0,0 +1,72 @@ +using System; + +using NUnit.Framework; + +using Wti.IO.Exceptions; + +namespace Wti.IO.Test.Exceptions +{ + [TestFixture] + public class CriticalFileReadExceptionTest + { + [Test] + [SetCulture("en-US")] + public void DefaultConstructor_ExpectedValues() + { + // Call + var exception = new CriticalFileReadException(); + + // Assert + Assert.IsInstanceOf(exception); + var expectedMessage = string.Format("Exception of type '{0}' was thrown.", exception.GetType()); + Assert.AreEqual(expectedMessage, exception.Message); + CollectionAssert.IsEmpty(exception.Data); + Assert.IsNull(exception.HelpLink); + Assert.IsNull(exception.InnerException); + Assert.IsNull(exception.Source); + Assert.IsNull(exception.StackTrace); + Assert.IsNull(exception.TargetSite); + } + + [Test] + public void MessageConstructor_ExpectedValues() + { + // Setup + const string messageText = ""; + + // Call + var exception = new CriticalFileReadException(messageText); + + // Assert + Assert.IsInstanceOf(exception); + Assert.AreEqual(messageText, exception.Message); + CollectionAssert.IsEmpty(exception.Data); + Assert.IsNull(exception.HelpLink); + Assert.IsNull(exception.InnerException); + Assert.IsNull(exception.Source); + Assert.IsNull(exception.StackTrace); + Assert.IsNull(exception.TargetSite); + } + + [Test] + public void MessageAndInnerExceptionConstructor_ExpectedValues() + { + // Setup + var innerException = new Exception(); + const string messageText = ""; + + // Call + var exception = new CriticalFileReadException(messageText, innerException); + + // Assert + Assert.IsInstanceOf(exception); + Assert.AreEqual(messageText, exception.Message); + CollectionAssert.IsEmpty(exception.Data); + Assert.IsNull(exception.HelpLink); + Assert.AreEqual(innerException, exception.InnerException); + Assert.IsNull(exception.Source); + Assert.IsNull(exception.StackTrace); + Assert.IsNull(exception.TargetSite); + } + } +} \ No newline at end of file Index: test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -5,6 +5,9 @@ using DelftTools.TestUtils; using NUnit.Framework; + +using Wti.IO.Exceptions; + using IOResources = Wti.IO.Properties.Resources; namespace Wti.IO.Test @@ -144,6 +147,50 @@ } } + [Test] + public void GetSurfaceLinesCount_FileCannotBeFound_ThrowCriticalFileReadException() + { + // Setup + string path = Path.Combine(testDataPath, "I_do_not_exist.csv"); + + // Precondition + Assert.IsFalse(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.GetSurfaceLinesCount(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.Error_File_0_does_not_exist, path); + Assert.AreEqual(expectedMessage, exception.Message); + Assert.IsInstanceOf(exception.InnerException); + } + } + + [Test] + public void GetSurfaceLinesCount_DirectoryCannotBeFound_ThrowCriticalFileReadException() + { + // Setup + string path = Path.Combine(testDataPath, "..", "this_folder_does_not_exist", "I_do_not_exist.csv"); + + // Precondition + Assert.IsFalse(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.GetSurfaceLinesCount(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.Error_Directory_in_path_0_missing, path); + Assert.AreEqual(expectedMessage, exception.Message); + Assert.IsInstanceOf(exception.InnerException); + } + } + private void DoReadLine_OpenedValidFileWithHeaderAndTwoSurfaceLines_ReturnCreatedSurfaceLine() { // Setup Index: test/Plugins/Wti/Wti.IO.Test/Wti.IO.Test.csproj =================================================================== diff -u -re5c186677d0ef8697eb04ea571b7c4ef8268183c -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- test/Plugins/Wti/Wti.IO.Test/Wti.IO.Test.csproj (.../Wti.IO.Test.csproj) (revision e5c186677d0ef8697eb04ea571b7c4ef8268183c) +++ test/Plugins/Wti/Wti.IO.Test/Wti.IO.Test.csproj (.../Wti.IO.Test.csproj) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -40,6 +40,7 @@ + Index: test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs =================================================================== diff -u -r8fe3ae6f129251f98795abd491315924ca707a76 -rf2284364cde43ab7969dd8f0fb038f846138ec41 --- test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs (.../PipingSurfaceLineCsvImporterTest.cs) (revision 8fe3ae6f129251f98795abd491315924ca707a76) +++ test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs (.../PipingSurfaceLineCsvImporterTest.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) @@ -215,5 +215,37 @@ "No items should be added to collection when import is aborted."); mocks.VerifyAll(); // Expect no calls on 'observer' } + + [Test] + public void ImportItem_FileDoesNotExist_AbortImportAndLog() + { + // Setup + string corruptPath = Path.Combine(testDataPath, "I_dont_exists.csv"); + + var mocks = new MockRepository(); + var observer = mocks.StrictMock(); + mocks.ReplayAll(); + + var importer = new PipingSurfaceLinesCsvImporter(); + + var observableSurfaceLinesList = new ObservableList(); + observableSurfaceLinesList.Attach(observer); + + object importedItem = null; + + // Call + Action call = () => importedItem = importer.ImportItem(corruptPath, observableSurfaceLinesList); + + // Assert + var internalErrorMessage = String.Format(WtiIOResources.Error_File_0_does_not_exist, + corruptPath); + var expectedLogMessage = string.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_CriticalErrorReading_0_Cause_1_, + corruptPath, internalErrorMessage); + TestHelper.AssertLogMessageIsGenerated(call, expectedLogMessage, 1); + Assert.AreSame(observableSurfaceLinesList, importedItem); + CollectionAssert.IsEmpty(observableSurfaceLinesList, + "No items should be added to collection when import is aborted."); + mocks.VerifyAll(); // Expect no calls on 'observer' + } } } \ No newline at end of file