/// <summary> /// Move/traverse up hierarchy for the item and confirm alignment with the hierarchy attribute configuration(s) and update the values accordingly. /// </summary> private FileRecordHierarchyLinker MoveUpHierarchy(FileRecordHierarchyLinker linker, FileRecordHierarchyItem from, FileRecordHierarchyItem to, FileRecord record, bool final = false) { if (from == null) { return(null); } if (!final && from == to) { return(linker); } foreach (var child in from.Children) { var fhr = child.Value.HierarchyReflector; var fha = child.Value.HierarchyReflector.FileHierarchy; var count = linker.GetChildCount(fhr.RecordIdentifier); // Validate alignment to configuration. if (fha.IsMandatory && count == 0) { fhr.CreateErrorMessage(record, "{0} is required; no record found."); continue; } // Minimum count check only honoured where at least one record found; otherwise, use IsMandatory to catch. if (fhr.IsCollection && count > 0 && fha.MinCount > 0 && count < fha.MinCount) { fhr.CreateErrorMessage(record, "{0} must have at least {2} records(s); additional required.", fha.MinCount); } // Update the values as we move up the hierarchy. var vals = linker.GetChildValues(fhr.RecordIdentifier); fhr.SetValue(linker.Value, vals); } return(MoveUpHierarchy(linker.Parent, from.Parent, to, record, final)); }
/// <summary> /// Internal read logic. /// </summary> private FileOperationResult ReadInternal() { // Check and see if we are already at the end of the file. if (IsEndOfFile) { throw new InvalidOperationException("Attempt made to read past the end of the file."); } // Make sure the configuration is valid. if (!_startedReading) { InitialConfiguration(); } // Read the next record(s) group. _startedReading = true; var records = new List <FileRecord>(); FileRecordHierarchyLinker linker = null; while (true) { ReadRecord(); // When end of file is reached, check corresponding file validations. if (IsEndOfFile) { return(ProcessEndOfFile()); } // Grab the current record. var record = _currentRecord; // Check and see if the first record is a header. if (record.LineNumber == 1) { var header = ProcessHeaderRecord(record); if (header != null) { return(header); } } // Check and see if the last record is a trailer. if (_nextRecord == null) { var trailer = ProcessTrailerRecord(record); if (trailer != null) { return(trailer); } } // Where no hierarchy each record is processed as-is; or where hierarchy is in place and top-level is unknown. if (!_hasHierarchy || (records.Count == 0 && record.RecordIdentifier != _fileFormat.ContentRecordIdentifier)) { return(ProcessNonHierarchicalContent(record)); } // We are all hierarchy all of the time from here on in... if (records.Count == 0) { record.Level = 0; record.Value = _fileFormat.ReadCreateRecordValueInternal(record, _fileFormat.ContentRowType); _contentReflector.Validate(record); linker = new FileRecordHierarchyLinker(_hierarchy[_fileFormat.ContentRecordIdentifier], null, 0) { Value = record.Value }; } else { linker = ProcessHierarchicalContent(linker, records, record); } records.Add(record); _readContent = true; // Check and see if the record is the last for the result set; if so, then exit. if (CheckCurrentIsLast()) { var curr = _hierarchy[linker.RecordIdentifier]; var root = _hierarchy[_fileFormat.ContentRecordIdentifier]; MoveUpHierarchy(linker, curr, root, record, true); break; } } return(new FileOperationResult <TContent>(FileContentStatus.Content, records.ToArray())); }
/// <summary> /// Process a hierarchical Content record. /// </summary> private FileRecordHierarchyLinker ProcessHierarchicalContent(FileRecordHierarchyLinker linker, List <FileRecord> records, FileRecord record) { if (!CheckNotHeaderOrTrailer(record)) { return(linker); } // Make sure we know what the record is. if (!_hierarchy.ContainsKey(record.RecordIdentifier)) { record.Messages.Add(MessageType.Error, "Record identifier is unknown."); return(linker); } // Ensure that the record is valid from a hierarchy position perspective. var isValid = true; var curr = _hierarchy[record.RecordIdentifier]; var prev = _hierarchy[records.Last(x => x.Level >= 0).RecordIdentifier]; if (curr.Level == prev.Level) { if (prev.Parent.Children.ContainsKey(record.RecordIdentifier)) { linker = linker.Parent; } else { isValid = record.Messages.Add(MessageType.Error, "Record identifier is not a valid peer of the previous record.") == null; } } else if (curr.Level < prev.Level) { if (FindUpHierachy(prev, record)) { linker = MoveUpHierarchy(linker, prev, curr.Parent, record); } else { isValid = record.Messages.Add(MessageType.Error, "Record identifier is not valid within current traversed hierarchy.") == null; } } else { if (!prev.Children.ContainsKey(record.RecordIdentifier)) { isValid = record.Messages.Add(MessageType.Error, "Record identifier is not a direct descendent of the previous record.") == null; } } // Check that record is in alignment with the hierarchy attribute configuration. var parentValue = linker.Value; if (isValid) { linker = linker.AddChild(curr, record); if (linker.Index > 0 && !curr.HierarchyReflector.IsCollection) { isValid = false; curr.HierarchyReflector.CreateErrorMessage(record, "{0} does not support multiple records; too many provided."); } if (curr.HierarchyReflector.IsCollection && curr.HierarchyReflector.FileHierarchy.MaxCount > 0 && linker.Index >= curr.HierarchyReflector.FileHierarchy.MaxCount) { curr.HierarchyReflector.CreateErrorMessage(record, "{0} must not exceed {2} records(s); too many provided.", curr.HierarchyReflector.FileHierarchy.MaxCount); } record.Level = curr.Level; } else { record.Level = -1; } // Parse the record to get the value and only update the linker where valid. record.Value = _fileFormat.ReadCreateRecordValueInternal(record, curr.HierarchyReflector.FileHierarchy.ChildType ?? curr.RecordReflector.Type); curr.RecordReflector.Validate(record); if (isValid) { linker.Value = record.Value; } RecordProcess(record); return(linker); }