/// <summary>
        /// Determine if data kind on intake must allow fields to be added "on the fly" (i.e. after the first row).
        /// Note that if true, then OnTheFlyInputFieldsCanBeAllowed must also be true.
        /// </summary>
        /// <param name="dataKind"></param>
        /// <returns>True means that on-the-fly fields must be allowed irrespective of AllowOnTheFlyInputFields); false means that on-the-fly fields may be disallowed via AllowOnTheFlyInputFields).</returns>
        internal static bool OnTheFlyInputFieldsAreAlwasyAllowed(this KindOfTextData dataKind)
        {
            switch (dataKind)
            {
            case KindOfTextData.X12:
                return(true);

            default:
                return(false);
            }
        }
        /// <summary>
        /// Define the type of feeder/dispenser to be used on intake/ouput.
        /// </summary>
        /// <param name="dataKind"></param>
        /// <returns></returns>
        internal static ExternalLineType ExternalLineType(this KindOfTextData dataKind)
        {
            switch (dataKind)
            {
            case KindOfTextData.X12:
            case KindOfTextData.HL7:
                return(Common.ExternalLineType.Xsegment);

            case KindOfTextData.XML:
            case KindOfTextData.JSON:
            case KindOfTextData.UnboundJSON:
                return(Common.ExternalLineType.Xrecord);

            default:
                return(Common.ExternalLineType.Xtext);
            }
        }
        /// <summary>
        /// Determine if is possible for the fields to be added "on the fly" (i.e. after the first row) on intake
        /// </summary>
        /// <param name="inputDataKind">Kind of input data</param>
        /// <returns>True when on-the-fly fields are allowed (assuming AllowOnTheFlyInputFields=true); false when not (regardless of AllowOnTheFlyInputFields)</returns>
        internal static bool OnTheFlyInputFieldsCanBeAllowed(this KindOfTextData inputDataKind)
        {
            switch (inputDataKind)
            {
            case KindOfTextData.Keyword:
            case KindOfTextData.Delimited:
            case KindOfTextData.X12:
            case KindOfTextData.XML:
            case KindOfTextData.JSON:
            case KindOfTextData.UnboundJSON:
                //records of these kinds "can grow", i.e. it is possible for fields to be added on the fly
                return(true);

            case KindOfTextData.Raw:
            case KindOfTextData.Flat:
            case KindOfTextData.Arbitrary:
                //these kinds must have all fields fixed after 1st row (even if AllowOnTheFlyInputFields is true)
                return(false);

            default:
                //IMPORTANT: Make sure each newly implemented data kind is assigned to either "fixed" or "can grow" category
                throw new NotSupportedException($"Unrecognized {inputDataKind} kind encountered.");
            }
        }
        //TODO: Replace these extension methods by properties of the respective classes (derived from Intake/OutputProvider - Strategy pattern)

        /// <summary>
        /// Determine if the type supports possibility for column headers to be in the 1st row
        /// </summary>
        /// <param name="dataKind">Kind of text data (either input or output)</param>
        /// <returns>True if it is possible to have header row; false if not, in which case the HeadersInFirstInputRow or HeadersInFirstOutputRow setting is ignored</returns>
        internal static bool CanHaveHeaderRow(this KindOfTextData dataKind)
        {
            switch (dataKind)
            {
            case KindOfTextData.Raw:
            case KindOfTextData.Keyword:
            case KindOfTextData.Arbitrary:
            case KindOfTextData.X12:
            case KindOfTextData.XML:
            case KindOfTextData.JSON:
            case KindOfTextData.UnboundJSON:
                //"speedy" data kinds, they can start output without knowing the output fields
                return(false);

            case KindOfTextData.Delimited:
            case KindOfTextData.Flat:
                //"needy" data kinds, they can't start output unless all fields are known
                return(true);

            default:
                //IMPORTANT: Make sure each newly implemented data kind is assigned to either "speedy" or "needy" category
                throw new NotSupportedException($"Unrecognized {dataKind} kind encountered.");
            }
        }
