/// <summary> /// The ResolveGroupReference function resolves a group /// reference against the information in the dictionary. /// </summary> /// <param name="reference"> /// The group reference to resolve. /// </param> /// <returns> /// An instance of FixDxResolvedGroup that contains all of /// the resolved information about the group. /// </returns> private FixDxResolvedGroup ResolveGroupReference(FixDxGroupReference reference) { if (string.IsNullOrEmpty(reference.Name)) { string error = "The supplied group reference's name is null or empty."; throw new ArgumentException(error); } else { if (!_mapFieldsByName.ContainsKey(reference.Name)) { string error = string.Format("Repeating group {0}'s field couldn't be resolved.", reference.Name); throw new ArgumentException(error); } else { FixDxField dxField = _mapFieldsByName[reference.Name]; FixDxResolvedGroup result = new FixDxResolvedGroup(dxField.Tag, dxField.Name, reference.Required); foreach (IFixDxElement element in Resolve(reference.Elements)) { result.Elements.Add(element); } return(result); } } }
/// <summary> /// The ParseGroup method attempts to parse all of the group /// instances from a repeating group. /// </summary> /// <param name="ctx"> /// The parser's current parsing context. /// </param> /// <param name="group"> /// The resolved group information for the repeating group /// that is being extracted from the message. /// </param> /// <param name="count"> /// The number of instances of the repeating group that the /// parser should expect to encounter. /// </param> /// <returns></returns> private ParseGroupResult ParseGroup(ParseContext ctx, FixDxResolvedGroup group, int count) { ParseGroupResult result = new ParseGroupResult(); // Build a temporary map of flags that indicate whether // or not a specific element of the repeating group has // been encountered during the parsing of an instance: Dictionary<int, bool> mapEncountered = new Dictionary<int, bool>(); foreach (IFixDxElement element in group.Elements) { if (!mapEncountered.ContainsKey(element.Tag)) { mapEncountered.Add(element.Tag, false); } else { mapEncountered[element.Tag] = false; } } // REC: Extract the FIX tag of the first field in // the repeating group; this is needed to determine // when one instance stops and another starts: IFixDxElement startField = group.Elements.First(); int nStartField = startField.Tag; // REC: Default to the complete status when parsing // a group, so that zero-instance groups don't cause // a problem; a zero-instance group is okay in terms // of parsing, and should only be flagged as a fault // when validation is run... result.Status = VfxFixParserStatus.MsgComplete; for (int i = 0; i != count; i++) { FixCollection collection = new FixCollection(); List<int> listEncounterMapKeys = new List<int>(); foreach (int key in mapEncountered.Keys) { listEncounterMapKeys.Add(key); } // REC: Reset the encounter map for all of the // tags that can be encountered in an instance // of the repeating group: foreach (int key in listEncounterMapKeys) { mapEncountered[key] = false; } while (ctx.Index < ctx.Buffer.Length) { // REC: Ignore leading SOH characters: while (ctx.Buffer[ctx.Index] == '\x01') { ctx.Index++; if (ctx.Index >= ctx.Buffer.Length) { return result; } } // REC: Locate the next tag/value separator: int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); if (idxSep == -1) { // REC: If the next separator couldn't // be found, the message is incomplete: result.Status = VfxFixParserStatus.MsgIncomplete; return result; } // REC: If the field doesn't have a tag then // the parsing of the groups can't continue: if (idxSep == ctx.Index) { result.Status = VfxFixParserStatus.MsgMalformed; return result; } // REC: Attempt to locate the end of the field: int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); if (idxSoh == -1) { result.Status = VfxFixParserStatus.MsgIncomplete; return result; } string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); if (strTag != null) { int nTag; if (int.TryParse(strTag, out nTag) == false) { result.Status = VfxFixParserStatus.MsgMalformed; return result; } string strVal = null; // REC: Determine whether or not the tag // is a valid member of the current group: IFixDxElement element = group.Elements.GetElement(nTag); if (element == null) { // REC: The parsing of a repeating group // should cease if a tag that is not a // member of the group is encountered: result.Instances.Add(collection); // REC: Set the status so that the caller knows // the group was successfully parsed: result.Status = VfxFixParserStatus.MsgComplete; return result; } // REC: Determine whether or not the tag // has already been encountered during // the parsing of the current instance: if (mapEncountered[nTag] == true) { // REC: Determine whether or not the // redundant tag is the first tag in // the repeating group's layout: if (nTag == nStartField) { // REC: This field is the start tag // for another instance of the group: result.Instances.Add(collection); break; } return result; } mapEncountered[nTag] = true; // REC: Determine whether or not the element // represents a length encoded field: if (element is FixDxResolvedField) { FixDxResolvedField fieldEntry = element as FixDxResolvedField; if (fieldEntry.LengthCoded == true) { // REC: Determine whether or not the // corresponding length field exists: string strLen = collection.GetField(fieldEntry.LengthField).Content; if (strLen != null) { int nLen = -1; if (int.TryParse(strLen, out nLen) == true) { // REC: Determine whether or not there // are enough characters remaining in // the buffer to parse the contents: if ((idxSep + nLen) >= ctx.Buffer.Length) { result.Status = VfxFixParserStatus.MsgIncomplete; return result; } strVal = ctx.Buffer.Substring(idxSep + 1, nLen); // REC: Adjust the context's read index: ctx.Index = (idxSep + 1) + nLen; } } else { // REC: The encoded length field couldn't // be located, so the contents will be // parsed as though they were any other // normal field's contents: strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Adjust the context's read index: ctx.Index = idxSoh + 1; } } else { strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Adjust the context's read index: ctx.Index = idxSoh + 1; } collection.AddField(new FixField(nTag, strVal)); } else if (element is FixDxResolvedGroup) { // REC: If the group field's value isn't set // to a specific value, then add a null group // entry to the collection and continue: if (idxSoh == idxSep + 1) { FixGroup parsedGroup = new FixGroup(nTag, null); collection.AddGroup(parsedGroup); result.Instances.Add(collection); ctx.Index = idxSoh + 1; } else { // REC: Attempt to convert the field's value // into an integer that represents the number // of repeating groups that should follow: strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: This might have been the issue with nested repeating // groups becoming a problem: ctx.Index = idxSoh + 1; int nInstances = -1; if (int.TryParse(strVal, out nInstances) == true) { FixDxResolvedGroup subGroup = element as FixDxResolvedGroup; ParseGroupResult subResult = ParseGroup(ctx, subGroup, nInstances); if (subResult.Status != VfxFixParserStatus.MsgComplete) { break; } else { FixGroup parsedGroup = new FixGroup(nTag, strVal); foreach (FixCollection instance in subResult.Instances) { parsedGroup.Instances.Add(instance); } collection.AddGroup(parsedGroup); // REC: Adjust the context's read index: //ctx.Index = idxSoh + 1; } } else { // REC: The instance count couldn't be converted // to an integer, so the message is malformed: result.Status = VfxFixParserStatus.MsgMalformed; break; } } } } } } return result; }
/// <summary> /// The ResolveGroupReference function resolves a group /// reference against the information in the dictionary. /// </summary> /// <param name="reference"> /// The group reference to resolve. /// </param> /// <returns> /// An instance of FixDxResolvedGroup that contains all of /// the resolved information about the group. /// </returns> private FixDxResolvedGroup ResolveGroupReference(FixDxGroupReference reference) { if (string.IsNullOrEmpty(reference.Name)) { string error = "The supplied group reference's name is null or empty."; throw new ArgumentException(error); } else { if (!_mapFieldsByName.ContainsKey(reference.Name)) { string error = string.Format("Repeating group {0}'s field couldn't be resolved.", reference.Name); throw new ArgumentException(error); } else { FixDxField dxField = _mapFieldsByName[reference.Name]; FixDxResolvedGroup result = new FixDxResolvedGroup(dxField.Tag, dxField.Name, reference.Required); foreach (IFixDxElement element in Resolve(reference.Elements)) { result.Elements.Add(element); } return result; } } }
/// <summary> /// The Resolve method attempts to resolve all of the /// element references in a collection to the entries /// that correspond to them. /// </summary> /// <param name="elements"> /// The collection of dictionary elements to resolve. /// </param> /// <returns> /// The resulting collection of resolved elements. /// </returns> /// <exception cref="ArgumentException"> /// Thrown if any elements in the collection that is /// supplied to the method cannot be resolved. /// </exception> public FixDxCollection Resolve(FixDxCollection elements) { FixDxCollection result = new FixDxCollection(); // REC: Iterate over all of the elements in the collection // and determine how to resolve each of them: foreach (IFixDxElement dxElement in elements) { if (dxElement is FixDxFieldReference) { FixDxFieldReference fieldReference = dxElement as FixDxFieldReference; result.Add(ResolveFieldReference(fieldReference)); } else if (dxElement is FixDxGroupReference) { FixDxGroupReference groupReference = dxElement as FixDxGroupReference; result.Add(ResolveGroupReference(groupReference)); } else if (dxElement is FixDxBlockReference) { FixDxBlockReference blockReference = dxElement as FixDxBlockReference; // REC: Determine what type of block the reference // is referring to (component or repeating): if (string.IsNullOrEmpty(blockReference.Name)) { string error = "The supplied block reference's name is null or empty."; throw new ArgumentException(error); } else if (!_mapBlocksByName.ContainsKey(blockReference.Name)) { string error = string.Format("The block reference {0} couldn't be resolved.", blockReference.Name); throw new ArgumentException(error); } else { FixDxBlock dxBlock = _mapBlocksByName[blockReference.Name]; if (dxBlock.Type == FixDxBlockTypes.Component) { foreach (IFixDxElement element in Resolve(Expand(dxBlock.Elements))) { result.Add(element); } } else if (dxBlock.Type == FixDxBlockTypes.Repeating) { // REC: Attempt to resolve the field that the repeating // block references as the start field for the group: if (string.IsNullOrEmpty(dxBlock.Field)) { string error = string.Format("Repeating Block {0}'s start field is null or empty.", dxBlock.Field); throw new ArgumentException(error); } else if (!_mapFieldsByName.ContainsKey(dxBlock.Field)) { string error = string.Format("Repeating block {0}'s start field can't be resolved.", dxBlock.Field); throw new ArgumentException(error); } else { FixDxField dxField = _mapFieldsByName[dxBlock.Field]; FixDxResolvedGroup dxGroup = new FixDxResolvedGroup(dxField.Tag, dxField.Name, false); foreach (IFixDxElement element in Resolve(Expand(dxBlock.Elements))) { dxGroup.Elements.Add(element); } result.Add(dxGroup); } } } } } // REC: Patch from RC - sanity check all elements in the result // to ensure that there are no unresolved references. foreach (IFixDxElement e in result) { if (e is FixDxFieldReference || e is FixDxGroupReference || e is FixDxFieldReference) { throw new Exception("unresolved references exist in the resolved collection"); } } return result; }
/// <summary> /// The Resolve method attempts to resolve all of the /// element references in a collection to the entries /// that correspond to them. /// </summary> /// <param name="elements"> /// The collection of dictionary elements to resolve. /// </param> /// <returns> /// The resulting collection of resolved elements. /// </returns> /// <exception cref="ArgumentException"> /// Thrown if any elements in the collection that is /// supplied to the method cannot be resolved. /// </exception> public FixDxCollection Resolve(FixDxCollection elements) { FixDxCollection result = new FixDxCollection(); // REC: Iterate over all of the elements in the collection // and determine how to resolve each of them: foreach (IFixDxElement dxElement in elements) { if (dxElement is FixDxFieldReference) { FixDxFieldReference fieldReference = dxElement as FixDxFieldReference; result.Add(ResolveFieldReference(fieldReference)); } else if (dxElement is FixDxGroupReference) { FixDxGroupReference groupReference = dxElement as FixDxGroupReference; result.Add(ResolveGroupReference(groupReference)); } else if (dxElement is FixDxBlockReference) { FixDxBlockReference blockReference = dxElement as FixDxBlockReference; // REC: Determine what type of block the reference // is referring to (component or repeating): if (string.IsNullOrEmpty(blockReference.Name)) { string error = "The supplied block reference's name is null or empty."; throw new ArgumentException(error); } else if (!_mapBlocksByName.ContainsKey(blockReference.Name)) { string error = string.Format("The block reference {0} couldn't be resolved.", blockReference.Name); throw new ArgumentException(error); } else { FixDxBlock dxBlock = _mapBlocksByName[blockReference.Name]; if (dxBlock.Type == FixDxBlockTypes.Component) { foreach (IFixDxElement element in Resolve(Expand(dxBlock.Elements))) { result.Add(element); } } else if (dxBlock.Type == FixDxBlockTypes.Repeating) { // REC: Attempt to resolve the field that the repeating // block references as the start field for the group: if (string.IsNullOrEmpty(dxBlock.Field)) { string error = string.Format("Repeating Block {0}'s start field is null or empty.", dxBlock.Field); throw new ArgumentException(error); } else if (!_mapFieldsByName.ContainsKey(dxBlock.Field)) { string error = string.Format("Repeating block {0}'s start field can't be resolved.", dxBlock.Field); throw new ArgumentException(error); } else { FixDxField dxField = _mapFieldsByName[dxBlock.Field]; FixDxResolvedGroup dxGroup = new FixDxResolvedGroup(dxField.Tag, dxField.Name, false); foreach (IFixDxElement element in Resolve(Expand(dxBlock.Elements))) { dxGroup.Elements.Add(element); } result.Add(dxGroup); } } } } } // REC: Patch from RC - sanity check all elements in the result // to ensure that there are no unresolved references. foreach (IFixDxElement e in result) { if (e is FixDxFieldReference || e is FixDxGroupReference || e is FixDxFieldReference) { throw new Exception("unresolved references exist in the resolved collection"); } } return(result); }