FixAntenna/NetCore/Validation/Utils/FixUtil.cs (1,458 lines of code) (raw):
// Copyright (c) 2021 EPAM Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Epam.FixAntenna.NetCore.Common;
using Epam.FixAntenna.NetCore.Common.Xml;
using Epam.FixAntenna.NetCore.Configuration;
using Epam.FixAntenna.NetCore.Dictionary;
using Epam.FixAntenna.NetCore.Helpers;
using Epam.FixAntenna.NetCore.Message;
using Epam.FixAntenna.NetCore.Validation.Entities;
using Epam.FixAntenna.NetCore.Validation.Error;
using Epam.FixAntenna.NetCore.Validation.Exceptions;
using Epam.FixAntenna.NetCore.Validation.Exceptions.Mapping;
using Epam.FixAntenna.NetCore.Validation.Utils.Cache;
using Epam.FixAntenna.NetCore.Validation.Utils.Containers;
using Epam.FixAntenna.NetCore.Validation.Utils.Definitions;
using Epam.FixAntenna.NetCore.Validation.Validators.Condition;
using Epam.FixAntenna.NetCore.Validation.Validators.Condition.Container;
namespace Epam.FixAntenna.NetCore.Validation.Utils
{
/// <summary>
/// Utility to work with FIX messages, such as validate,
/// returns custom data of message and etc.
/// </summary>
internal sealed class FixUtil
{
private static readonly IDictionary<int, Field> EmptyFieldMap = new Dictionary<int, Field>();
private static FixErrorBuilder _fixErrorBuilder = FixErrorBuilder.CreateBuilder();
private readonly BlockDefinitionsUtils _blockDefinitionsUtils;
private readonly Dictionary<string, IConditionalMessage> _conditionsCache =
new Dictionary<string, IConditionalMessage>();
private readonly DictionaryTypes _dictionaryTypes;
private readonly IDictionary<string, IList<int>> _fieldCache = new Dictionary<string, IList<int>>();
private readonly IDictionary<string, IList<Fielddef>> _fieldDefMessageTypeCache =
new Dictionary<string, IList<Fielddef>>();
private readonly IDictionary<string, Fielddef> _fieldDefNameCache = new Dictionary<string, Fielddef>();
private readonly IDictionary<int, Fielddef> _fieldDefTagIdCache = new Dictionary<int, Fielddef>();
private readonly IDictionary<string, ISet<int>> _fieldRequiredCache = new Dictionary<string, ISet<int>>();
private readonly MessagesCache _groupsCacheWithInternalGroups = new MessagesCache();
private readonly MessageDefinitionsUtils _msgDefinitionsUtils;
private readonly Blockdef _smhDef;
private readonly ISet<int> _smhTags = new HashSet<int>();
private readonly Blockdef _smtDef;
private readonly ISet<int> _smtTags = new HashSet<int>();
private readonly FixVersionContainer _versionContainer;
private int[] _allTags;
private Fielddef[] _fieldsdef;
private IList<Valblockdef> _valblockdefs;
/// <summary>
/// Constructor FixUtils creates a new FixUtils instance.
/// </summary>
/// <param name="version"> the Version of FIX protocol </param>
/// <exception cref="ArgumentException"> </exception>
public FixUtil(FixVersionContainer version) : this(version, null)
{
}
/// <summary>
/// Constructor FixUtils creates a new FixUtils instance.
/// </summary>
/// <param name="version"> the Version of FIX protocol </param>
/// <param name="appFixVersion"> the App version of FIX protocol </param>
/// <exception cref="ArgumentException"> </exception>
public FixUtil(FixVersionContainer version, FixVersionContainer appFixVersion)
{
if (version == null)
{
throw new ArgumentException();
}
if (appFixVersion != null)
{
if (version.FixVersion == FixVersion.Fixt11)
{
_versionContainer = appFixVersion;
}
else
{
_versionContainer = version;
}
}
else
{
_versionContainer = version;
}
_dictionaryTypes = FixDictionaryFactory.Instance.GetDictionaries(version, appFixVersion);
_msgDefinitionsUtils = new MessageDefinitionsUtils(_dictionaryTypes);
_blockDefinitionsUtils = new BlockDefinitionsUtils(_dictionaryTypes);
_smhDef = _blockDefinitionsUtils.Get(Constants.Smh);
_smhTags.AddRange(GetFieldsTags(_smhDef.FieldOrDescrOrGroup));
_smtDef = _blockDefinitionsUtils.Get(Constants.Smt);
_smtTags.AddRange(GetFieldsTags(_smtDef.FieldOrDescrOrGroup));
PrepareFieldsDefinitions(_dictionaryTypes);
// cached fields after initialization of all utils
PutFields();
PutRequiredField();
// cached condition after initialization of all utils
PutConditions();
// cached fieldDefs for messageTypes
PrepareFieldDefCache();
// cached fieldDefs for messageTypes
PrepareValblockdefs();
// cached groups data
PrepareGroupsCache();
}
/// <summary>
/// Gets val blocks.
/// </summary>
public IList<Valblockdef> GetValblockdefs()
{
return _valblockdefs;
}
/// <summary>
/// Gets tags.
/// </summary>
public int[] GetAllTags()
{
return _allTags;
}
public bool IsHeader(int tag)
{
return _smhTags.Contains(tag);
}
public bool IsTrailer(int tag)
{
return _smtTags.Contains(tag);
}
/// <summary>
/// Gets fields.
/// </summary>
public Fielddef[] GetFieldDef()
{
return _fieldsdef;
}
/// <summary>
/// Gets conditional cache.
/// </summary>
public Dictionary<string, IConditionalMessage> GetConditionalCache()
{
return _conditionsCache;
}
/// <summary>
/// Returns map of condition operators from input list
/// </summary>
/// <param name="list"> the input list. </param>
/// <param name="rootTag"> the root tag of group. </param>
/// <param name="conditionalType"> the type of conditional FIX message. </param>
/// <returns> Map of condition operators </returns>
private IConditionalMessage GetConditions<T1>(IList<T1> list, int rootTag, ConditionalType conditionalType)
{
var listLength = list.Count;
var conditions = new Dictionary<int, ICondition>();
var conditionalMessage = BuildConditionalMessage(conditionalType, rootTag, conditions);
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
var obj = list[objectCount];
switch (obj)
{
case Field field:
{
var condition = field.Condreq;
BuildCondition(conditions, field, condition);
break;
}
case Group group:
{
var groupRootTag = @group.Nofield;
var condGroup = GetConditions(@group.Content, groupRootTag, ConditionalType.Group);
condGroup.SetRequired(@group.Req != null && "Y".Equals(@group.Req));
if (conditions.TryGetValue(groupRootTag, out var condition))
{
// added into group conditional list.
condGroup.GetConditionMap()[groupRootTag] = condition;
// remove from the root message conditional list.
conditions.Remove(groupRootTag);
}
conditionalMessage.AddConditionalGroup((ConditionalGroup)condGroup);
break;
}
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
var conditionalBlock = GetConditions(listFromBlock, block.Tag,
ConditionalType.Block);
if (block.Condreq != null)
{
var conditionValidateParser = new ConditionValidateParser(block.Condreq);
((ConditionalBlock)conditionalBlock).SetCondition(conditionValidateParser.GetCondition());
}
conditionalBlock.SetRequired(block.Req != null && "Y".Equals(block.Req));
conditionalMessage.AddConditionalBlock((ConditionalBlock)conditionalBlock);
break;
}
}
}
return conditionalMessage;
}
//TODO: need to create implementation of LinkedHashMap in .net
private void BuildCondition(Dictionary<int, ICondition> conditions, Field field, string condition)
{
if (condition != null)
{
var conditionValidateParser = new ConditionValidateParser(condition);
conditions.Add(field.Tag, conditionValidateParser.GetCondition());
}
}
private IConditionalMessage BuildConditionalMessage(ConditionalType conditionalType, int rootTag,
IDictionary<int, ICondition> conditions)
{
IConditionalMessage conditionalMessage;
switch (conditionalType)
{
case ConditionalType.Message:
conditionalMessage = new ConditionalMessage(rootTag, conditions);
break;
case ConditionalType.Group:
conditionalMessage = new ConditionalGroup(rootTag, conditions);
break;
case ConditionalType.Block:
conditionalMessage = new ConditionalBlock(rootTag, conditions);
break;
default:
conditionalMessage = new ConditionalMessage(rootTag, conditions);
break;
}
return conditionalMessage;
}
/// <summary>
/// Puts fields into cache
/// </summary>
private void PutFields()
{
var msgdefCollection = _msgDefinitionsUtils.Get();
foreach (var msgdef in msgdefCollection)
{
IList<object> list = msgdef.FieldOrDescrOrAlias;
_fieldCache[msgdef.Msgtype] = GetFieldsTags(list);
}
// puts header fields
var blockdef = _blockDefinitionsUtils.Get(Constants.Smh);
_fieldCache[Constants.Smh] = GetFieldsTags(blockdef.FieldOrDescrOrGroup);
// puts trailer fields
blockdef = _blockDefinitionsUtils.Get(Constants.Smt);
_fieldCache[Constants.Smt] = GetFieldsTags(blockdef.FieldOrDescrOrGroup);
}
private void PutRequiredField()
{
foreach (var type in _msgDefinitionsUtils.GetMessageTypes())
{
_fieldRequiredCache[type] = BuildRequiredTagsForMessage(type);
}
}
/// <summary>
/// Gets tags for message.
/// </summary>
/// <param name="messageType"> the message type of message </param>
public IList<int> GetTagsByMsgType(string messageType)
{
return _fieldCache[messageType];
}
/// <summary>
/// Gets fields for message.
/// </summary>
/// <param name="messageType"> the message type of message </param>
public IList<Fielddef> GetFieldsByMessageType(string messageType)
{
var fielddefs = new List<Fielddef>(_fieldDefMessageTypeCache[Constants.Smh]);
fielddefs.AddRange(_fieldDefMessageTypeCache[messageType]);
fielddefs.AddRange(_fieldDefMessageTypeCache[Constants.Smt]);
return fielddefs;
}
/// <summary>
/// Gets fix version.
/// </summary>
public FixVersion GetVersion()
{
return _versionContainer.FixVersion;
}
public FixVersionContainer GetVersionContainer()
{
return _versionContainer;
}
/// <summary>
/// Gets field.
/// </summary>
/// <param name="tag"> the tag </param>
public Fielddef GetFieldDefByTag(int tag)
{
_fieldDefTagIdCache.TryGetValue(tag, out var value);
return value;
}
/// <summary>
/// Gets field.
/// </summary>
/// <param name="name"> the field name </param>
public Fielddef GetFieldDefByName(string name)
{
var nameBasedKey = name.ToLower();
if (_fieldDefNameCache.ContainsKey(nameBasedKey))
{
return _fieldDefNameCache[name.ToLower()];
}
var excText = new StringBuilder();
excText.Append("Unknown field name ");
excText.Append("[");
excText.Append(name);
excText.Append("]");
excText.Append(" in FIX version ");
excText.Append("[");
excText.Append(_versionContainer.FixVersion);
excText.Append("]");
throw new DictionaryRuntimeException(excText.ToString());
}
/// <summary>
/// Gets field tag.
/// </summary>
/// <param name="name"> the field name </param>
public int GetFieldTagByName(string name)
{
return GetFieldDefByName(name).Tag;
}
/// <summary>
/// Gets field type by field name.
/// </summary>
/// <param name="name"> the field name </param>
public string GetFieldTypeByFieldName(string name)
{
return GetFieldDefByName(name).Type;
}
/// <summary>
/// Gets field type by field tag.
/// </summary>
/// <param name="tag"> the filed tag. </param>
public string GetFieldTypeByFieldTag(int tag)
{
return GetFieldDefByTag(tag).Type;
}
/// <summary>
/// Find group by start tag.
/// </summary>
/// <param name="msgType"> the message type. </param>
/// <param name="startTagId"> the start tag if repeating group. </param>
public Group FindGroup(string msgType, int startTagId)
{
var msgContent = GetMessageDefUtils().Get(msgType).FieldOrDescrOrAlias;
if (msgContent == null)
{
var excText = new StringBuilder();
excText.Append("Unknown message type ").Append("[").Append(msgType).Append("]")
.Append(" in FIX version ").Append("[").Append(_versionContainer.FixVersion).Append("]");
throw new DictionaryRuntimeException(excText.ToString());
}
return SearchGroupInContent(msgContent, startTagId);
}
private Group SearchGroupInContent(IList<object> content, int startTagId)
{
foreach (var o in content)
{
switch (o)
{
case Group grp when grp.Startfield == startTagId:
return grp;
case Group grp:
{
grp = SearchGroupInContent(grp.Content, startTagId);
if (grp != null)
{
return grp;
}
break;
}
case Block block:
{
var blockIdent = block.Idref;
var blockContent = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
var grp = SearchGroupInContent(blockContent, startTagId);
if (grp != null)
{
return grp;
}
break;
}
}
}
return null;
}
/// <summary>
/// Get repeating group content.
/// </summary>
/// <param name="msgType"> the message type. </param>
/// <param name="startTagId"> the start tag if repeating group. </param>
public IList<object> GetGroupContent(string msgType, int startTagId)
{
var group = FindGroup(msgType, startTagId);
if (group != null)
{
return @group.Content;
}
var excText = new StringBuilder();
excText.Append("Unknown repeating group with start tag ");
excText.Append("[");
excText.Append(startTagId);
excText.Append("]");
excText.Append(" in message of type ");
excText.Append("[");
excText.Append(msgType);
excText.Append("]");
excText.Append(" in FIX version ");
excText.Append("[");
excText.Append(_versionContainer.FixVersion);
excText.Append("]");
throw new DictionaryRuntimeException(excText.ToString());
}
/// <summary>
/// Get all field defs in repeating group.
/// </summary>
/// <param name="msgType"> the message type. </param>
/// <param name="startTagId"> the start tag if repeating group. </param>
public IList<Fielddef> GetGroupFieldDefs(string msgType, int startTagId)
{
return GetFieldDefs(GetGroupContent(msgType, startTagId), true);
}
/// <summary>
/// Get message type field def hierarchy.
/// </summary>
/// <param name="msgType"> the message type. </param>
public IList<object> GetMessageFieldDefHier(string msgType)
{
var smhContent = GetSmhContentHier();
var bodyContent = GetFieldDefsHier(_msgDefinitionsUtils.Get(msgType).FieldOrDescrOrAlias);
var smtContent = GetSmtContentHier();
var fielddefs = new List<object>(smhContent.Count + bodyContent.Count + smtContent.Count);
fielddefs.AddRange(smhContent);
fielddefs.AddRange(bodyContent);
fielddefs.AddRange(smtContent);
return fielddefs;
}
private IList<object> GetSmhContentHier()
{
var blockdef = _blockDefinitionsUtils.Get(Constants.Smh);
return GetFieldDefsHier(blockdef.FieldOrDescrOrGroup);
}
private IList<object> GetSmtContentHier()
{
var blockdef = _blockDefinitionsUtils.Get(Constants.Smt);
return GetFieldDefsHier(blockdef.FieldOrDescrOrGroup);
}
/// <summary>
/// Method isTagDefinedForMessage, verifies whether the tag is present in a
/// message by type of message.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="tagNum"> the num of tag </param>
/// <returns> true if presents, otherwise false </returns>
public bool IsTagDefinedForMessage(string msgType, int tagNum)
{
return IsContainsTagInFieldsList(msgType, tagNum) || IsContainsTagInFieldsList(Constants.Smh, tagNum) ||
IsContainsTagInFieldsList(Constants.Smt, tagNum) ||
msgType.StartsWith("U", StringComparison.Ordinal);
}
/// <summary>
/// Method getRequiredTagsForMessage returns array of required tags, or empty
/// array if message does not have required tags.
/// </summary>
/// <param name="msgType"> the type of message </param>
/// <returns> int[] Array of required tags </returns>
/// <exception cref="MessageDefinitionsException">if message does not exist </exception>
public ISet<int> GetRequiredTagsForMessage(string msgType)
{
var reqTags = _fieldRequiredCache[msgType];
if (reqTags == null)
{
throw new MessageDefinitionsException(
new FixError(FixErrorCode.InvalidMsgtype, "Message type '" + msgType + "' does not exist", null),
new Exception().InnerException);
}
return reqTags;
}
private ISet<int> BuildRequiredTagsForMessage(string msgType)
{
ISet<int> requiredTags = new HashSet<int>();
requiredTags.AddRange(_msgDefinitionsUtils.GetRequiredTags(msgType, _blockDefinitionsUtils));
requiredTags.AddRange(_blockDefinitionsUtils.GetRequiredTags(Constants.Smh));
requiredTags.AddRange(_blockDefinitionsUtils.GetRequiredTags(Constants.Smt));
return requiredTags;
}
/// <summary>
/// Verifies whether the tag is defines for message by type of message
/// </summary>
/// <param name="shortName"> the type of message </param>
/// <param name="tagNum"> the tag </param>
/// <returns> <c>true</c> if defines, otherwise <c>false</c> </returns>
public bool IsTagDefinedForMessageOrBlock(string shortName, int tagNum)
{
var msgDefinitions = GetDefinition(shortName);
if (msgDefinitions == null)
{
return false;
}
switch (msgDefinitions)
{
case Msgdef msgdef:
return IsContainsTagInFieldsList(msgdef.FieldOrDescrOrAlias, tagNum);
case Blockdef blockdef:
return IsContainsTagInFieldsList(blockdef.FieldOrDescrOrGroup, tagNum);
default:
return false;
}
}
/// <summary>
/// Checks if fields list of message Contains tags from block
/// </summary>
/// <param name="list"> the list of field of block </param>
/// <param name="fixMessage"> the field list of message </param>
/// <returns> true if Contains in otherwise false </returns>
public bool HasRequiredTagInMessage<T1>(IList<T1> list, Message.FixMessage fixMessage)
{
return HasRequiredTagInMessage(list, fixMessage, false);
}
public bool HasRequiredTagInMessage<T1>(IList<T1> list, Message.FixMessage fixMessage, bool withInternalGroup)
{
var listLength = list.Count;
for (var objectIndex = 0; objectIndex < listLength; objectIndex++)
{
var obj = list[objectIndex];
switch (obj)
{
case Field field:
{
if (fixMessage.IsTagExists(field.Tag))
{
return true;
}
break;
}
case Block block:
{
var blockIdent = block.Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (HasRequiredTagInMessage(listFromBlock, fixMessage, withInternalGroup))
{
return true;
}
break;
}
default:
{
if (withInternalGroup && obj is Group grp)
{
int? nofield = grp.Nofield;
IList<object> groupContent = grp.Content;
if (HasRequiredTagInMessage(groupContent, fixMessage, withInternalGroup))
{
return true;
}
}
break;
}
}
}
return false;
}
/// <summary>
/// Gets group tags, the returned result includes the inner groups.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="groupTag"> the group tag </param>
/// <param name="fixMessage"> the list of fields </param>
public IDictionary<int, Field> GetGroupTagsWithInternalGroups(string msgType, int groupTag,
Message.FixMessage fixMessage)
{
return GetGroupsTags(msgType, groupTag, fixMessage, true);
}
/// <summary>
/// Gets group tags, the returned result does not include the inner groups.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="tag"> the tag </param>
/// <param name="fixMessage"> the list of fields </param>
public IDictionary<int, Field> GetGroupTagsWithOutInternalGroups(string msgType, int tag,
Message.FixMessage fixMessage)
{
return GetGroupsTags(msgType, tag, fixMessage, false);
}
/// <summary>
/// Gets group tags.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="groupTag"> the group tag </param>
/// <param name="fixMessage"> the list of fields </param>
/// <param name="withInternalGroup"> if flag is true the inner groups will be included </param>
public IDictionary<int, Field> GetGroupsTags(string msgType, int groupTag, Message.FixMessage fixMessage,
bool withInternalGroup)
{
if (groupTag == -1)
{
return EmptyFieldMap;
}
var msgdef = _msgDefinitionsUtils.Get(msgType);
if (msgdef == null)
{
return EmptyFieldMap;
}
var objectList = msgdef.FieldOrDescrOrAlias;
return GetGroupFields(groupTag, objectList, fixMessage, withInternalGroup);
}
private bool CheckNeededBlock<T1>(Message.FixMessage fixMessage, Block block, IList<T1> listFromBlock)
{
return HasRequiredTagInMessage(listFromBlock, fixMessage) ||
IsConditionalRequired(block.Condreq, fixMessage);
}
/// <summary>
/// Checks if tag exist.
/// </summary>
/// <param name="tag"> the tag </param>
public bool IsKnownTag(int tag)
{
foreach (var allTag in _allTags)
{
if (allTag == tag)
{
return true;
}
}
return false;
}
/// <summary>
/// Checks if group <c>groupTag</c> defined for message type <c>msgType</c>
/// </summary>
/// <param name="groupTag"> the group tag </param>
/// <param name="msgType"> the message type </param>
public bool IsGroupTag(string msgType, int groupTag)
{
var gc = _groupsCacheWithInternalGroups.Get(msgType);
if (gc == null)
{
return false;
}
return gc.GetGroupCache(groupTag) != null;
}
/// <summary>
/// Returns start tag for group by goup tag.
/// </summary>
/// <param name="msgType"> Type of message. </param>
/// <param name="tag"> Tag of Group length. </param>
/// <returns> Start field tag of group, if msgType does not exist return -1 </returns>
public int GetStartTagForGroup(string msgType, int tag)
{
var msgdef = _msgDefinitionsUtils.Get(msgType);
if (msgdef == null)
{
return -1;
}
// this implementations does not search in inner blocks and SMH, SMT
IList<object> objectList = msgdef.FieldOrDescrOrAlias;
return GetStartTagForGroup(tag, objectList);
}
/// <summary>
/// Method getDefUtils returns the defUtils of this FixUtils object.
/// </summary>
/// <returns> the defUtils of this FixUtils object. </returns>
public MessageDefinitionsUtils GetMessageDefUtils()
{
return _msgDefinitionsUtils;
}
/// <summary>
/// Method getBlockDefUtils returns the blockDefUtils of this FixUtils
/// object.
/// </summary>
/// <returns> the blockDefUtils of this FixUtils object. </returns>
public BlockDefinitionsUtils GetBlockDefUtils()
{
return _blockDefinitionsUtils;
}
/// <summary>
/// Method getFixdic returns the fixdic of this FixUtils object.
/// </summary>
/// <returns> the fixdic of this FixUtils object. </returns>
public DictionaryTypes GetFixdic()
{
return _dictionaryTypes;
}
/// <summary>
/// Method getSmhDef returns the smhDef of this FixUtils object.
/// </summary>
/// <returns> the smhDef of this FixUtils object. </returns>
public Blockdef GetSmhDef()
{
return _smhDef;
}
/// <summary>
/// Method getSmtDef returns the smtDef of this FixUtils object.
/// </summary>
/// <returns> the smtDef of this FixUtils object. </returns>
public Blockdef GetSmtDef()
{
return _smtDef;
}
private void PrepareFieldDefCache()
{
var msgCollection = _msgDefinitionsUtils.Get();
// puts header fields
var blockdef = _blockDefinitionsUtils.Get(Constants.Smh);
var fielddefs = GetFieldDefs(blockdef.FieldOrDescrOrGroup, true);
_fieldDefMessageTypeCache[Constants.Smh] = fielddefs;
PrepareFieldDefTagMap(fielddefs);
// puts messages fields
foreach (var msgdef in msgCollection)
{
IList<object> list = msgdef.FieldOrDescrOrAlias;
fielddefs = GetFieldDefs(list, true);
_fieldDefMessageTypeCache[msgdef.Msgtype] = fielddefs;
PrepareFieldDefTagMap(fielddefs);
}
// puts trailer fields
blockdef = _blockDefinitionsUtils.Get(Constants.Smt);
fielddefs = GetFieldDefs(blockdef.FieldOrDescrOrGroup, true);
_fieldDefMessageTypeCache[Constants.Smt] = fielddefs;
PrepareFieldDefTagMap(fielddefs);
}
private void PrepareFieldDefTagMap(IList<Fielddef> fielddefs)
{
var size = fielddefs.Count;
for (var i = 0; i < size; i++)
{
var fielddef = fielddefs[i];
_fieldDefTagIdCache[fielddef.Tag] = fielddef;
_fieldDefNameCache[fielddef.Name.ToLower()] = fielddef;
}
}
/// <summary>
/// Gets the collection of field defs.
/// </summary>
/// <param name="fieldsWithGroupsAndBlocks"> the collection with fields, groups and blocks </param>
public IList<object> GetFieldDefsHier<T1>(IList<T1> fieldsWithGroupsAndBlocks)
{
var listLength = fieldsWithGroupsAndBlocks.Count;
IList<object> fielddefs = new List<object>();
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
object obj = fieldsWithGroupsAndBlocks[objectCount];
if (obj is Field)
{
var field = (Field)obj;
foreach (var fielddef in _fieldsdef)
{
if (fielddef.Tag == field.Tag)
{
fielddefs.Add(fielddef);
}
}
}
else if (obj is Group)
{
fielddefs.Add(GetFieldDefsHier(((Group)obj).Content));
}
else if (obj is Block)
{
var blockIdent = ((Block)obj).Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
((List<object>)fielddefs).AddRange(GetFieldDefsHier(listFromBlock));
}
}
return fielddefs;
}
/// <summary>
/// Gets the collection of field defs.
/// </summary>
/// <param name="fieldsWithGroupsAndBlocks"> the collection with fields, groups and blocks </param>
/// <param name="useGroupTags"> the flag provides to include the fields of group to result collection </param>
public IList<Fielddef> GetFieldDefs<T1>(IList<T1> fieldsWithGroupsAndBlocks, bool useGroupTags)
{
var listLength = fieldsWithGroupsAndBlocks.Count;
IList<Fielddef> fielddefs = new List<Fielddef>();
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
object obj = fieldsWithGroupsAndBlocks[objectCount];
if (obj is Field)
{
var field = (Field)obj;
foreach (var fielddef in _fieldsdef)
{
if (fielddef.Tag == field.Tag)
{
fielddefs.Add(fielddef);
}
}
}
else if (obj is Group && useGroupTags)
{
((List<Fielddef>)fielddefs).AddRange(GetFieldDefs(((Group)obj).Content, true));
}
else if (obj is Block)
{
var blockIdent = ((Block)obj).Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
((List<Fielddef>)fielddefs).AddRange(GetFieldDefs(listFromBlock, true));
}
}
return fielddefs;
}
/// <summary>
/// Returns list of tags of fields from input list.
/// </summary>
/// <param name="list"> Input list </param>
/// <returns> List of tags of fields </returns>
public IList<int> GetFieldsTags<T1>(IList<T1> list)
{
var listLength = list.Count;
var tags = new List<int>();
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
var obj = list[objectCount];
switch (obj)
{
case Field field:
tags.Add(field.Tag);
break;
case Group grp:
tags.AddRange(GetFieldsTags(grp.Content));
break;
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
tags.AddRange(GetFieldsTags(listFromBlock));
break;
}
}
}
return tags;
}
/// <summary>
/// Returns list of tags of fields from input list.
/// </summary>
/// <param name="list"> Input list </param>
/// <returns> List of tags of fields </returns>
public IList<Field> GetFields<T1>(IList<T1> list)
{
var listLength = list.Count;
IList<Field> tags = new List<Field>();
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
object obj = list[objectCount];
if (obj is Field)
{
var field = (Field)obj;
tags.Add(field);
}
else if (obj is Group)
{
((List<Field>)tags).AddRange(GetFields(((Group)obj).Content));
}
else if (obj is Block)
{
var blockIdent = ((Block)obj).Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
((List<Field>)tags).AddRange(GetFields(listFromBlock));
}
}
return tags;
}
/// <summary>
/// Returns list of tags of fields from input list.
/// </summary>
/// <param name="messageType"> the type of FIX Message. </param>
/// <returns> List of tags of fields </returns>
public IList<Field> GetFields(string messageType)
{
return GetFields(_msgDefinitionsUtils.Get(messageType).FieldOrDescrOrAlias);
}
public GroupTagInfo GetTagInfoAboutGroup(IList<object> fieldOrDescrOrAlias, int tag)
{
var groupTagInfo = new GroupTagInfo(false, GroupTagInfo.DefaultRootGroupTag);
if (fieldOrDescrOrAlias == null)
{
return new GroupTagInfo(false, GroupTagInfo.DefaultRootGroupTag);
}
var countOfElemtntsInList = fieldOrDescrOrAlias.Count;
for (var indexOfElement = 0; indexOfElement < countOfElemtntsInList; indexOfElement++)
{
var o = fieldOrDescrOrAlias[indexOfElement];
if (o is Group grp)
{
if (HasTagInContent(grp.Content, tag))
{
return new GroupTagInfo(true, grp.Nofield);
}
groupTagInfo = GetTagInfoAboutGroup(grp.Content, tag);
if (CheckChangeOfIngo(groupTagInfo))
{
break;
}
}
else if (o is Block block)
{
var blockIdent = block.Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
groupTagInfo = GetTagInfoAboutGroup(listFromBlock, tag);
if (CheckChangeOfIngo(groupTagInfo))
{
break;
}
}
}
return groupTagInfo;
}
/// <summary>
/// Counts the group length.
/// </summary>
/// <param name="message"> the message </param>
/// <param name="indexOfGroupTags"> the index of group tag </param>
/// <param name="startGroupTag"> the start group tag </param>
/// <param name="lengthOfGroupTag"> the length of group tag </param>
/// <param name="stackOfGroupsTag"> the group tags </param>
/// <param name="messageType"> the message type </param>
/// <param name="rootGroupStartTag"> the root group start tag </param>
public int CountLengthForGroupUnit(TagValue[] message, int indexOfGroupTags, int startGroupTag,
TagValue lengthOfGroupTag, ISet<int> stackOfGroupsTag, string messageType, int rootGroupStartTag)
{
IList<int> theFirstTags = new List<int>();
var lengthOfGroup = 0;
if (indexOfGroupTags == -1)
{
var contOfGroups = 0;
theFirstTags.Add(lengthOfGroupTag.TagId);
theFirstTags.Add(rootGroupStartTag);
for (var indexOfFiled = 0; indexOfFiled < message.Length; indexOfFiled++)
{
var fixField = message[indexOfFiled];
if (contOfGroups == 1)
{
var tag = fixField.TagId;
if (theFirstTags.Contains(tag))
{
return lengthOfGroup;
}
if (stackOfGroupsTag.Contains(tag))
{
lengthOfGroup = indexOfFiled + 1;
}
else
{
var fieldList = FixMessageFactory.NewInstanceFromPool();
fieldList.AddAll(message.ToList());
var grFieldMap = GetGroupTagsWithInternalGroups(messageType, fixField.TagId, fieldList);
if (grFieldMap != null && grFieldMap.Count > 0)
{
return lengthOfGroup;
}
}
}
else
{
if (fixField.TagId == startGroupTag)
{
--contOfGroups;
}
// if count of groups higer than size of message.
if (indexOfFiled == message.Length - 1)
{
return CountLengthForOneGroupUnit(message, indexOfGroupTags, startGroupTag,
lengthOfGroupTag, stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
}
}
else
{
var lengthOfPart = message.Length - (indexOfGroupTags + 1);
if (lengthOfPart > 0)
{
var partOfMessageWithGroup = new TagValue[lengthOfPart];
Array.Copy(message, indexOfGroupTags + 1, partOfMessageWithGroup, 0, lengthOfPart);
lengthOfGroup = CountLengthForGroupUnit(partOfMessageWithGroup, -1, startGroupTag, lengthOfGroupTag,
stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
return lengthOfGroup;
}
/// <summary>
/// Counts the group length.
/// </summary>
/// <param name="message"> the message </param>
/// <param name="indexOfGroupTags"> the index of group tag </param>
/// <param name="startGroupTag"> the start group tag </param>
/// <param name="lengthOfGroupTag"> the length of group tag </param>
/// <param name="stackOfGroupsTag"> the group tags </param>
/// <param name="messageType"> the message type </param>
/// <param name="rootGroupStartTag"> the root group start tag </param>
public int CountLengthForGroupUnit(Message.FixMessage message, int indexOfGroupTags, int startGroupTag,
TagValue lengthOfGroupTag, ISet<int> stackOfGroupsTag, string messageType, int rootGroupStartTag)
{
IList<int> theFirstTags = new List<int>();
var lengthOfGroup = 0;
var msgSize = message.Length;
if (indexOfGroupTags == -1)
{
theFirstTags.Add(lengthOfGroupTag.TagId);
theFirstTags.Add(rootGroupStartTag);
for (var indexOfFiled = 0; indexOfFiled < msgSize; indexOfFiled++)
{
var fixField = message[indexOfFiled];
var tag = fixField.TagId;
if (theFirstTags.Contains(tag))
{
// checks if the parent root tag.
if (rootGroupStartTag == tag)
{
// checks if index less than size of message
if (indexOfFiled + 1 < msgSize)
{
// checks if next tag from the group
if (stackOfGroupsTag.Contains(message[indexOfFiled + 1].TagId))
{
lengthOfGroup = indexOfFiled + 1;
continue;
}
// checks the last but one tag from the group.
if (indexOfFiled + 2 == msgSize)
{
lengthOfGroup = indexOfFiled + 1;
continue;
}
}
}
return lengthOfGroup;
}
if (stackOfGroupsTag.Contains(tag))
{
lengthOfGroup = indexOfFiled + 1;
// if count of groups higer than size of message.
if (indexOfFiled == msgSize - 1)
{
return CountLengthForOneGroupUnit(message, indexOfGroupTags, startGroupTag,
lengthOfGroupTag, stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
else
{
var grFieldMap = GetGroupTagsWithInternalGroups(messageType, fixField.TagId, message);
if (grFieldMap != null && grFieldMap.Count > 0)
{
return lengthOfGroup;
}
}
}
}
else
{
var lengthOfPart = msgSize - (indexOfGroupTags + 1);
if (lengthOfPart > 0)
{
var partOfMessageWithGroup = new Message.FixMessage();
CopyFixElements(message, indexOfGroupTags + 1, partOfMessageWithGroup, lengthOfPart);
lengthOfGroup = CountLengthForGroupUnit(partOfMessageWithGroup, -1, startGroupTag, lengthOfGroupTag,
stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
return lengthOfGroup;
}
private void CopyFixElements(Message.FixMessage src, int srcPos, Message.FixMessage dest, int length)
{
var srcSize = src.Length;
for (var srcIndex = 0; srcIndex < srcSize; srcIndex++)
{
if (srcIndex < srcPos)
{
continue;
}
if (srcIndex == length + srcPos)
{
break;
}
dest.Add(src[srcIndex]);
}
}
/// <summary>
/// Counts the group length.
/// </summary>
/// <param name="message"> the message </param>
/// <param name="indexOfGroupTags"> the index of group tag </param>
/// <param name="startGroupTag"> the start group tag </param>
/// <param name="lengthOfGroupTag"> the length of group tag </param>
/// <param name="stackOfGroupsTag"> the group tags </param>
/// <param name="messageType"> the message type </param>
/// <param name="rootGroupStartTag"> the root group start tag </param>
public int CountLengthForOneGroupUnit(TagValue[] message, int indexOfGroupTags, int startGroupTag,
TagValue lengthOfGroupTag, ISet<int> stackOfGroupsTag, string messageType, int rootGroupStartTag)
{
IList<int> theFirstTags = new List<int>();
var lengthOfGroup = 0;
if (indexOfGroupTags == -1)
{
// int contOfGroups =
// Integer.parseInt(lengthOfGroupTag.GetStringValue());
theFirstTags.Add(lengthOfGroupTag.TagId);
theFirstTags.Add(rootGroupStartTag);
for (var indexOfFiled = 0; indexOfFiled < message.Length; indexOfFiled++)
{
var fixField = message[indexOfFiled];
var tag = fixField.TagId;
// check required first tag for the group;
if (indexOfFiled != 0 && tag == startGroupTag)
{
return lengthOfGroup;
}
if (theFirstTags.Contains(tag))
{
return lengthOfGroup;
}
if (stackOfGroupsTag.Contains(tag))
{
lengthOfGroup = indexOfFiled + 1;
}
else
{
var fieldList = FixMessageFactory.NewInstanceFromPool();
fieldList.AddAll(message);
var grFieldMap = GetGroupTagsWithInternalGroups(messageType, fixField.TagId, fieldList);
if (grFieldMap != null && grFieldMap.Count > 0)
{
return lengthOfGroup;
}
}
}
}
else
{
var lengthOfPart = message.Length - (indexOfGroupTags + 1);
if (lengthOfPart > 0)
{
var partOfMessageWithGroup = new TagValue[lengthOfPart];
Array.Copy(message, indexOfGroupTags + 1, partOfMessageWithGroup, 0, lengthOfPart);
lengthOfGroup = CountLengthForOneGroupUnit(partOfMessageWithGroup, -1, startGroupTag,
lengthOfGroupTag, stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
return lengthOfGroup;
}
/// <summary>
/// Counts the group length.
/// </summary>
/// <param name="message"> the message </param>
/// <param name="indexOfGroupTags"> the index of group tag </param>
/// <param name="startGroupTag"> the start group tag </param>
/// <param name="lengthOfGroupTag"> the length of group tag </param>
/// <param name="stackOfGroupsTag"> the group tags </param>
/// <param name="messageType"> the message type </param>
/// <param name="rootGroupStartTag"> the root group start tag </param>
public int CountLengthForOneGroupUnit(Message.FixMessage message, int indexOfGroupTags, int startGroupTag,
TagValue lengthOfGroupTag, ISet<int> stackOfGroupsTag, string messageType, int rootGroupStartTag)
{
IList<int> theFirstTags = new List<int>();
var lengthOfGroup = 0;
var msgSize = message.Length;
if (indexOfGroupTags == -1)
{
// int contOfGroups =
// Integer.parseInt(lengthOfGroupTag.GetStringValue());
theFirstTags.Add(lengthOfGroupTag.TagId);
theFirstTags.Add(rootGroupStartTag);
for (var indexOfFiled = 0; indexOfFiled < msgSize; indexOfFiled++)
{
var fixField = message[indexOfFiled];
var tag = fixField.TagId;
// check required first tag for the group;
if (indexOfFiled != 0 && tag == startGroupTag)
{
return lengthOfGroup;
}
if (theFirstTags.Contains(tag))
{
return lengthOfGroup;
}
if (stackOfGroupsTag.Contains(tag))
{
lengthOfGroup = indexOfFiled + 1;
}
else
{
var grFieldMap = GetGroupTagsWithInternalGroups(messageType, fixField.TagId, message);
if (grFieldMap != null && grFieldMap.Count > 0)
{
return lengthOfGroup;
}
}
}
}
else
{
var lengthOfPart = msgSize - (indexOfGroupTags + 1);
if (lengthOfPart > 0)
{
var partOfMessageWithGroup = new Message.FixMessage();
CopyFixElements(message, indexOfGroupTags + 1, partOfMessageWithGroup, lengthOfPart);
lengthOfGroup = CountLengthForOneGroupUnit(partOfMessageWithGroup, -1, startGroupTag,
lengthOfGroupTag, stackOfGroupsTag, messageType, rootGroupStartTag);
}
}
return lengthOfGroup;
}
/// <summary>
/// Gets the field.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="tag"> the tag </param>
/// <returns> field if tag occurred otherwise nl </returns>
public Field GetField(string msgType, int? tag)
{
var fields = GetFields(msgType);
foreach (var field in fields)
{
if (field.Tag == tag)
{
return field;
}
}
return null;
}
/// <summary>
/// Checks if tag is required for message type.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <param name="tag"> the tag </param>
public bool IsRequiredTag(string msgType, int tag)
{
return GetRequiredTagsForMessage(msgType).Contains(tag);
}
private bool CheckChangeOfIngo(GroupTagInfo groupTagInfo)
{
return groupTagInfo.IsGroupTag() && GroupTagInfo.DefaultRootGroupTag != groupTagInfo.GetRootGroupTag();
}
private bool HasTagInContent(IList<object> content, int tag)
{
var hasTag = false;
if (content == null)
{
return hasTag;
}
var countOfElemtntsInList = content.Count;
for (var indexOfElement = 0; indexOfElement < countOfElemtntsInList; indexOfElement++)
{
var o = content[indexOfElement];
if (o is Field field)
{
if (field.Tag == tag)
{
hasTag = true;
break;
}
}
else if (o is Block block)
{
var blockIdent = block.Idref;
//TODO: get rid of object - replace with IFindable
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (HasTagInContent(listFromBlock, tag))
{
hasTag = true;
break;
}
}
}
return hasTag;
}
private void PrepareValblockdefs()
{
var valblockDefs = new List<Valblockdef>();
var types = _dictionaryTypes.Dictionaries;
if (types != null)
{
foreach (var type in types)
{
if (type is Fixdic fixdic)
{
valblockDefs.AddRange(fixdic.Fielddic.Valblockdef);
}
}
}
_valblockdefs = new ReadOnlyCollection<Valblockdef>(valblockDefs);
}
/// <summary>
/// Prepare fields definitions for input dictionary of FIX protocol.
/// </summary>
/// <param name="dictionaryTypes"> Input dictionary of FIX protocol </param>
public void PrepareFieldsDefinitions(DictionaryTypes dictionaryTypes)
{
var fixDictionaries = dictionaryTypes.Dictionaries;
if (fixDictionaries == null)
{
return;
}
// fixed bug, allTags and fieldsdef builds only for last dictionary
// bug 14795: Validation of tag values does not function for FIXT.1.1 protocol
var fielddefs = new List<Fielddef>();
AddFixField(fielddefs, fixDictionaries);
AddFixtFields(fielddefs, fixDictionaries);
_fieldsdef = fielddefs.ToArray();
_allTags = new int[_fieldsdef.Length];
for (var i = 0; i < _fieldsdef.Length; i++)
{
_allTags[i] = _fieldsdef[i].Tag;
}
}
private void AddFixField(List<Fielddef> fielddefs, IList<IType> typeList)
{
foreach (var type in typeList)
{
if (type is Fixdic fixdic)
{
if (!fixdic.IsFixtDictionary)
{
var fielddefList = fixdic.Fielddic.Fielddef;
if (fielddefList != null)
{
fielddefs.AddRange(fielddefList);
}
}
}
}
}
private void AddFixtFields(List<Fielddef> fielddefs, IList<IType> dictionaries)
{
foreach (var type in dictionaries)
{
if (type is Fixdic fixdic)
{
if (fixdic.IsFixtDictionary)
{
// fixed bug: 15039 FIXT1.1. sessions 4.0, 4.1, 4.2 aren't created.
// and bug 15064 Unexpected behavior after first Heartbeat for FIXT.1.1. FIX.4.0 and FIX.4.2 sessions
var fixtFieldList = fixdic.Fielddic.Fielddef.ToList();
for (var j = fixtFieldList.Count - 1; j > 0; j--)
{
var fixtField = fixtFieldList[j];
for (var i = 0; i < fielddefs.Count; i++)
{
var fieldDef = fielddefs[i];
var tag = fieldDef.Tag;
if (tag == fixtField.Tag)
{
fielddefs[i] = fixtField; // replace field from fixt dictionary
fixtFieldList.RemoveAt(j);
break;
}
}
}
// add filtered fixt fields
fielddefs.AddRange(fixtFieldList);
}
}
}
}
/// <summary>
/// Method isContainsTagInFieldsList, verifies whether the tag is present in
/// a list.
/// </summary>
/// <param name="list"> the input list </param>
/// <param name="tag"> the tag </param>
/// <returns> <c>true</c> if presents, otherwise <c>false</c> </returns>
private bool IsContainsTagInFieldsList(IList<object> list, int tag)
{
var listLength = list.Count;
for (var objectCount = 0; objectCount < listLength; objectCount++)
{
var obj = list[objectCount];
if (obj is Field field && field.Tag == tag)
{
return true;
}
if (obj is Group group && IsContainsTagInFieldsList(@group.Content, tag))
{
return true;
}
if (obj is Block block)
{
var blockIdent = block.Idref;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (IsContainsTagInFieldsList(listFromBlock, tag))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Verifies whether the tag is present in a message by type of message.
/// </summary>
/// <param name="messageType"> the type of message </param>
/// <param name="tag"> the tag </param>
/// <returns> <c>true</c> if presents, otherwise <c>false</c> </returns>
private bool IsContainsTagInFieldsList(string messageType, int tag)
{
var tags = _fieldCache[messageType];
return tags != null && tags.Contains(tag);
}
/// <summary>
/// Puts conditions into cache.
/// </summary>
private void PutConditions()
{
var msgdefCollection = _msgDefinitionsUtils.Get();
foreach (var msgdef in msgdefCollection)
{
var list = msgdef.FieldOrDescrOrAlias;
var key = msgdef.Msgtype;
if (_conditionsCache.TryGetValue(msgdef.Msgtype, out var value))
{
_conditionsCache.Remove(key);
}
_conditionsCache.Add(key, GetConditions(list, -1, ConditionalType.Message));
}
}
/// <summary>
/// Method getDef returns map of message definition by message type.
/// </summary>
/// <param name="messageType"> the type of message </param>
/// <returns> IFixMessageDefinitions definitions of message </returns>
private IFindable GetDefinition(string messageType)
{
return (IFindable)_msgDefinitionsUtils.Get(messageType) ?? _blockDefinitionsUtils.Get(messageType);
}
private IDictionary<int, Field> GetGroupFields<T1>(int groupTag, IList<T1> messageFieldsOrGroups,
Message.FixMessage message, bool withInternalGroup)
{
var fields = new Dictionary<int, Field>(32);
foreach (object obj in messageFieldsOrGroups)
{
if (obj is Group)
{
var group = (Group)obj;
if (@group.Nofield == groupTag)
{
fields.PutAll(GetGroupFields(@group.Content, message, withInternalGroup));
}
else
{
fields.PutAll(GetGroupFields(groupTag, @group.Content, message, withInternalGroup));
}
}
else if (obj is Block)
{
var block = (Block)obj;
var condition = block.Condreq;
IList<object> listFromBlock = _blockDefinitionsUtils.Get(block.Idref).FieldOrDescrOrGroup;
var flatListFromBlock = GetFields(listFromBlock);
if (HasRequiredTagInMessage(flatListFromBlock, message, withInternalGroup) ||
IsConditionalRequired(condition, message))
{
fields.PutAll(GetGroupFields(groupTag, listFromBlock, message, withInternalGroup));
}
}
}
return fields;
}
private bool HasGroup<T1>(IList<T1> listFromBlock)
{
var objectCount = listFromBlock.Count;
for (var objectIndex = 0; objectIndex < objectCount; objectIndex++)
{
var obj = listFromBlock[objectIndex];
switch (obj)
{
case Group _:
return true;
case Block block:
{
var blockId = block.Idref ?? block.Name;
if (HasGroup(_blockDefinitionsUtils.Get(blockId).FieldOrDescrOrGroup))
{
return true;
}
break;
}
}
}
return false;
}
private int GetStartTagForGroup<T1>(int tag, IList<T1> objectList)
{
var objectCount = objectList.Count;
var startTag = -1;
for (var objectIndex = 0; objectIndex < objectCount; objectIndex++)
{
if (startTag != -1)
{
break;
}
var obj = objectList[objectIndex];
if (obj is Group grp)
{
if (grp.Nofield == tag)
{
startTag = grp.Startfield;
break;
}
startTag = GetStartTagForGroup(tag, grp.Content);
}
else if (obj is Block block)
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (HasGroup(listFromBlock))
{
startTag = GetStartTagForGroup(tag, listFromBlock);
}
}
}
return startTag;
}
private IDictionary<int, Field> GetGroupFields<T1>(IList<T1> objectList, Message.FixMessage fixMessage,
bool withInternalGroup)
{
var fields = new Dictionary<int, Field>(32);
var objectCount = objectList.Count;
for (var objectIndex = 0; objectIndex < objectCount; objectIndex++)
{
var obj = objectList[objectIndex];
switch (obj)
{
case Field field:
fields[field.Tag] = field;
break;
case Group group when withInternalGroup:
{
if (fixMessage.GetTag(@group.Nofield) != null)
{
fields.PutAll(GetGroupFields(@group.Content, fixMessage, withInternalGroup));
}
break;
}
case Group group:
fields.Remove(@group.Nofield);
break;
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (HasRequiredTagInMessage(listFromBlock, fixMessage, withInternalGroup) ||
IsConditionalRequired(block.Condreq, fixMessage) || IsRequired(block.Req))
{
fields.PutAll(GetGroupFields(listFromBlock, fixMessage, withInternalGroup));
}
break;
}
}
}
return fields;
}
public GroupsCache GetGroupsCache(string msgType)
{
var groupsCache = _groupsCacheWithInternalGroups.Get(msgType);
return groupsCache ?? new GroupsCache();
}
private void PrepareGroupsCache()
{
var msgTypes = _msgDefinitionsUtils.GetMessageTypes();
foreach (var msgType in msgTypes)
{
var msgdef = _msgDefinitionsUtils.Get(msgType);
PrepareGroupsCache(msgType, msgdef.FieldOrDescrOrAlias);
}
}
private void PrepareGroupsCache<T1>(string msgType, IList<T1> objectList)
{
var objectCount = objectList.Count;
for (var objectIndex = 0; objectIndex < objectCount; objectIndex++)
{
var obj = objectList[objectIndex];
switch (obj)
{
case Group grp:
{
var groupId = grp.Nofield;
var startFieldId = grp.Startfield;
var groupCache = new GroupCache(groupId, startFieldId);
groupCache.PutAllCache(GetGroupFields(msgType, groupId, grp.Content, true));
var groupsCache = _groupsCacheWithInternalGroups.Get(msgType);
PutValueIntoGroupsCache(msgType, groupId, groupId, groupCache, groupsCache);
break;
}
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
PrepareGroupsCache(msgType, listFromBlock);
break;
}
}
}
}
private IDictionary<int, Field> GetGroupFields<T1>(string msgType, int groupId, IList<T1> objectList,
bool withInternalGroup)
{
var fields = new Dictionary<int, Field>(32);
var objectCount = objectList.Count;
for (var objectIndex = 0; objectIndex < objectCount; objectIndex++)
{
var obj = objectList[objectIndex];
switch (obj)
{
case Field field:
fields[field.Tag] = field;
break;
case Group group when withInternalGroup:
{
var internalGroupId = @group.Nofield;
var internalGroupStartFieldId = @group.Startfield;
var groupCache = new GroupCache(internalGroupId, internalGroupStartFieldId);
groupCache.PutAllCache(GetGroupFields(msgType, internalGroupId, @group.Content,
withInternalGroup));
var groupsCache = _groupsCacheWithInternalGroups.Get(msgType);
PutValueIntoGroupsCache(msgType, groupId, internalGroupId, groupCache, groupsCache);
break;
}
case Group group:
fields.Remove(@group.Nofield);
break;
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
var blockCache = new BlockCache(block, groupId);
blockCache.PutAllCache(GetGroupFields(msgType, groupId, listFromBlock, withInternalGroup));
var groupsCache = _groupsCacheWithInternalGroups.Get(msgType);
PutValueIntoGroupsCache(msgType, groupId, groupId, blockCache, groupsCache);
break;
}
}
}
return fields;
}
private void PutValueIntoGroupsCache<T1, T2>(string msgType, int parentGroupId, int groupId,
ICache<T1, T2> iCache, GroupsCache groupsCache)
{
if (groupsCache == null)
{
groupsCache = new GroupsCache();
}
switch (iCache)
{
case BlockCache blockCache:
groupsCache.PutBlockCache(parentGroupId, blockCache);
break;
case GroupCache groupCache:
groupsCache.PutGroupCache(parentGroupId, groupId, groupCache);
break;
}
_groupsCacheWithInternalGroups.Put(msgType, groupsCache);
}
private bool IsRequired(string text)
{
return text != null && !text.Equals("N");
}
private bool IsConditionalRequired(string condition, Message.FixMessage fixMessage)
{
if (condition is null)
{
return false;
}
var conditionValidateParser = new ConditionValidateParser(condition);
var iCondition = conditionValidateParser.GetCondition();
return iCondition.IsRequired(fixMessage);
}
/// <summary>
/// Checks if message body contain tag.
/// <i>Note: tags from group ignored.</i>
/// </summary>
/// <param name="messageType"> the message type </param>
/// <param name="tag"> the tag </param>
public bool IsMessageContainField(string messageType, int tag)
{
var msgDef = GetMessageDefUtils().Get(messageType);
return IsMessageContainField(msgDef.FieldOrDescrOrAlias, tag);
}
private bool IsMessageContainField<T1>(IList<T1> fields, int tag)
{
foreach (var obj in fields)
{
switch (obj)
{
case Field field when field.Tag == tag:
return true;
case Block block:
{
var blockIdent = block.Idref;
var listFromBlock = _blockDefinitionsUtils.Get(blockIdent).FieldOrDescrOrGroup;
if (IsMessageContainField(listFromBlock, tag))
{
return true;
}
break;
}
}
}
return false;
}
/// <summary>
/// Gets valblockdef by idRef.
/// </summary>
/// <param name="idRef"> the id ref name </param>
/// <returns> Valblockdef </returns>
public Valblockdef GetValblockdef(string idRef)
{
foreach (var valBlock in _valblockdefs)
{
if (idRef.Equals(valBlock.Id))
{
return valBlock;
}
}
return null;
}
public static string DescrToHtmlStr(Descr descr)
{
if (descr == null)
{
return null;
}
var xmlSerializer = new XmlSerializer(typeof(Descr));
using (var sww = new Utf8Writer())
{
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Indent = true
};
using (var writer = XmlWriter.Create(sww, settings))
{
writer.WriteStartDocument(true);
//hack to avoid namespace writing to the descr element
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
xmlSerializer.Serialize(writer, descr, ns);
return sww.ToString();
}
}
}
public static string CommentToHtmlStr(Comment comment)
{
if (comment == null)
{
return null;
}
var xmlSerializer = new XmlSerializer(typeof(Comment));
using (var sww = new Utf8Writer())
{
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Indent = true
};
using (var writer = XmlWriter.Create(sww, settings))
{
writer.WriteStartDocument(true);
//hack to avoid namespace writing to the comment element
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
xmlSerializer.Serialize(writer, comment, ns);
return sww.ToString();
}
}
}
}
}