public void TestSerializationOfMessageErrorContext() { var segContext1 = new SegmentErrorContext("Name", 66, ErrorCodes.ComponentDataElementsTooMany); segContext1.Add(ErrorCodes.DataElementsTooMany); segContext1.Add("Name1", 66, ErrorCodes.ComponentDataElementsTooMany, 32, 96, "876"); segContext1.Add("Name2", 88, ErrorCodes.ControlNumberNotMatching, 16, 48, "438"); var segContext2 = new SegmentErrorContext("Name2", 32, ErrorCodes.ComponentDataElementsTooMany); segContext2.Add(ErrorCodes.DataElementsTooMany); segContext2.Add("Name1", 66, ErrorCodes.ComponentDataElementsTooMany, 16, 48, "476"); segContext2.Add("Name2", 88, ErrorCodes.ControlNumberNotMatching, 5, 5, "200"); var msgContext = new MessageErrorContext("MsgError", "111111"); msgContext.Add(ErrorCodes.DataElementsTooMany); msgContext.Add(segContext1); msgContext.Add(segContext2); BinaryFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, msgContext); stream.Seek(0, SeekOrigin.Begin); var copy = (MessageErrorContext)formatter.Deserialize(stream); AssertTwoMessageErrorsAreEqual(msgContext, copy); }
/// <summary> /// Validates EDI document instance. /// </summary> /// <param name="message">The EDI document instance.</param> /// <returns>A collection of validation errors.</returns> /// <exception cref="Exception">Throws an exception if the instance is not a valid ediFabric spec.</exception> public ValidationException Validate(object message) { if (message == null) { throw new ArgumentNullException("message"); } string messageName = null; string controlNumber = null; try { var xDoc = Serialize(message); if (xDoc.Root == null) { throw new Exception("Failed to serialize instance."); } string format; if (!TryGetMessageContext(xDoc.Root, out messageName, out controlNumber, out format)) { throw new Exception("Failed to extract message name or control number."); } if (XsdCache.Count > XsdCacheMax) { XsdCache.Clear(); } var schemas = XsdCache.GetOrAdd(message.GetType().FullName, key => NewSchemaSet(_xsdStream(new MessageContext(message)), xDoc.Root.Name.Namespace.NamespaceName)); var messageContext = new MessageErrorContext(messageName, controlNumber); xDoc.Validate(schemas, (o, e) => { var errorCode = MapErrorCode(GetErrorCode(e)); var segmentContext = BuildContext(o as XElement, schemas, errorCode); messageContext.Add(segmentContext); }); foreach (var code in ValidateStructure(xDoc.Root, controlNumber, format)) { messageContext.Add(code); } return(new ValidationException("Validation successful.", messageContext)); } catch (Exception ex) { if (!string.IsNullOrEmpty(messageName) && !string.IsNullOrEmpty(controlNumber)) { var messageContext = new MessageErrorContext(messageName, controlNumber); messageContext.Add(ErrorCodes.Unknown); return(new ValidationException("Validation failed.", messageContext, ex)); } return(new ValidationException("Validation failed.", null, ex)); } }
/// <summary> /// Initializes a new instance of the <see cref="MessageEventArgs{T, U}"/> class. /// </summary> /// <param name="message">The EDI message.</param> /// <param name="errorContext">The message error context.</param> /// <param name="groupHeader">The group header.</param> /// <param name="interchangeHeader">The interchange header.</param> /// <param name="inDuplicateGroup">Detects if the message is part of a duplicate group.</param> /// <param name="inDuplicateInterchange">Detects if the message is part of a duplicate interchange.</param> protected MessageEventArgs(T interchangeHeader, U groupHeader, EdiMessage message, MessageErrorContext errorContext, bool inDuplicateGroup, bool inDuplicateInterchange) { InterchangeHeader = interchangeHeader; GroupHeader = groupHeader; Message = message; ErrorContext = errorContext; InDuplicateGroup = inDuplicateGroup; InDuplicateInterchange = inDuplicateInterchange; }
public void TestSerializationOfParsingException() { var segContext1 = new SegmentErrorContext("Name", 66, ErrorCodes.ComponentDataElementsTooMany); segContext1.Add(ErrorCodes.DataElementsTooMany); segContext1.Add("Name1", 66, ErrorCodes.ComponentDataElementsTooMany, 32, 96, "876"); segContext1.Add("Name2", 88, ErrorCodes.ControlNumberNotMatching, 16, 48, "438"); var segContext2 = new SegmentErrorContext("Name2", 32, ErrorCodes.ComponentDataElementsTooMany); segContext2.Add(ErrorCodes.DataElementsTooMany); segContext2.Add("Name1", 66, ErrorCodes.ComponentDataElementsTooMany, 16, 48, "476"); segContext2.Add("Name2", 88, ErrorCodes.ControlNumberNotMatching, 5, 5, "200"); var msgContext = new MessageErrorContext("MsgError", "111111"); msgContext.Add(ErrorCodes.DataElementsTooMany); msgContext.Add(segContext1); msgContext.Add(segContext2); ParsingException ex; try { throw new ParsingException(ErrorCodes.SegmentWithErrors, "message", "HL*1*0*20*1~", msgContext); } catch (ParsingException thrown) { ex = thrown; } BinaryFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, ex); stream.Seek(0, SeekOrigin.Begin); var copy = (ParsingException)formatter.Deserialize(stream); //var copy = new ParsingException(ErrorCodes.ControlNumberNotMatching, "message2"); AssertTwoParsingExceptionsAreEqual(ex, copy); }
void AssertTwoMessageErrorsAreEqual(MessageErrorContext context, MessageErrorContext other) { Assert.AreEqual(context.Name, other.Name, "Name"); Assert.AreEqual(context.ControlNumber, other.ControlNumber, "Position"); Assert.AreEqual(context.Codes.Count, other.Codes.Count); var contextCodesArray = other.Codes.ToArray(); var copyCodesArray = context.Codes.ToArray(); for (int i = 0; i < context.Codes.Count; i++) { Assert.AreEqual(copyCodesArray[i], contextCodesArray[i]); } Assert.AreEqual(context.Errors.Count, other.Errors.Count); var copyErrorsArray = context.Errors.ToArray(); var contextErrorsArray = other.Errors.ToArray(); for (int i = 0; i < context.Errors.Count; i++) { AssertTwoSegmentErrorsAreEqual(contextErrorsArray[i], copyErrorsArray[i]); } }
/// <summary> /// Validates a message according to its validation attributes. /// </summary> /// <param name="result">The resulting message context.</param> /// <param name="skipTrailer">Whether to validate the trailer. Skip when validating custom created messages.</param> /// <returns>Whether the message is valid or not. If not valid then the message error context will contain the reasons.</returns> public bool IsValid(out MessageErrorContext result, bool skipTrailer = false) { result = new MessageErrorContext(Name, ControlNumber, Version, MessagePart, null); int segmentsNum; result.AddRange(this.Validate(out segmentsNum)); if (!skipTrailer) { foreach (var errorCode in ValidateStructure(segmentsNum)) { result.Add(errorCode); } } if (HasErrors) { result.AddRange(ErrorContext.Errors); result.AddRange(ErrorContext.Codes); } return(!result.HasErrors); }
public MessageErrorContext Analyze(IEnumerable <SegmentContext> segments, MessageContext messageContext, Separators separators, int partsIndex, int segmentIndex) { var errorContext = new MessageErrorContext(messageContext.Name, messageContext.ControlNumber, messageContext.Version, partsIndex, "Message was parsed with errors."); var currSeg = Children.First() as Segment; var index = segmentIndex; foreach (var segment in segments) { Segment tempSeg; index++; if (segment.IsJump) { tempSeg = this.Descendants <Segment>() .LastOrDefault( d => d.EdiName == "HL" && d.Children.Count > 1 && d.Children.ElementAt(1).Value == segment.SecondValue); if (tempSeg != null) { currSeg = tempSeg; } } tempSeg = currSeg.TraverseDepthFirst().FirstOrDefault(n => n.Match(segment)); if (tempSeg == null) { var errorCode = SegmentErrorCode.SegmentNotInProperSequence; TypeInfo type = null; var notFoundSegment = this.Descendants <Segment>().FirstOrDefault(d => d.EdiName == segment.Name); if (notFoundSegment == null) { errorCode = SegmentErrorCode.UnrecognizedSegment; } else { type = notFoundSegment.TypeInfo; } errorContext.Add(new SegmentErrorContext(segment.Name, index, type, segment.Value, errorCode)); if (messageContext.PartialAllowed) { // ReSharper disable once RedundantAssignment tempSeg = currSeg; continue; } return(errorContext); } currSeg = tempSeg; if (currSeg.IsParsed) { currSeg = (Segment)currSeg.InsertRepetition(); } try { if (separators == null) { currSeg.Parse(segment.Value, messageContext.PartialAllowed); } else { currSeg.Parse(segment.Value, separators, messageContext.PartialAllowed); } } catch (ParserSegmentException ex) { var segmentContext = new SegmentErrorContext(segment.Name, index, currSeg.TypeInfo, segment.Value); segmentContext.Add(ex.ErrorContext); errorContext.Add(segmentContext); if (messageContext.PartialAllowed) { continue; } return(errorContext); } } return(errorContext.HasErrors ? errorContext : null); }
private void ReadAttributes(Func <MessageContext, Assembly> rulesAssembly) { if (rulesAssembly == null) { throw new ArgumentNullException("rulesAssembly"); } if (MessageType != null) { return; } Assembly assembly; try { assembly = rulesAssembly(this); } catch (Exception ex) { var errorContext = new MessageErrorContext(Name, ControlNumber, Version, 0, ex.Message, MessageErrorCode.TransactionSetNotSupported); throw new ParserMessageException(errorContext); } var matches = assembly.GetTypes().Where(m => { var att = ((MessageAttribute)m.GetTypeInfo().GetCustomAttribute(typeof(MessageAttribute))); if (att == null) { return(false); } if (att.Format == Format && att.Version == Version && att.Id == Name) { PartialAllowed = att.IsEvaluation; SplitterRegex = att.SplitterRegex; return(true); } return(false); }).ToList(); var attribute = "[Message(" + Format + ", " + Version + ", " + Name + ")]"; if (!matches.Any()) { var msg = String.Format("Type with attribute'{0}' was not found in assembly '{1}'.", attribute, assembly.FullName); var errorContext = new MessageErrorContext(Name, ControlNumber, Version, 0, msg, MessageErrorCode.TransactionSetNotSupported); throw new ParserMessageException(errorContext); } if (matches.Count > 1) { var msg = String.Format("Multiple types with attribute'{0}' were found in assembly '{1}'.", attribute, assembly.FullName); var errorContext = new MessageErrorContext(Name, ControlNumber, Version, 0, msg, MessageErrorCode.TransactionSetNotSupported); throw new ParserMessageException(errorContext); } MessageType = matches.First().GetTypeInfo(); }
internal static void ParseSegment(this ParseNode parseNode, string line, Separators separators, MessageErrorContext errorContext = null) { if (parseNode == null) { throw new ArgumentNullException("parseNode"); } if (String.IsNullOrEmpty(line)) { throw new ArgumentNullException("line"); } if (separators == null) { throw new ArgumentNullException("separators"); } if (parseNode.Prefix != Prefixes.S) { throw new Exception(String.Format("Only segments are supported: {0}", parseNode.Name)); } var dataElementsGrammar = ParseNode.BuldTree(parseNode.Type, false).Children; var dataElements = line.GetDataElements(separators); for (var deIndex = 0; deIndex < dataElements.Length; deIndex++) { var currentDataElement = dataElements[deIndex]; if (String.IsNullOrEmpty(currentDataElement)) { continue; } if (dataElementsGrammar.Count <= deIndex) { if (errorContext != null) { errorContext.Add(parseNode.EdiName, parseNode.IndexInParent() + 1, ErrorCodes.DataElementsTooMany); } throw new ParsingException(ErrorCodes.InvalidInterchangeContent, "Too many data elements in segment.", line, errorContext); } var currentDataElementGrammar = dataElementsGrammar.ElementAt(deIndex); var repetitions = currentDataElementGrammar.IsX12RepetitionSeparator() ? new[] { currentDataElement } : currentDataElement.GetRepetitions(separators); foreach (var repetition in repetitions) { if (String.IsNullOrEmpty(repetition)) { continue; } var childParseNode = parseNode.AddChild(currentDataElementGrammar.Type, currentDataElementGrammar.Name, currentDataElementGrammar.Prefix == Prefixes.D ? repetition.UnEscapeLine(separators) : null); if (currentDataElementGrammar.Prefix != Prefixes.C) { continue; } var componentDataElementsGrammar = currentDataElementGrammar.Children; var componentDataElements = repetition.GetComponentDataElements(separators); for (var cdeIndex = 0; cdeIndex < componentDataElements.Length; cdeIndex++) { var currentComponentDataElement = componentDataElements[cdeIndex]; if (String.IsNullOrEmpty(currentComponentDataElement)) { continue; } var currentComponentDataElementGrammar = componentDataElementsGrammar.ElementAt(cdeIndex); if (componentDataElementsGrammar.Count <= cdeIndex) { if (errorContext != null) { errorContext.Add(parseNode.EdiName, parseNode.IndexInParent() + 1, currentComponentDataElementGrammar.Name, currentDataElementGrammar.IndexInParent() + 1, ErrorCodes.ComponentDataElementsTooMany, currentComponentDataElementGrammar.IndexInParent() + 1, repetitions.ToList().IndexOf(repetition) + 1, currentComponentDataElement); } throw new ParsingException(ErrorCodes.InvalidInterchangeContent, "Too many component data elements.", line, errorContext); } childParseNode.AddChild(currentComponentDataElementGrammar.Type, currentComponentDataElementGrammar.Name, currentComponentDataElement.UnEscapeLine(separators)); } } } }
internal static object Analyze(this List <SegmentContext> segments, Separators separators, MessageContext messageContext) { if (segments == null) { throw new ArgumentNullException("segments"); } if (separators == null) { throw new ArgumentNullException("separators"); } if (messageContext == null) { throw new ArgumentNullException("messageContext"); } var messageGrammar = ParseNode.BuldTree(messageContext.SystemType, true); if (messageGrammar.Prefix != Prefixes.M) { throw new Exception(String.Format("Only messages are supported: {0}", messageGrammar.Name)); } var errorContext = new MessageErrorContext(messageContext.Tag, messageContext.ControlNumber); var segmentPosition = messageGrammar.Children.First(); var instancePosition = new ParseNode(messageContext.SystemType); foreach (var segment in segments) { if (segment.IsControl) { continue; } Logger.Log(String.Format("Segment to match: {0}", segment.LogName)); // Jump back to HL segment if needed if (segment.IsJump) { try { segmentPosition = messageGrammar.JumpToHl(instancePosition.Root(), segment.ParentId); } catch (Exception ex) { errorContext.Add(segment.Name, segments.IndexOf(segment) + 1, ErrorCodes.UnexpectedSegment); throw new ParsingException(ErrorCodes.InvalidInterchangeContent, "Unable to resolve HL.", ex, segment.Value, errorContext); } } var currSeg = segmentPosition.TraverseSegmentsDepthFirst().FirstOrDefault(n => n.IsEqual(segment)); if (currSeg == null) { if (messageGrammar.Descendants().All(d => d.EdiName != segment.Name)) { errorContext.Add(segment.Name, segments.IndexOf(segment) + 1, ErrorCodes.UnrecognizedSegment); throw new ParsingException(ErrorCodes.InvalidInterchangeContent, "Segment is not supported in rules class.", segment.Value, errorContext); } errorContext.Add(segment.Name, segments.IndexOf(segment) + 1, ErrorCodes.UnexpectedSegment); throw new ParsingException(ErrorCodes.InvalidInterchangeContent, "Segment was not in the correct position according to the rules class.", segment.Value, errorContext); } var segmentTree = currSeg.AncestorsToIntersection(segmentPosition); instancePosition = instancePosition.AncestorsAndSelf().Last(nt => nt.Name == segmentTree.First().Parent.Name); foreach (var parseTree in segmentTree) { instancePosition = instancePosition.AddChild(parseTree.Type, parseTree.Type.Name); if (parseTree.Prefix == Prefixes.S) { instancePosition.ParseSegment(segment.Value, separators, errorContext); } } segmentPosition = currSeg; Logger.Log(String.Format("Matched segment: {0}", segmentPosition.Name)); } return(instancePosition.Root().ToInstance()); }
public ParserMessageException(MessageErrorContext messageErrorContext) : base(messageErrorContext.Message) { MessageErrorContext = messageErrorContext; }