Index: Ringtoets/Common/src/Ringtoets.Common.IO/Structures/StructuresCharacteristicsCsvReader.cs =================================================================== diff -u -r653c18f73b4404d9ac58d818845474d68e48635d -r50d02243937ff6a19020bf095c778252c1c47561 --- Ringtoets/Common/src/Ringtoets.Common.IO/Structures/StructuresCharacteristicsCsvReader.cs (.../StructuresCharacteristicsCsvReader.cs) (revision 653c18f73b4404d9ac58d818845474d68e48635d) +++ Ringtoets/Common/src/Ringtoets.Common.IO/Structures/StructuresCharacteristicsCsvReader.cs (.../StructuresCharacteristicsCsvReader.cs) (revision 50d02243937ff6a19020bf095c778252c1c47561) @@ -26,6 +26,7 @@ using Core.Common.IO.Readers; using Core.Common.Utils; using Core.Common.Utils.Builders; +using Ringtoets.Common.IO.Properties; using CoreCommonUtilsResources = Core.Common.Utils.Properties.Resources; namespace Ringtoets.Common.IO.Structures @@ -43,7 +44,7 @@ { "identificatie", "kunstwerken.identificatie", - "numeriekeWaarde", + "numeriekewaarde", "standarddeviatie.variance", "boolean" }; @@ -70,13 +71,14 @@ /// Counts the number of parameter definitions found in the file. /// /// An integer greater than or equal to 0, being the number of parameter rows. - /// File/directory cannot be found or + /// File/directory cannot be found or /// some other I/O related problem occurred or the header is not in the required format. public int GetLineCount() { using (var reader = StreamReaderHelper.InitializeStreamReader(filePath)) { - ValidateHeader(reader, 1); + lineNumber = 1; + ValidateHeader(reader); return CountNonEmptyLines(reader, 2); } @@ -87,15 +89,15 @@ /// /// The next based on the read file, /// or null when at the end of the file. - /// + /// /// Thrown when either: /// /// The file or directory cannot be found. /// The file is empty. /// Some I/O related problem occurred. /// The header is not in the required format. /// - /// Thrown when either: + /// Thrown when either: /// /// The line does not contain the separator character. /// Location id field is empty or consists out of only white spaces. @@ -144,14 +146,13 @@ /// 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, int currentLine) + /// The header is not in the required format. + private void ValidateHeader(TextReader reader) { string[] tokenizedHeader = GetTokenizedHeader(reader); const int uninitializedValue = -999; int[] requiredHeaderColumnIndices = GetRequiredHeaderColumnIndices(uninitializedValue, tokenizedHeader); - ValidateRequiredColumnIndices(currentLine, requiredHeaderColumnIndices, uninitializedValue); + ValidateRequiredColumnIndices(requiredHeaderColumnIndices, uninitializedValue); } /// @@ -160,7 +161,7 @@ /// The reader at the row from which counting should start. /// The current line, used for error messaging. /// An integer greater than or equal to 0, being the number of parameter rows. - /// An I/O exception occurred. + /// An I/O exception occurred. private int CountNonEmptyLines(TextReader reader, int currentLine) { int count = 0, lineNumberForMessage = currentLine; @@ -182,7 +183,7 @@ /// The opened text file reader. /// Row number for error messaging. /// The read line, or null when at the end of the file. - /// An critical I/O exception occurred. + /// An critical I/O exception occurred. private string ReadLineAndHandleIOExceptions(TextReader reader, int currentLine) { try @@ -195,7 +196,7 @@ } catch (IOException e) { - string errorMessage = string.Format((string)CoreCommonUtilsResources.Error_General_IO_ErrorMessage_0_, + string errorMessage = string.Format((string) CoreCommonUtilsResources.Error_General_IO_ErrorMessage_0_, e.Message); var fullErrorMessage = new FileReaderErrorMessageBuilder(filePath).Build(errorMessage); throw new CriticalFileReadException(fullErrorMessage, e); @@ -206,15 +207,15 @@ /// Reads the header and sets the internal indices of the required header columns. /// /// The reader used to read the file. - /// The file is empty or some I/O exception + /// The file is empty or some I/O exception /// occurred or the header is not in the required format. private void IndexFile(TextReader reader) { string[] tokenizedHeader = GetTokenizedHeader(reader); const int uninitializedValue = -999; int[] requiredHeaderColumnIndices = GetRequiredHeaderColumnIndices(uninitializedValue, tokenizedHeader); - ValidateRequiredColumnIndices(lineNumber, requiredHeaderColumnIndices, uninitializedValue); + ValidateRequiredColumnIndices(requiredHeaderColumnIndices, uninitializedValue); SetColumnIndices(requiredHeaderColumnIndices); } @@ -224,7 +225,7 @@ /// /// The reader used to read the file. /// The header split based on . - /// The file is empty or some I/O exception + /// The file is empty or some I/O exception /// occurred. private string[] GetTokenizedHeader(TextReader reader) { @@ -249,26 +250,33 @@ int index = Array.IndexOf(requiredHeaderColumns, columnName); if (index != -1) { - // TODO: same column multiple times! - requiredHeaderColumnIndices[index] = columnIndex; + if (requiredHeaderColumnIndices[index] == initialColumnIndexValue) + { + requiredHeaderColumnIndices[index] = columnIndex; + } + else + { + string message = string.Format(Resources.StructuresCharacteristicsCsvReader_Column_0_must_be_defined_only_once, columnName); + throw CreateCriticalFileReadException(lineNumber, message); + } } } return requiredHeaderColumnIndices; } /// - /// + /// Checks if all required header columns have been matched. /// - /// - /// - /// - /// The header is not in the required format. - private void ValidateRequiredColumnIndices(int currentLine, int[] requiredHeaderColumnIndices, int uninitializedValue) + /// The array of matched column indices. + /// The initial index value put in . + /// The header is not in the required format. + private void ValidateRequiredColumnIndices(int[] requiredHeaderColumnIndices, int uninitializedValue) { if (requiredHeaderColumnIndices.Any(i => i == uninitializedValue)) { - throw CreateCriticalFileReadException(currentLine, string.Format("Het bestand is niet geschikt om kunstwerken parameters uit te lezen (Verwachte koptekst moet de volgende kolommen bevatten: {0}.", - string.Join(", ", requiredHeaderColumns))); + string message = string.Format(Resources.StructuresCharacteristicsCsvReader_ValidateRequiredColumnIndices_Invalid_header_Must_have_columns_0_, + string.Join(", ", requiredHeaderColumns)); + throw CreateCriticalFileReadException(lineNumber, message); } } @@ -286,7 +294,7 @@ /// /// The next line which is not a white line, or null when no non-white /// line could be found before the end of file. - /// An critical I/O exception occurred. + /// An critical I/O exception occurred. private string ReadNextNonEmptyLine(StreamReader reader) { string readText; @@ -309,7 +317,7 @@ /// /// The read text. /// - /// Thrown when either: + /// Thrown when either: /// /// does not contain the separator character. /// Location id field is empty or consists out of only white spaces. @@ -345,7 +353,7 @@ /// /// The text. /// The tokenized parts. - /// lacks separator character. + /// lacks separator character. private string[] TokenizeString(string readText) { if (!readText.Contains(separator)) @@ -363,7 +371,7 @@ /// /// The tokenized text. /// The location ID. - /// Location ID field is empty or only has whitespaces. + /// Location ID field is empty or only has whitespaces. private string ParseLocationId(string[] tokenizedText) { string locationId = tokenizedText[locationIdIndex]; @@ -380,7 +388,7 @@ /// The tokenized text. /// /// The parameter ID. - /// Parameter ID field is empty or only has whitespaces. + /// Parameter ID field is empty or only has whitespaces. private string ParseParameterId(string[] tokenizedText) { string parameterId = tokenizedText[parameterIdIndex]; @@ -396,7 +404,7 @@ /// /// The tokenized text. /// The numeric value (can be ). - /// When the numeric value field is not a number + /// When the numeric value field is not a number /// or when it's too large or too small to be represented as . private double ParseNumericValue(string[] tokenizedText) { @@ -409,7 +417,7 @@ /// /// The tokenized text. /// The standard deviation or coefficient of variation value (can be ). - /// When the standard deviation or coefficient + /// When the standard deviation or coefficient /// of variation value field is not a number or when it's too large or too small /// to be represented as . private double ParseVarianceValue(string[] tokenizedText) @@ -425,7 +433,7 @@ /// Name of the parameter. /// when is null /// or only whitespaces; otherwise the parsed number. - /// When is + /// When is /// not a number or when it's too large or too small to be represented as . private double ParseDoubleValue(string doubleValueText, string parameterName) { @@ -454,7 +462,7 @@ /// /// The tokenized text. /// The based on the text in the file. - /// When the 'boolean' field is not a valid value. + /// When the 'boolean' field is not a valid value. private VarianceType ParseVarianceType(string[] tokenizedText) { string varianceTypeText = tokenizedText[varianceTypeIndex]; @@ -486,12 +494,12 @@ } /// - /// Throws a configured instance of . + /// Throws a configured instance of . /// /// The line number being read. /// The critical error message. /// Optional: exception that caused this exception to be thrown. - /// New with message set. + /// New with message set. private LineParseException CreateLineParseException(int currentLine, string lineParseErrorMessage, Exception innerException = null) { string locationDescription = string.Format(CoreCommonUtilsResources.TextFile_On_LineNumber_0_, currentLine); @@ -501,12 +509,12 @@ } /// - /// Throws a configured instance of . + /// Throws a configured instance of . /// /// The line number being read. /// The critical error message. /// Optional: exception that caused this exception to be thrown. - /// New with message and inner exception set. + /// New with message and inner exception set. private CriticalFileReadException CreateCriticalFileReadException(int currentLine, string criticalErrorMessage, Exception innerException = null) { string locationDescription = string.Format(CoreCommonUtilsResources.TextFile_On_LineNumber_0_,