Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <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));
        }
Exemple #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FileValidationException"/> class.
 /// </summary>
 /// <param name="fileValidation">The <see cref="FileValidation"/> rule that caused the exception.</param>
 /// <param name="message">The message.</param>
 /// <param name="record">The <see cref="FileRecord"/> where applicable.</param>
 internal FileValidationException(FileValidation fileValidation, string message, FileRecord record = null) : base(message)
 {
     FileValidation = fileValidation;
     Record         = record;
 }
Exemple #4
0
 /// <summary>
 /// Compose the underlying children records.
 /// </summary>
 private void ComposeChildren(List <FileRecord> records, FileRecordReflector frf, FileRecord current)
 {
     foreach (var child in frf.Children)
     {
         var obj = child.GetValue(current.Value);
         if (child.IsCollection)
         {
             foreach (var item in (System.Collections.IEnumerable)obj)
             {
                 ComposeRecord(records, child.PropertyType, current.Level + 1, child.RecordIdentifier, item, true);
             }
         }
         else
         {
             ComposeRecord(records, child.PropertyType, current.Level + 1, child.RecordIdentifier, obj, true);
         }
     }
 }
Exemple #5
0
 /// <summary>
 /// Enables post-processing when writing the line data <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="record">The related <see cref="FileRecord"/>.</param>
 /// <param name="sb">The line data <see cref="StringBuilder"/>.</param>
 internal void WritePostProcessLineDataInternal(FileRecord record, StringBuilder sb)
 {
     WritePostProcessLineData(record, sb);
 }
Exemple #6
0
 /// <summary>
 /// Enables post-processing when writing the line data <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="record">The related <see cref="FileRecord"/>.</param>
 /// <param name="sb">The line data <see cref="StringBuilder"/>.</param>
 protected virtual void WritePostProcessLineData(FileRecord record, StringBuilder sb)
 {
 }
Exemple #7
0
 /// <summary>
 /// Writes the indexed <paramref name="column"/> from the <paramref name="record"/> to the line data <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="fcr">The corresponding <see cref="FileColumnReflector"/> configuration.</param>
 /// <param name="record">The related <see cref="FileRecord"/>.</param>
 /// <param name="column">The column index.</param>
 /// <param name="sb">The line data <see cref="StringBuilder"/>.</param>
 /// <returns><c>true</c> indicates that the column write was successful; otherwise, <c>false</c>.</returns>
 /// <remarks>Implementers must use <see cref="FileColumnReflector"/> <see cref="FileColumnReflector.StringWidthCorrector(FileRecord, ref string)"/> to
 /// validate and correct the column string prior to updating the line data to ensure that the configured file and column formatting rules are adhered to.</remarks>
 internal bool WriteColumnToLineDataInternal(FileColumnReflector fcr, FileRecord record, int column, StringBuilder sb)
 {
     return(WriteColumnToLineData(fcr, record, column, sb));
 }
Exemple #8
0
 /// <summary>
 /// Writes the indexed <paramref name="column"/> from the <paramref name="record"/> to the line data <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="fcr">The corresponding <see cref="FileColumnReflector"/> configuration.</param>
 /// <param name="record">The related <see cref="FileRecord"/>.</param>
 /// <param name="column">The column index.</param>
 /// <param name="sb">The line data <see cref="StringBuilder"/>.</param>
 /// <returns><c>true</c> indicates that the column write was successful; otherwise, <c>false</c>.</returns>
 /// <remarks>Implementers must use <see cref="FileColumnReflector"/> <see cref="FileColumnReflector.StringWidthCorrector(FileRecord, ref string)"/> to
 /// validate and correct the column string prior to updating the line data to ensure that the configured file and column formatting rules are adhered to.</remarks>
 protected abstract bool WriteColumnToLineData(FileColumnReflector fcr, FileRecord record, int column, StringBuilder sb);
Exemple #9
0
 /// <summary>
 /// Read the <see cref="FileRecord"/> creating the corresponding value (<paramref name="type"/> instance).
 /// </summary>
 /// <param name="record">The <see cref="FileRecord"/>.</param>
 /// <param name="type">The identified <see cref="Type"/> to be created.</param>
 /// <returns>The <see cref="Type"/> instance.</returns>
 internal object ReadCreateRecordValueInternal(FileRecord record, Type type)
 {
     return(ReadCreateRecordValue(record, type));
 }
