/// <summary> /// Return next EDI transaction set. /// IFatpipeDocument will contain ISA, GA and 1 ST segment /// </summary> /// <returns></returns> public IFatpipeDocument GetNextTransactionSet() { if (DocumentReader == null) { throw new EDIReaderException("Initialize API should be invoked successfully before calling GetNextTransactionSet"); } string location = "EDIReader.GetNextTransactionSet"; Stopwatch sw = new Stopwatch(); sw.Start(); Logger.Debug(location, "Start - {0}", GetCurrentPosContext()); bool isTransactionSetFinished = false; while (isTransactionSetFinished == false && (CurrentSegmentDetails = ReadNextSegment()) != null && CurrentSegmentDetails.Length > 0) { Logger.Debug(location, "{0} - Next segment {1}", GetCurrentPosContext(), CurrentSegmentDetails[0]); EDIState nextState = GetEDIState(CurrentSegmentDetails[0]); switch (nextState) { case EDIState.GS: if (!(LastState == EDIState.ISA || LastState == EDIState.GE)) { throw new EDIReaderException(string.Format("GS segment should appear only after ISA or GE. {1}", GetCurrentPosContext())); } //Just set GSRecordFields, GS node will be constructed on first ST node GSRecordFields = CurrentSegmentDetails; GSSegmentProcessed = false; GSSegment = CurrentSegment; FunctionalGroupNumber++; // TODO: What is functionalId and ControlNumber? - set second and third parameter below Errors.AddFunctionalGroupDetails(FunctionalGroupNumber, "", "", true); break; case EDIState.GE: // TODO: Remove GS check here in case empty GS segment is not valid scenario if (!(LastState == EDIState.GS || LastState == EDIState.SE)) { throw new EDIReaderException(string.Format("GE segment should appear only after GS or SE. {1}", GetCurrentPosContext())); } //TODO: Add validation of GE segment details values //CheckForMissingSegments(CurrentSegmentDetails[0]); break; case EDIState.ST: if (!(LastState == EDIState.GS || LastState == EDIState.SE)) { throw new EDIReaderException(string.Format("ST segment should appear only after GS or SE. {1}", GetCurrentPosContext())); } // Do not reset SegmentNumber as it's being used as over all segment number in EdiValidator for position //SegmentNumber = 0; TransactionSetNumber++; // TODO: What is functionalId and ControlNumber? - set second and third parameter below Errors.AddTransactionSetDetails(TransactionSetNumber, "", "", true); FatpipeDocumentInst.BeautifiedOriginalPayloadBody = string.Empty; TransactionSegment = CurrentSegment; FormattedTransactionSegment = CurrentSegment; // If ST segment is not valid then move file reading pointer to SE segment if (ProcessSTSegment() == false) { MoveToSESegment(); isTransactionSetFinished = true; nextState = EDIState.SE; } break; case EDIState.SE: if (!(LastState == EDIState.ST || LastState == EDIState.Other)) { throw new EDIReaderException(string.Format("{0} segment should appear only after ST or Other. {1}", LastState, GetCurrentPosContext())); } //CheckForMissingSegments(CurrentSegmentDetails[0]); CreateAndAddNewSegment(CurrentSegmentDetails[0], CurrentSegmentDetails); //TODO: Add validation of SE segment details values isTransactionSetFinished = true; TransactionSegment = TransactionSegment + SegmentDelimiter + CurrentSegment; FormattedTransactionSegment = FormattedTransactionSegment + FormattedSegmentDelimiter + CurrentSegment; //ValidateContingencies(); break; case EDIState.Other: if (!(LastState == EDIState.ST || LastState == EDIState.Other)) { throw new EDIReaderException(string.Format("{0} segment should appear only after ST or Other. {1}", LastState, GetCurrentPosContext())); } //Console.WriteLine("segment = " + CurrentSegmentDetails[0]); CreateAndAddNewSegment(CurrentSegmentDetails[0], CurrentSegmentDetails); TransactionSegment = TransactionSegment + SegmentDelimiter + CurrentSegment; FormattedTransactionSegment = FormattedTransactionSegment + FormattedSegmentDelimiter + CurrentSegment; break; case EDIState.IEA: //TODO: Add validation of IEA segment details values break; } LastState = nextState; } // Construct original payload if (isTransactionSetFinished == true) { FatpipeDocumentInst.OriginalPayload = ISASegment + SegmentDelimiter; FatpipeDocumentInst.OriginalPayload += GSSegment + SegmentDelimiter; FatpipeDocumentInst.OriginalPayload += TransactionSegment + SegmentDelimiter; FatpipeDocumentInst.OriginalPayload += "GE" + EDIDelimiters.FieldSeperator + "1" + EDIDelimiters.FieldSeperator + "1" + SegmentDelimiter; FatpipeDocumentInst.OriginalPayload += "IEA" + EDIDelimiters.FieldSeperator + "1" + EDIDelimiters.FieldSeperator + "1" + SegmentDelimiter; FatpipeDocumentInst.BeautifiedOriginalPayloadStartHeader = ISASegment + FormattedSegmentDelimiter; FatpipeDocumentInst.BeautifiedOriginalPayloadStartHeader += GSSegment + FormattedSegmentDelimiter; FatpipeDocumentInst.BeautifiedOriginalPayloadBody += FormattedTransactionSegment + FormattedSegmentDelimiter; FatpipeDocumentInst.BeautifiedOriginalPayloadEndHeader = "GE" + EDIDelimiters.FieldSeperator + "1" + EDIDelimiters.FieldSeperator + "1" + FormattedSegmentDelimiter; FatpipeDocumentInst.BeautifiedOriginalPayloadEndHeader += "IEA" + EDIDelimiters.FieldSeperator + "1" + EDIDelimiters.FieldSeperator + "1" + FormattedSegmentDelimiter; } sw.Stop(); Logger.Debug(location, "Stop - {0}. Elapsed time {1} ms", GetCurrentPosContext(), sw.ElapsedMilliseconds); return(isTransactionSetFinished == true ? FatpipeDocumentInst : null); }
/// <summary> /// Read xml file and construct IFatpipeDocument. /// Xml file reader will traverse Xml files and for each element /// match it with current pluglet. If match fails then it tries to /// find matching pluglet (similar to X12). /// </summary> /// <returns></returns> public IFatpipeDocument ReadFile(Stream xmlFileStream, IDocumentPlug documentPlug) { if (xmlFileStream == null) { throw new ArgumentNullException("xmlFileStream", "Xml file stream cannot be null"); } if (documentPlug == null) { throw new ArgumentNullException("documentPlug", "Document plug cannot be null"); } string location = "XmlFileReader.ReadFile"; Logger.Debug(location, "Start"); BeautifiedOriginalPayload = string.Empty; CurrentElementNumber = 0; TotalPayloadLength = 0; CurrentLinePayloadStart = 0; CurrentLinePayloadEnd = 0; CurrentLevel = 0; Stopwatch sw = new Stopwatch(); sw.Start(); errors = new InterchangeErrors(); // Since xml file doesn't have concept of ST/SE, ans we want to use InterchangeErrors for reporting purpose // create dummy transaction set details Errors.AddTransactionSetDetails(1, "", "", true); IPluglet currentPluglet = documentPlug.RootPluglet; currentPluglet.ResetCurrentOccurances(); currentPluglet.InitializeStartSegmentList(); FatpipeDocumentInst = new FatpipeDocument(); FatpipeDocumentInst.DocumentPlug = documentPlug; FatpipeDocumentInst.RootFragment = currentPluglet.ConstructDocumentFragment(null, null); IDocumentFragment currentDocumentFragment = FatpipeDocumentInst.RootFragment; IDocumentFragment newDocumentFragment = null; bool isLeafNode = false; try { XmlTextReader xmlReader = new XmlTextReader(xmlFileStream); // If some element doesn't match document plutlet then stop // TODO: Should we try to match other elements? If yes, which pluglet to start with? // Also we need to ignore this entire element bool stopProcessing = false; while (xmlReader.Read()) { switch (xmlReader.NodeType) { case XmlNodeType.Element: isLeafNode = false; AddStartElementToPayload(xmlReader.Name, xmlReader.IsEmptyElement); if (xmlReader.IsEmptyElement) { break; } if (stopProcessing == false) { currentDocumentFragment = ConstructNewDocumentFragment(xmlReader.Name, currentPluglet, currentDocumentFragment); } // If some element doesn't match document plutlet then stop // TODO: Should we try to match other elements? If yes, which pluglet to start with? // Also we need to ignore this entire element if (currentDocumentFragment == null) { stopProcessing = true; } else { currentPluglet = currentDocumentFragment.Pluglet; } CurrentLevel++; CurrentElementNumber++; break; case XmlNodeType.Text: isLeafNode = true; AddValueToPayload(xmlReader.Value); if (stopProcessing == false) { currentDocumentFragment.Value = xmlReader.Value; } // Assumption: Leaf nodes are on same line and non-leaf nodes are 1-per line (separate line for start and end element) // If this assumption is wrong then we can construct the xml in string format to match fulfill above assumption // and then Ux can use this string version of xml to highlight the errors. CurrentElementNumber--; // Decrement since leaf level elements are one line, so we need to increment element on endElement only. break; case XmlNodeType.EndElement: CurrentLevel--; AddEndElementToPayload(xmlReader.Name, isLeafNode); if (stopProcessing == false) { // Check if all mandatory segments were present CheckMissingMandatoryElements(currentPluglet, currentDocumentFragment); currentDocumentFragment = currentDocumentFragment.Parent; currentPluglet = currentPluglet.Parent; } CurrentElementNumber++; isLeafNode = false; break; default: break; } } } catch (XmlException xmlException) { // TODO: Pass start and end postition Errors.AddGenericError(currentPluglet == null ? "N/A" : currentPluglet.Name, X12ErrorCode.UnexpectedSegmentCode, string.Format("Error parsing XML document: {0}", xmlException.Message), CurrentElementNumber / 2, CurrentLinePayloadStart + TotalPayloadLength, CurrentLinePayloadEnd + TotalPayloadLength); } catch (Exception exception) { // TODO: Pass start and end postition (for all Errors.Add* calls) in this file. Errors.AddGenericError(currentPluglet == null ? "N/A" : currentPluglet.Name, X12ErrorCode.UnexpectedSegmentCode, "Internal error occurred, please contact Maarg", CurrentElementNumber / 2, CurrentLinePayloadStart + TotalPayloadLength, CurrentLinePayloadEnd + TotalPayloadLength); Logger.Error(location, EventId.XmlReaderUnhandledException, "Error occured during xml file processing: {0}", exception.ToString()); } FatpipeDocumentInst.BeautifiedOriginalPayloadBody = BeautifiedOriginalPayload; sw.Stop(); Logger.Debug(location, "Stop - Elapsed time {0} ms", sw.ElapsedMilliseconds); return(FatpipeDocumentInst); }
/// <summary> /// Return flat file input and construct IFatpipeDocument /// IFatpipeDocument will NOT contain ISA and GA segments as it's not /// present in case of flat file. /// Since ISA segment is missing and there is no speific ST segment /// DocumentPlug is mandatory parameter. /// </summary> /// <returns></returns> public IFatpipeDocument ReadFile(Stream flatFileStream, IDocumentPlug documentPlug) { if (flatFileStream == null) { throw new ArgumentNullException("flatFileStream", "Flat file stream cannot be null"); } if (documentPlug == null) { throw new ArgumentNullException("documentPlug", "Document plug cannot be null"); } string location = "FlatFileReader.ReadFile"; Logger.Debug(location, "Start"); Stopwatch sw = new Stopwatch(); sw.Start(); errors = new InterchangeErrors(); DocumentPlug = documentPlug; long orgStartPos = flatFileStream.Position; FlatFileDelimiters = InitializeDelimiters(documentPlug); SegmentDelimiter = ((char)FlatFileDelimiters.SegmentDelimiter).ToString(); FormattedSegmentDelimiter = SegmentDelimiter; bool crLFPresent = FlatFileDelimiters.SegmentDelimiter == Delimiters.CarriageReturn || FlatFileDelimiters.SegmentDelimiterSuffix1 == Delimiters.CarriageReturn; if (!crLFPresent) { FormattedSegmentDelimiter = string.Format("{0}{1}", FormattedSegmentDelimiter, Environment.NewLine); } SegmentNumberFromStart = 0; CurrentSegmentStartPos = 0; this.DocumentReader = new StreamReader(flatFileStream, Encoding.UTF8); // Since flat file doesn't have concept of ST/SE, ans we want to use InterchangeErrors for reporting purpose // create dummy transaction set details Errors.AddTransactionSetDetails(1, "", "", true); CurrentPluglet = DocumentPlug.RootPluglet; CurrentPluglet.ResetCurrentOccurances(); // do we need this for flat files? CurrentPluglet.InitializeStartSegmentList(); FatpipeDocumentInst = new FatpipeDocument(); //FatpipeDocumentInst.TransactionSetType = ???; FatpipeDocumentInst.DocumentPlug = DocumentPlug; FatpipeDocumentInst.RootFragment = CurrentPluglet.ConstructDocumentFragment(null, null); while ((CurrentSegmentDetails = ReadNextSegment()) != null && CurrentSegmentDetails.Length > 0) { Logger.Debug(location, "{0} - Next segment {1}", GetCurrentPosContext(), CurrentSegmentDetails[0]); CreateAndAddNewSegment(CurrentSegmentDetails[0], CurrentSegmentDetails); if (string.IsNullOrWhiteSpace(FatpipeDocumentInst.OriginalPayload)) { FatpipeDocumentInst.OriginalPayload = CurrentSegment; } else { FatpipeDocumentInst.OriginalPayload += SegmentDelimiter + CurrentSegment; } if (string.IsNullOrWhiteSpace(FatpipeDocumentInst.BeautifiedOriginalPayloadBody)) { FatpipeDocumentInst.BeautifiedOriginalPayloadBody = CurrentSegment; } else { FatpipeDocumentInst.BeautifiedOriginalPayloadBody += FormattedSegmentDelimiter + CurrentSegment; } } sw.Stop(); Logger.Debug(location, "Stop - Elapsed time {0} ms", sw.ElapsedMilliseconds); return(FatpipeDocumentInst); }