Example #5
0
 /// <summary>
 /// Helper method to create a single constituent feeder.
 /// </summary>
 /// <param name="reader"></param>
 /// <param name="sourceNo"></param>
 /// <param name="inputDataKind"></param>
 /// <param name="intakeIsAsync"></param>
 /// <param name="skipHeader"></param>
 /// <param name="x12SegmentDelimiter"></param>
 /// <param name="xmlJsonSettings"></param>
 /// <returns></returns>
 private static LineFeederForSource CreateLineFeeder(TextReader reader, int sourceNo, KindOfTextData inputDataKind, bool intakeIsAsync, bool skipHeader, string x12SegmentDelimiter, string xmlJsonSettings)
 {
     if (inputDataKind.ExternalLineType() == ExternalLineType.Xtext)
     {
         return(new TextFeederForSource(reader, sourceNo, skipHeader));
     }
     if (inputDataKind == KindOfTextData.X12)
     {
         return(new X12FeederForSource(reader, sourceNo, x12SegmentDelimiter));
     }
     if (inputDataKind == KindOfTextData.XML)
     {
         return(new XmlFeederForSource(reader, sourceNo, xmlJsonSettings, intakeIsAsync));
     }
     if (inputDataKind == KindOfTextData.JSON)
     {
         return(new JsonFeederForSource(reader, sourceNo, xmlJsonSettings, intakeIsAsync));
     }
     if (inputDataKind == KindOfTextData.UnboundJSON)
     {
         return(new UnboundJsonFeederForSource(reader, sourceNo, xmlJsonSettings));
     }
     throw new NotSupportedException($"Feeder type for {inputDataKind} could not be determined.");
 }
Example #6
0
        /// <summary>
        /// Create feeder based on arbitrary set of readers.
        /// </summary>
        /// <param name="readers"></param>
        /// <param name="inputDataKind"></param>
        /// <param name="intakeIsAsync"></param>
        /// <param name="skipRepeatedHeaders"></param>
        /// <param name="x12SegmentDelimiter">Any non-default char means X12, i.e. segments</param>
        /// <param name="xmlJsonSettings"></param>
        /// <returns></returns>
        internal static ILineFeeder CreateLineFeeder(IEnumerable <TextReader> readers, KindOfTextData inputDataKind, bool intakeIsAsync, bool skipRepeatedHeaders, string x12SegmentDelimiter, string xmlJsonSettings)
        {
            int  sourceNo   = 1;
            bool skipHeader = false; // false for 1st source, same as skipRepeatedHeaders for remaining sources

            return(new LineFeeder(readers.Select(r =>
            {
                var feeder = CreateLineFeeder(r, sourceNo++, inputDataKind, intakeIsAsync, skipHeader, x12SegmentDelimiter, xmlJsonSettings);
                skipHeader = skipRepeatedHeaders;
                return feeder;
            }).ToList()));
            //Here, ToList is needed to prevent multiple iterations over readers (which would've messed up sourceNo closure); besides LineFeeder ctor demands IList (not IEnumerable)
        }
