diff --git a/QuickFIXn/DataDictionary/DDField.cs b/QuickFIXn/DataDictionary/DDField.cs index f19f818ff..ee775be5b 100644 --- a/QuickFIXn/DataDictionary/DDField.cs +++ b/QuickFIXn/DataDictionary/DDField.cs @@ -13,22 +13,22 @@ public class DDField /// /// dictionary of enum=>description values /// - public DDField(int tag, String name, IReadOnlyDictionary enums, String fixFldType) + public DDField(int tag, string name, IReadOnlyDictionary enums, string fixFldType) { this.Tag = tag; this.Name = name; this.EnumDict = enums.ToFrozenDictionary(); this.FixFldType = fixFldType; - this.FieldType = FieldTypeFromFix(this.FixFldType, out bool isMVFWE); - this.IsMultipleValueFieldWithEnums = isMVFWE; + this.FieldType = FieldTypeFromFix(this.FixFldType, out bool isMultipleValueFieldWithEnums); + this.IsMultipleValueFieldWithEnums = isMultipleValueFieldWithEnums; } public int Tag { get; private set; } - public String Name { get; private set; } + public string Name { get; private set; } - public IReadOnlyDictionary EnumDict { get; } + public IReadOnlyDictionary EnumDict { get; } - public String FixFldType { get; private set; } + public string FixFldType { get; private set; } public Type FieldType { get; private set; } /// @@ -36,14 +36,14 @@ public DDField(int tag, String name, IReadOnlyDictionary enums, /// public bool IsMultipleValueFieldWithEnums { get; private set; } - public Boolean HasEnums() + public bool HasEnums() { return EnumDict.Count > 0; } - private Type FieldTypeFromFix(String type, out bool multipleValueFieldWithEnums) + private Type FieldTypeFromFix(string type, out bool isMultipleValueFieldWithEnums) { - multipleValueFieldWithEnums = false; + isMultipleValueFieldWithEnums = false; switch (type) { @@ -54,9 +54,9 @@ private Type FieldTypeFromFix(String type, out bool multipleValueFieldWithEnums) case "AMT": return typeof(Fields.DecimalField); case "QTY": return typeof(Fields.DecimalField); case "CURRENCY": return typeof(Fields.StringField); - case "MULTIPLEVALUESTRING": multipleValueFieldWithEnums = true; return typeof(Fields.StringField); - case "MULTIPLESTRINGVALUE": multipleValueFieldWithEnums = true; return typeof(Fields.StringField); - case "MULTIPLECHARVALUE": multipleValueFieldWithEnums = true; return typeof(Fields.StringField); + case "MULTIPLEVALUESTRING": isMultipleValueFieldWithEnums = true; return typeof(Fields.StringField); + case "MULTIPLESTRINGVALUE": isMultipleValueFieldWithEnums = true; return typeof(Fields.StringField); + case "MULTIPLECHARVALUE": isMultipleValueFieldWithEnums = true; return typeof(Fields.StringField); case "EXCHANGE": return typeof(Fields.StringField); case "UTCTIMESTAMP": return typeof(Fields.DateTimeField); case "BOOLEAN": return typeof(Fields.BooleanField); diff --git a/QuickFIXn/DataDictionary/DDMap.cs b/QuickFIXn/DataDictionary/DDMap.cs index 0f1f5cee6..a68e5fe96 100644 --- a/QuickFIXn/DataDictionary/DDMap.cs +++ b/QuickFIXn/DataDictionary/DDMap.cs @@ -1,68 +1,66 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace QuickFix.DataDictionary -{ - /// - /// Represents the DD definition for a message type - /// - public class DDMap : IFieldMapSpec - { - public Dictionary Fields = new Dictionary(); - public Dictionary Groups = new Dictionary(); - public HashSet ReqFields = new HashSet(); +namespace QuickFix.DataDictionary; - public void AddField(DDField fld) - { - Fields.Add(fld.Tag, fld); - } +/// +/// Represents the DD definition for a message type +/// +public class DDMap : IFieldMapSpec +{ + public Dictionary Fields = new(); + public Dictionary Groups = new(); + public HashSet ReqFields = []; - public void AddGroup(DDGrp grp) - { - Groups.Add(grp.Delim, grp); - } + public void AddField(DDField fld) + { + Fields.Add(fld.Tag, fld); + } - public Boolean IsGroup(int tag) - { - return Groups.ContainsKey(tag); - } + public void AddGroup(DDGrp grp) + { + Groups.Add(grp.Delim, grp); + } - public Boolean IsField(int tag) - { - return Fields.ContainsKey(tag); - } + public Boolean IsGroup(int tag) + { + return Groups.ContainsKey(tag); + } - public DDGrp GetGroup(int tag) - { - return Groups[tag]; - } + public Boolean IsField(int tag) + { + return Fields.ContainsKey(tag); + } - /// - /// Same as GetGroup, just a more-general return type. - /// - /// - /// - public IGroupSpec GetGroupSpec(int tag) - { - return Groups[tag]; - } + public DDGrp GetGroup(int tag) + { + return Groups[tag]; } /// - /// Represents the DD definition for a group type + /// Same as GetGroup, just a more-general return type. /// - public class DDGrp : DDMap, IGroupSpec + /// + /// + public IGroupSpec GetGroupSpec(int tag) + { + return Groups[tag]; + } +} + +/// +/// Represents the DD definition for a group type +/// +public class DDGrp : DDMap, IGroupSpec +{ + public int NumFld { get; set; } + public int Delim { get; set; } + public Boolean Required = false; + public DDGrp() : base() { - public int NumFld { get; set; } - public int Delim { get; set; } - public Boolean Required = false; - public DDGrp() : base() - { - Delim = 0; - NumFld = 0; - Required = false; - } + Delim = 0; + NumFld = 0; + Required = false; } } + diff --git a/QuickFIXn/DataDictionary/DictionaryParseException.cs b/QuickFIXn/DataDictionary/DictionaryParseException.cs index 4c74c757c..aba8dbc14 100644 --- a/QuickFIXn/DataDictionary/DictionaryParseException.cs +++ b/QuickFIXn/DataDictionary/DictionaryParseException.cs @@ -1,13 +1,12 @@ using System; -namespace QuickFix +namespace QuickFix.DataDictionary; + +public class DictionaryParseException : ApplicationException { - public class DictionaryParseException : ApplicationException - { - public DictionaryParseException() { } - public DictionaryParseException(string message) - : base(message) { } - public DictionaryParseException(string message, System.Exception inner) - : base(message, inner) { } - } + public DictionaryParseException() { } + public DictionaryParseException(string message) + : base(message) { } + public DictionaryParseException(string message, System.Exception inner) + : base(message, inner) { } } diff --git a/QuickFIXn/DataDictionary/IFieldMapSpec.cs b/QuickFIXn/DataDictionary/IFieldMapSpec.cs index 3c3091b79..da3637770 100644 --- a/QuickFIXn/DataDictionary/IFieldMapSpec.cs +++ b/QuickFIXn/DataDictionary/IFieldMapSpec.cs @@ -1,31 +1,27 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace QuickFix.DataDictionary +namespace QuickFix.DataDictionary; + +public interface IFieldMapSpec { - public interface IFieldMapSpec - { - /// - /// Returns true if this tag is for a group-counter field defined for this IFieldMapSpec - /// - /// - /// - Boolean IsGroup(int tag); + /// + /// Returns true if this tag is for a group-counter field defined for this IFieldMapSpec + /// + /// + /// + Boolean IsGroup(int tag); + + /// + /// Returns true if this tag is for a field defined for this IFieldMapSpec + /// + /// + /// + Boolean IsField(int tag); - /// - /// Returns true if this tag is for a field defined for this IFieldMapSpec - /// - /// - /// - Boolean IsField(int tag); - - /// - /// Get the IGroupSpec that corresponds to this group-counter tag - /// - /// - /// - IGroupSpec GetGroupSpec(int tag); - } + /// + /// Get the IGroupSpec that corresponds to this group-counter tag + /// + /// + /// + IGroupSpec GetGroupSpec(int tag); } diff --git a/QuickFIXn/DataDictionary/IGroupSpec.cs b/QuickFIXn/DataDictionary/IGroupSpec.cs index 7c1f6ba29..a907ab7c5 100644 --- a/QuickFIXn/DataDictionary/IGroupSpec.cs +++ b/QuickFIXn/DataDictionary/IGroupSpec.cs @@ -1,12 +1,11 @@ using System; -namespace QuickFix.DataDictionary +namespace QuickFix.DataDictionary; + +public interface IGroupSpec : IFieldMapSpec { - public interface IGroupSpec : IFieldMapSpec - { - /// - /// The tag of the delimiter field, i.e. the first field of each group member - /// - int Delim { get; set; } - } + /// + /// The tag of the delimiter field, i.e. the first field of each group member + /// + int Delim { get; set; } } diff --git a/QuickFIXn/DataDictionary/InvalidMessageTypeException.cs b/QuickFIXn/DataDictionary/InvalidMessageTypeException.cs index 4c9587531..3d304c176 100644 --- a/QuickFIXn/DataDictionary/InvalidMessageTypeException.cs +++ b/QuickFIXn/DataDictionary/InvalidMessageTypeException.cs @@ -1,19 +1,18 @@ using System; -namespace QuickFix +namespace QuickFix.DataDictionary; + +[Obsolete("This class will be removed in 1.16 (because it's unused)")] +public class InvalidMessageTypeException : ApplicationException { [Obsolete("This class will be removed in a future release (because it's unused)")] - public class InvalidMessageTypeException : ApplicationException - { - [Obsolete("This class will be removed in a future release (because it's unused)")] - public InvalidMessageTypeException() { } + public InvalidMessageTypeException() { } - [Obsolete("This class will be removed in a future release (because it's unused)")] - public InvalidMessageTypeException(string message) - : base(message) { } + [Obsolete("This class will be removed in a future release (because it's unused)")] + public InvalidMessageTypeException(string message) + : base(message) { } - [Obsolete("This class will be removed in a future release (because it's unused)")] - public InvalidMessageTypeException(string message, System.Exception inner) - : base(message, inner) { } - } + [Obsolete("This class will be removed in a future release (because it's unused)")] + public InvalidMessageTypeException(string message, System.Exception inner) + : base(message, inner) { } } diff --git a/QuickFIXn/DataDictionary/MissingRequiredFieldException.cs b/QuickFIXn/DataDictionary/MissingRequiredFieldException.cs index ffc1e4bee..626c3a2fe 100644 --- a/QuickFIXn/DataDictionary/MissingRequiredFieldException.cs +++ b/QuickFIXn/DataDictionary/MissingRequiredFieldException.cs @@ -1,28 +1,24 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; #pragma warning disable 0628 // Suppress "new protected member declared in sealed class" warning. -namespace QuickFix +namespace QuickFix.DataDictionary; + +[Obsolete("This class will be removed in 1.16 (because it's unused)")] +public sealed class MissingRequiredFieldException : ApplicationException { [Obsolete("This class will be removed in a future release (because it's unused)")] - public sealed class MissingRequiredFieldException : ApplicationException - { - [Obsolete("This class will be removed in a future release (because it's unused)")] - public MissingRequiredFieldException() { } + public MissingRequiredFieldException() { } - [Obsolete("This class will be removed in a future release (because it's unused)")] - public MissingRequiredFieldException(int field) - : base("Missing required field: " + field.ToString()) { } + [Obsolete("This class will be removed in a future release (because it's unused)")] + public MissingRequiredFieldException(int field) + : base($"Missing required field: {field}") { } - [Obsolete("This class will be removed in a future release (because it's unused)")] - public MissingRequiredFieldException(string message) - : base(message) { } + [Obsolete("This class will be removed in a future release (because it's unused)")] + public MissingRequiredFieldException(string message) + : base(message) { } - [Obsolete("This class will be removed in a future release (because it's unused)")] - public MissingRequiredFieldException(string message, System.Exception inner) - : base(message, inner) { } - } + [Obsolete("This class will be removed in a future release (because it's unused)")] + public MissingRequiredFieldException(string message, System.Exception inner) + : base(message, inner) { } } diff --git a/QuickFIXn/DataDictionary/XMLMsgComponent.cs b/QuickFIXn/DataDictionary/XMLMsgComponent.cs index 14ef2d3cf..b32e82f66 100644 --- a/QuickFIXn/DataDictionary/XMLMsgComponent.cs +++ b/QuickFIXn/DataDictionary/XMLMsgComponent.cs @@ -1,40 +1,40 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace QuickFix +namespace QuickFix.DataDictionary; + +[Obsolete("This class will be removed in 1.16 (because it's unused)")] +partial class DataDictionaryParser { - partial class DataDictionaryParser + private sealed class XMLMsgComponent { - private sealed class XMLMsgComponent - { - public enum TypeEnum { Field, Component, Group }; + public enum TypeEnum { Field, Component, Group }; - public XMLMsgComponent(string name, bool req, TypeEnum type ) - { - Name = name; - Required = req; - SubComponents = new List(); - ComponentType = type; - } - public TypeEnum ComponentType { get; set; } - public string Name { get; set; } - public bool Required { get; set; } - public List SubComponents { get; set; } + [Obsolete("This class will be removed in 1.16 (because it's unused)")] + public XMLMsgComponent(string name, bool req, TypeEnum type ) + { + Name = name; + Required = req; + SubComponents = new List(); + ComponentType = type; + } + public TypeEnum ComponentType { get; set; } + public string Name { get; set; } + public bool Required { get; set; } + public List SubComponents { get; set; } - public static TypeEnum GetComponentType(string typestr, string parentName) + [Obsolete("This class will be removed in 1.16 (because it's unused)")] + public static TypeEnum GetComponentType(string typestr, string parentName) + { + TypeEnum ctype; + switch (typestr) { - TypeEnum ctype; - switch (typestr) - { - case "field": ctype = TypeEnum.Field; break; - case "group": ctype = TypeEnum.Group; break; - case "component": ctype = TypeEnum.Component; break; - default: throw new MessageParseError("unknown component in msg: " + parentName); - } - return ctype; + case "field": ctype = TypeEnum.Field; break; + case "group": ctype = TypeEnum.Group; break; + case "component": ctype = TypeEnum.Component; break; + default: throw new MessageParseError("unknown component in msg: " + parentName); } + return ctype; } } } diff --git a/QuickFIXn/Message/Message.cs b/QuickFIXn/Message/Message.cs index f34334844..5fd743cf7 100644 --- a/QuickFIXn/Message/Message.cs +++ b/QuickFIXn/Message/Message.cs @@ -49,7 +49,7 @@ public Message( public Message(string msgstr, DD? transportDict, DD? appDict, bool validate) : this() { - PopulateHeaderFromMessageString(msgstr); + PopulateHeaderFromMessageString(msgstr, transportDict); if (IsAdmin()) FromString(msgstr, validate, transportDict, transportDict, null, false); else @@ -90,6 +90,14 @@ public static int ExtractFieldTag(string msgstr, int pos) return tag; } + /// + /// Extract a field whose length is determined by a datalength field + /// + /// + /// + /// + /// + /// public static StringField ExtractDataField(string msgstr, int dataLength, ref int pos) { try @@ -116,6 +124,13 @@ public static StringField ExtractDataField(string msgstr, int dataLength, ref in } } + /// + /// Extract a field that ends with SOH + /// + /// + /// + /// + /// public static StringField ExtractField(string msgstr, ref int pos) { try @@ -123,7 +138,7 @@ public static StringField ExtractField(string msgstr, ref int pos) int tagend = msgstr.IndexOf('=', pos); int tag = Convert.ToInt32(msgstr.Substring(pos, tagend - pos)); pos = tagend + 1; - int fieldvalend = msgstr.IndexOf((char)1, pos); + int fieldvalend = msgstr.IndexOf(SOH, pos); StringField field = new StringField(tag, msgstr.Substring(pos, fieldvalend - pos)); pos = fieldvalend + 1; @@ -149,7 +164,19 @@ public static string ExtractBeginString(string msgstr) return ExtractField(msgstr, ref i).Value; } + [Obsolete("This method is deprecated and will be removed in 1.16. Use IsHeaderField(int tag, DD? dd) instead.")] public static bool IsHeaderField(int tag) + { + return IsGenericHeaderField(tag); + } + + /// + /// Determine if field is a header according to the standard vanilla DD. + /// The header list in this function is hardcoded. + /// + /// + /// + private static bool IsGenericHeaderField(int tag) { switch (tag) { @@ -184,12 +211,21 @@ public static bool IsHeaderField(int tag) return false; } } - public static bool IsHeaderField(int tag, DD? dd) + + /// + /// Determine if field is a header field. + /// If transportDataDictionary is null, compares against a hard-coded list of header fields as + /// specified in the standard FIX data dictionaries. + /// + /// + /// + /// + public static bool IsHeaderField(int tag, DD? transportDataDictionary) { - if (IsHeaderField(tag)) + if (IsGenericHeaderField(tag)) return true; - if (dd is not null) - return dd.IsHeaderField(tag); + if (transportDataDictionary is not null) + return transportDataDictionary.IsHeaderField(tag); return false; } @@ -319,7 +355,7 @@ public override void Clear() Trailer.Clear(); } - private void PopulateHeaderFromMessageString(string msgstr) + private void PopulateHeaderFromMessageString(string msgstr, DD? dd) { Clear(); @@ -332,7 +368,7 @@ private void PopulateHeaderFromMessageString(string msgstr) if (count < 3 && Header.HEADER_FIELD_ORDER[count++] != f.Tag) return; - if(IsHeaderField(f.Tag)) + if(IsHeaderField(f.Tag, dd)) Header.SetField(f, false); else break; @@ -368,18 +404,10 @@ public void FromString( while (pos < msgstr.Length) { - StringField? f = null; - int fieldTag = ExtractFieldTag(msgstr, pos); - if (fieldTag == Tags.XmlData) - { - if (IsHeaderField(Tags.XmlDataLen)) - f = ExtractDataField(msgstr, Header.GetInt(Tags.XmlDataLen), ref pos); - else if (IsSetField(Tags.XmlDataLen)) - f = ExtractDataField(msgstr, GetInt(Tags.XmlDataLen), ref pos); - } - - f ??= ExtractField(msgstr, ref pos); + StringField f = fieldTag == Tags.XmlData + ? ExtractDataField(msgstr, Header.GetInt(Tags.XmlDataLen), ref pos) + : ExtractField(msgstr, ref pos); if (validate && (count < 3) && (Header.HEADER_FIELD_ORDER[count++] != f.Tag)) throw new InvalidMessage("Header fields out of order"); diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2c1b8acbb..4c12d3893 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -34,6 +34,7 @@ What's New * #841 - experimental support for CME Enhanced Resend (gbirchmeier) * #961 - new GenerateKeys app to create Example-app SSL certs (dckorben/gbirchmeier) * #1001 - AcceptanceTests bug: double.Parse with InvariantCulture (gbirchmeier) +* #562 - deprecate Message.IsHeaderField without transport DD param (gbirchmeier) ### v1.14.0 diff --git a/UnitTests/MessageTests.cs b/UnitTests/MessageTests.cs index e406fe9ec..0b3cfc20d 100644 --- a/UnitTests/MessageTests.cs +++ b/UnitTests/MessageTests.cs @@ -217,9 +217,18 @@ public void ConstructStringFieldOrder() [Test] public void IsHeaderFieldTest() { - Assert.That(Message.IsHeaderField(Tags.BeginString), Is.EqualTo(true)); - Assert.That(Message.IsHeaderField(Tags.TargetCompID), Is.EqualTo(true)); - Assert.That(Message.IsHeaderField(Tags.Account), Is.EqualTo(false)); + Assert.That(Message.IsHeaderField(Tags.BeginString, null), Is.EqualTo(true)); + Assert.That(Message.IsHeaderField(Tags.TargetCompID, null), Is.EqualTo(true)); + Assert.That(Message.IsHeaderField(Tags.Account, null), Is.EqualTo(false)); + + var dd = new QuickFix.DataDictionary.DataDictionary(); + dd.LoadFIXSpec("FIX44"); + dd.Header.AddField( + new QuickFix.DataDictionary.DDField(901109, "FakeField", new Dictionary(), "STRING")); + + Assert.That(Message.IsHeaderField(Tags.TargetCompID, dd), Is.EqualTo(true)); + Assert.That(Message.IsHeaderField(Tags.Account, dd), Is.EqualTo(false)); + Assert.That(Message.IsHeaderField(901109, dd), Is.EqualTo(true)); } [Test]