/// <summary> /// Initialize EDIReader with a stream. Verify that stream contains EDI document (ISA segment). /// </summary> /// <param name="stream"></param> /// <returns>true if EDI document otherwise false</returns> public bool Initialize(Stream stream, IFatpipeManager fatpipeManager) { Logger.Info("EDIReader.Initialize", "Start"); if (stream == null) { throw new ArgumentNullException("stream"); } Stopwatch sw = new Stopwatch(); sw.Start(); FPManager = fatpipeManager; long orgStartPos = stream.Position; Delimiters delimiters; InterchangeTokenizer tokenizer = new InterchangeTokenizer(stream); bool isValidEDIDocument = tokenizer.IsX12Interchange(out delimiters); CurrentSegmentNumber = 0; ValidTransactionSetCount = InvalidTransactionSetCount = 0; PrevTransactionSetType = 0; DocumentReader = null; if (isValidEDIDocument == true) { EDIDelimiters = delimiters; //TODO: Review following logic //Read ISA field till component separator - Do not include component separator ISASegment = tokenizer.ISARecord.Substring(0, tokenizer.ISARecord.IndexOf((char)EDIDelimiters.ComponentSeperator) + 1); //TODO: Suraj: confirm this special case - last value as data element separator ISARecordFields = ISASegment.Split((char)EDIDelimiters.FieldSeperator); CurrentSegmentNumber = 1; this.DocumentReader = new StreamReader(stream, Encoding.UTF8); //TODO: Why is seek required here? this.DocumentReader.BaseStream.Seek(tokenizer.ISARecordLen + orgStartPos, SeekOrigin.Begin); FatpipeDocumentInst = new FatpipeDocument(); SegmentDelimiter = ((char)EDIDelimiters.SegmentDelimiter).ToString(); } else { ISASegment = string.Empty; } LastState = EDIState.ISA; sw.Stop(); Logger.Info("EDIReader.Initialize", "Stop. Elapsed time {0} ms", sw.ElapsedMilliseconds); return(isValidEDIDocument); }
/// <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> /// Initialize EDIReader with a stream. Verify that stream contains EDI document (ISA segment). /// </summary> /// <param name="stream"></param> /// <param name="fatpipeManager"></param> /// <param name="documentPlug"></param> /// <returns>true if EDI document otherwise false</returns> public bool Initialize(Stream stream, IFatpipeManager fatpipeManager, IDocumentPlug documentPlug) { if (stream == null) { throw new ArgumentNullException("stream"); } Stopwatch sw = new Stopwatch(); sw.Start(); Errors = new InterchangeErrors(); FPManager = fatpipeManager; DocumentPlug = documentPlug; long orgStartPos = stream.Position; Delimiters delimiters; InterchangeTokenizer tokenizer = new InterchangeTokenizer(stream); bool isValidEDIDocument = tokenizer.IsX12Interchange(out delimiters); FunctionalGroupNumber = TransactionSetNumber = SegmentNumberFromStart = SegmentNumber = 0; ValidTransactionSetCount = InvalidTransactionSetCount = 0; PrevTransactionSetType = 0; DocumentReader = null; if (isValidEDIDocument == true) { EDIDelimiters = delimiters; //TODO: Review following logic //Read ISA field till component separator - Do not include component separator ISASegment = tokenizer.ISARecord.Substring(0, tokenizer.ISARecord.IndexOf((char)EDIDelimiters.ComponentSeperator) + 1); //TODO: Suraj: confirm this special case - last value as data element separator ISARecordFields = ISASegment.Split((char)EDIDelimiters.FieldSeperator); SegmentNumberFromStart = 1; stream.Position = orgStartPos; this.DocumentReader = new StreamReader(stream, Encoding.UTF8); //TODO: Why is seek required here? this.CurrentSegmentStartPos = 0; this.CurrentSegmentEndPos = 0; //this.CurrentSegmentEndPos = this.CurrentSegmentStartPos + tokenizer.ISARecordLen - 1; //this.DocumentReader.BaseStream.Seek(this.CurrentSegmentEndPos + basePosition, SeekOrigin.Begin); FatpipeDocumentInst = new FatpipeDocument(); FatpipeDocumentInst.BeautifiedOriginalPayloadStartHeader = string.Empty; FatpipeDocumentInst.BeautifiedOriginalPayloadBody = string.Empty; FatpipeDocumentInst.BeautifiedOriginalPayloadEndHeader = string.Empty; SegmentDelimiter = ((char)EDIDelimiters.SegmentDelimiter).ToString(); FormattedSegmentDelimiter = SegmentDelimiter; bool crLFPresent = delimiters.SegmentDelimiter == Delimiters.CarriageReturn || delimiters.SegmentDelimiterSuffix1 == Delimiters.CarriageReturn; if (!crLFPresent) { FormattedSegmentDelimiter = string.Format("{0}{1}", FormattedSegmentDelimiter, Environment.NewLine); } Logger.Info("EDIReader.Initialize", "EDIDelimiters: SegmentDelimiter={0}, FieldSeperator={1}, ComponentSeperator={2}", (char)EDIDelimiters.SegmentDelimiter, (char)EDIDelimiters.FieldSeperator, (char)EDIDelimiters.ComponentSeperator); } else //invalid document code path { this.CurrentSegmentStartPos = orgStartPos; this.CurrentSegmentEndPos = orgStartPos + 3; Logger.Error("EDIReader.Initialize", EventId.EDIReaderInvalidDocument, "EDI document is not valid. Error: {0}", tokenizer.Error); Errors.AddGenericError("ISA", SchemaErrorCode.SchemaCode100EInvalidDocType, tokenizer.Error, SegmentNumber, this.CurrentSegmentStartPos, this.CurrentSegmentEndPos); StringBuilder sb = new StringBuilder(); int errorIndex = 1; Errors.IsaIeaErrorList.WriteError(sb, ref errorIndex); ISASegment = string.Empty; } LastState = EDIState.ISA; sw.Stop(); Logger.Debug("EDIReader.Initialize", "Elapsed time {0} ms", sw.ElapsedMilliseconds); return(isValidEDIDocument); }
public static IFatpipeDocument Transform(IFatpipeDocument sourceDocument, ITransformPlug transformPlug) { if (sourceDocument == null) { throw new ArgumentNullException("sourceDocument"); } Stopwatch sw = new Stopwatch(); sw.Start(); FatpipeDocument targetDocument = new FatpipeDocument(); targetDocument.RootFragment = transformPlug.TargetDocument.RootPluglet.ConstructDocumentFragment(null, null);; //TODO: Handle loops foreach (ITransformGroup transformGroup in transformPlug.Facets) { Logger.Debug("DocumentTransformer.Transform", "Processing {0} tranformGroup", transformGroup.Name); foreach (ITransformLink transformLink in transformGroup.Links) { Logger.Debug("DocumentTransformer.Transform", "Processing Link#-{0}: {1} [{2}] => {3} [{4}]" , transformLink.Name , transformLink.Source.ReferenceType, transformLink.Source.Name , transformLink.Target.ReferenceType, transformLink.Target.Name); //TODO: Handle all kind of transformation if (transformLink.Source.ReferenceType == ReferenceType.Document && transformLink.Target.ReferenceType == ReferenceType.Document) { // Traverse source path IDocumentFragment sourceFragment = sourceDocument.RootFragment.MoveTo(transformLink.Source.Name); if (sourceFragment != null) { IPluglet targetPluglet = targetDocument.RootFragment.Pluglet.MoveTo(transformLink.Target.Name); if (targetPluglet != null) { Logger.Debug("DocumentTransformer.Transform", "Source = {0}, Target = {1}", sourceFragment.Name, targetPluglet.Tag); Dictionary <string, string> attributes = new Dictionary <string, string>(); foreach (IPluglet attr in targetPluglet.Attributes) { string attributeName = attr.Name; attributeName.Remove(0, 1); attributeName.Remove(attributeName.Length, 1); attributes.Add(attributeName, attributeName); } bool isAttribute = false; string name = transformLink.Target.Name.Substring( transformLink.Target.Name.LastIndexOf(targetPluglet.PathSeperator) + targetPluglet.PathSeperator.Length); // Check if this transformation point to attribute or leaf node if (targetPluglet.Attributes != null && attributes.ContainsKey(name)) { isAttribute = true; } if (isAttribute == false) { IDocumentFragment newFragment = targetPluglet.ConstructDocumentFragment(null, sourceFragment.Value); ((DocumentFragment)targetDocument.RootFragment).AddDocumentFragment(newFragment); } else { ((DocumentFragment)targetDocument.RootFragment).AddDocumentFragment(transformLink.Target.Name, sourceFragment.Value); } } else { string error = string.Format("Link#-{0}: {1} path not found in target tree", transformLink.Name, transformLink.Target.Name); if (targetDocument.Errors == null) { targetDocument.Errors = new List <string>(); } targetDocument.Errors.Add(error); Logger.Error("DocumentTransformer.Transform", EventId.DocTransformerNoMapping, error); } } } else { Logger.Debug("DocumentTransformer.Transform", "Ignoring Link#-{0}", transformLink.Name); } } //TODO: Handle transformGroup.Formulas } sw.Stop(); Logger.Debug("DocumentTransformer.Transform", "Stop. Elapsed time {0} ms", sw.ElapsedMilliseconds); return(targetDocument); }
/// <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); }