Exemple #10
0
 /// <summary>
 /// Read the <see cref="FileRecord"/> creating the corresponding value (<paramref name="type"/> instance).
 /// </summary>
 /// <param name="record">The <see cref="FileRecord"/>.</param>
 /// <param name="type">The identified <see cref="Type"/> to be created.</param>
 /// <returns>The <see cref="Type"/> instance.</returns>
 protected abstract object ReadCreateRecordValue(FileRecord record, Type type);
Exemple #11
0
 /// <summary>
 /// Reads the <see cref="FileRecord"/> extracting the <see cref="FileRecord.RecordIdentifier"/>.
 /// </summary>
 /// <param name="record">The <see cref="FileRecord"/>.</param>
 /// <returns>The record identifier where applicable; otherwise, <c>null</c>.</returns>
 protected abstract string ReadRecordIdentifier(FileRecord record);
Exemple #12
0
 /// <summary>
 /// Reads the <see cref="FileRecord"/> extracting the <see cref="FileRecord.RecordIdentifier"/>.
 /// </summary>
 /// <param name="record">The <see cref="FileRecord"/>.</param>
 /// <returns>The record identifier where applicable; otherwise, <c>null</c>.</returns>
 internal string ReadRecordIdentifierInternal(FileRecord record)
 {
     return(ReadRecordIdentifier(record));
 }
Exemple #13
0
        /// <summary>
        /// Writes the indexed <paramref name="column"/> from the <paramref name="record"/> to the line data <see cref="StringBuilder"/>.
        /// </summary>
        /// <param name="fcr">The corresponding <see cref="FileColumnReflector"/> configuration.</param>
        /// <param name="record">The related <see cref="FileRecord"/>.</param>
        /// <param name="column">The column index.</param>
        /// <param name="sb">The line data <see cref="StringBuilder"/>.</param>
        /// <returns><c>true</c> indicates that the column write was successful; otherwise, <c>false</c>.</returns>
        protected override bool WriteColumnToLineData(FileColumnReflector fcr, FileRecord record, int column, StringBuilder sb)
        {
            // Delimit each column.
            Check.NotNull(sb, nameof(sb));
            if (column > 0)
            {
                sb.Append(Delimiter);
            }

            // Get the string value and correct the width if needed.
            var str = column > Check.NotNull(record, nameof(record)).Columns.Count ? null : record.Columns[column];

            // Override if it is the hierarchy column.
            if (HierarchyColumnIndex.HasValue && HierarchyColumnIndex.Value == column)
            {
                str = record.RecordIdentifier;
            }

            // Where null this means there is nothing specific to write.
            if (string.IsNullOrEmpty(str))
            {
                return(true);
            }

            // Validate/correct the string value to ensure column width conformance.
            if (!Check.NotNull(fcr, nameof(fcr)).StringWidthCorrector(record, ref str))
            {
                return(false);
            }

            // Check if the column content contains the delimiter and handle accordingly.
            var qualify = fcr.PropertyTypeCode == TypeCode.String && TextQualifier != NoCharacter && !TextQualifierOnlyWithDelimiterOnWrite;

            if (str.IndexOf(Delimiter) >= 0)
            {
                if (TextQualifier == NoCharacter)
                {
                    record.Messages.Add(MessageType.Error, "Text delimiter character found inside column text; no text qualifier has been specified and would result in errant record.");
                    return(false);
                }
                else
                {
                    qualify = true;
                }
            }

            // Double qualify a qualifier inside of the text.
            if (TextQualifier != NoCharacter)
            {
                if (!qualify && str.IndexOf(TextQualifier) >= 0)
                {
                    qualify = true;
                }

                if (qualify)
                {
                    str = str.Replace(TextQualifier.ToString(System.Globalization.CultureInfo.InvariantCulture), new string(TextQualifier, 2));
                }
            }

            if (qualify)
            {
                sb.Append(TextQualifier);
            }

            sb.Append(str);

            if (qualify)
            {
                sb.Append(TextQualifier);
            }

            return(true);
        }