Example #7
0
 /// <summary>
 /// Create a feeder based on file names.
 /// </summary>
 /// <param name="files"></param>
 /// <param name="inputDataKind"></param>
 /// <param name="intakeIsAsync"></param>
 /// <param name="skipRepeatedHeaders"></param>
 /// <param name="x12SegmentDelimiter">non-default char means X12 mode</param>
 /// <param name="xmlJsonSettings"></param>
 /// <returns></returns>
 internal static ILineFeeder CreateLineFeeder(IEnumerable <string> files, KindOfTextData inputDataKind, bool intakeIsAsync, bool skipRepeatedHeaders, string x12SegmentDelimiter, string xmlJsonSettings)
 {
     return(CreateLineFeeder(files.Select(f => File.OpenText(f)).ToList(), inputDataKind, intakeIsAsync, skipRepeatedHeaders, x12SegmentDelimiter, xmlJsonSettings)); // OpenText may throw
     // Note the ToList above; its purpose is to force eager evaluation, so that exception (e.g. FileNotFoundException) is thrown (& caught)
     // during IntakeProvider initialization (InitIntake method); otherwise, the exception would've been deferred until start of reading records.
 }
 /// <summary>
 /// Determine if output processing needs to know all output fields before starting output
 /// </summary>
 /// <param name="outputDataKind">Kind of output data</param>
 /// <returns>True for "needy" kinds (output fields needed up-front), false for "speedy" data kinds (output doesn't need to know all fields at start)</returns>
 internal static bool OutputFieldsAreNeededUpFront(this KindOfTextData outputDataKind)
 {
     return(outputDataKind.CanHaveHeaderRow());
     //both settings CanHaveHeaderRow and OutputFieldsAreNeededUpFront are synonymous, but they are kept separate for clarity of intents
 }
 /// <summary>
 /// Helper method to create constituent dispenser: arbitrary writer, target number, X12 indicator provided
 /// </summary>
 /// <param name="writer"></param>
 /// <param name="targetNo"></param>
 /// <param name="outputDataKind"></param>
 /// <param name="outputIsAsync"></param>
 /// <param name="x12SegmentDelimiter"></param>
 /// <param name="xmlSettings"></param>
 /// <returns></returns>
 private static LineDispenserForTarget CreateLineDispenser(TextWriter writer, int targetNo, KindOfTextData outputDataKind, bool outputIsAsync, Lazy<string> x12SegmentDelimiter, string xmlSettings)
 {
    if (outputDataKind.ExternalLineType() == ExternalLineType.Xtext) return new TextDispenserForTarget(writer, targetNo);
    if (outputDataKind == KindOfTextData.X12) return new X12DispenserForTarget(writer, targetNo, x12SegmentDelimiter);
    if (outputDataKind == KindOfTextData.XML) return new XmlDispenserForTarget(writer, targetNo, xmlSettings, outputIsAsync);
    if (outputDataKind == KindOfTextData.JSON) return new JsonDispenserForTarget(writer, targetNo, xmlSettings, outputIsAsync);
    if (outputDataKind == KindOfTextData.UnboundJSON) return new UnboundJsonDispenserForTarget(writer, targetNo, xmlSettings);
    throw new NotSupportedException($"Dispenser type for {outputDataKind} could not be determined.");
 }
 /// <summary>
 /// Creates LineDispenser for a collection of text writers (generates sequential target numbers).
 /// </summary>
 /// <param name="writers"></param>
 /// <param name="outputDataKind"></param>
 /// <param name="outputIsAsync"></param>
 /// <param name="x12SegmentDelimiter">Segment delimiter in case of X12 output (may include whitespace, e.g. "~\r\n"); null otherwise.</param>
 /// <param name="xmlSettings"></param>
 /// <returns></returns>
 internal static ILineDispenser CreateLineDispenser(IEnumerable<TextWriter> writers, KindOfTextData outputDataKind, bool outputIsAsync, Lazy<string> x12SegmentDelimiter, string xmlSettings)
 {
    int targetNo = 1;
    return new LineDispenser(writers.Select(w =>
    {
       var dispTarget = CreateLineDispenser(w, targetNo++, outputDataKind, outputIsAsync, x12SegmentDelimiter, xmlSettings);
       return dispTarget;
    }).ToList());
    //Here, ToList is needed to prevent multiple iterations over writers (which would've messed up targetNo closure); besides LineDispenser ctor demands IList (not IEnumerable)
 }
 /// <summary>
 /// Creates LineDispenser for a collection of file names and boolean flags
 /// </summary>
 /// <param name="files"></param>
 /// <param name="outputDataKind"></param>
 /// <param name="outputIsAsync"></param>
 /// <param name="x12SegmentDelimiter">Segment delimiter in case of X12 output (may include whitespace, e.g. "~\r\n"); null otherwise.</param>
 /// <param name="xmlSettings"></param>
 /// <returns></returns>
 internal static ILineDispenser CreateLineDispenser(IEnumerable<string> files, KindOfTextData outputDataKind, bool outputIsAsync, Lazy<string> x12SegmentDelimiter, string xmlSettings)
 {
    return CreateLineDispenser(files.Select(f => File.CreateText(f)).ToList(), outputDataKind, outputIsAsync, x12SegmentDelimiter, xmlSettings);  // CreateText may throw
    // Note the ToList above; its purpose is to force eager evaluation, so that exception (e.g. UnauthorizedAccessException) is thrown (& caught)
    // during OutputProvider initialization (InitOutput method); otherwise, the exception would've been deferred until start of writing records.
 }