Exemple #14
0
        /// <summary>
        /// Reads the <see cref="FileRecord"/> extracting the <see cref="FileRecord.RecordIdentifier"/> and <see cref="FileRecord.Columns"/>.
        /// </summary>
        /// <param name="record">The <see cref="FileRecord"/>.</param>
        /// <returns>The record identifier where applicable; otherwise, <c>null</c>.</returns>
        protected override string ReadRecordIdentifier(FileRecord record)
        {
            char c           = Char.MinValue;
            var  sb          = new StringBuilder(256);
            var  cols        = new List <string>();
            bool isQualified = false;

            // Iterate and split the record data into multiple columns.
            for (int i = 0; i < Check.NotNull(record, nameof(record)).LineData.Length; i++)
            {
                c = record.LineData[i];
                if (c == Delimiter)
                {
                    // Where not qualified it is the end of the column.
                    if (!isQualified)
                    {
                        cols.Add(sb.ToString());
                        sb.Clear();
                        continue;
                    }
                }

                if (c == TextQualifier)
                {
                    if (isQualified)
                    {
                        // Where end of record data, this is terminating the column correctly.
                        if (i >= record.LineData.Length - 1)
                        {
                            isQualified = false;
                            break;
                        }

                        // Where next character is delimiter, then terminating column correctly.
                        if (record.LineData[i + 1] == Delimiter)
                        {
                            isQualified = false;
                            continue;
                        }

                        // Where next charater is same, then it is escaped as single char.
                        if (record.LineData[i + 1] == TextQualifier)
                        {
                            sb.Append(TextQualifier);
                            i++;
                            continue;
                        }

                        // Handle the text qualifier issue.
                        switch (TextQualifierHandling)
                        {
                        case TextQualifierHandling.LooseAllow:
                            record.Messages.Add(MessageType.Warning, QualifierInsideQualifiedText, i + 1);
                            break;

                        case TextQualifierHandling.LooseSkip:
                            record.Messages.Add(MessageType.Warning, QualifierInsideQualifiedText, i + 1);
                            continue;

                        default:
                            record.Messages.Add(MessageType.Error, QualifierInsideQualifiedText, i + 1);
                            return(null);
                        }
                    }
                    else
                    {
                        if (sb.Length == 0)
                        {
                            isQualified = true;
                            continue;
                        }

                        if (i < record.LineData.Length - 1 && record.LineData[i + 1] == TextQualifier)
                        {
                            sb.Append(TextQualifier);
                            i++;
                            continue;
                        }

                        // Handle the text qualifier issue.
                        switch (TextQualifierHandling)
                        {
                        case TextQualifierHandling.LooseAllow:
                            record.Messages.Add(MessageType.Warning, QualifierInsideUnqualifiedText, i + 1);
                            break;

                        case TextQualifierHandling.LooseSkip:
                            record.Messages.Add(MessageType.Warning, QualifierInsideUnqualifiedText, i + 1);
                            continue;

                        default:
                            record.Messages.Add(MessageType.Error, QualifierInsideUnqualifiedText, i + 1);
                            return(null);
                        }
                    }
                }

                sb.Append(c);
            }

            // Finish up the column processing.
            if (isQualified)
            {
                switch (TextQualifierHandling)
                {
                case TextQualifierHandling.LooseAllow:
                case TextQualifierHandling.LooseSkip:
                    record.Messages.Add(MessageType.Warning, QualifierNotClosedText);
                    break;

                default:
                    record.Messages.Add(MessageType.Error, QualifierNotClosedText);
                    return(null);
                }
            }

            if (sb.Length > 0)
            {
                cols.Add(sb.ToString());
            }

            record.Columns = cols;

            // Determine the record identifier.
            if (IsHierarchical)
            {
                if (HierarchyColumnIndex >= record.Columns.Count)
                {
                    record.Messages.Add(MessageType.Error, "Unable to determine the code identitier as the hierarchy column index is outside the bounds of the number of columns found for the record.");
                }
                else
                {
                    return(record.Columns[HierarchyColumnIndex.Value]);
                }
            }

            return(null);
        }
Exemple #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FileReaderLoggerData"/> class.
 /// </summary>
 /// <param name="operationResult">The <see cref="FileOperationResult"/>.</param>
 /// <param name="fileRecord">The corresponding <see cref="T:FileRecord"/> to the logged message.</param>
 /// <param name="messageItem">The specific <see cref="T:MessageItem"/> from the <see cref="FileRecord"/> being logged.</param>
 public FileReaderLoggerData(FileOperationResult operationResult, FileRecord fileRecord = null, MessageItem messageItem = null)
 {
     OperationResult = operationResult ?? throw new ArgumentNullException(nameof(operationResult));
     FileRecord      = fileRecord;
     MessageItem     = messageItem;
 }