FixAntenna/NetCore/FixEngine/MessageStructure.cs (194 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.Linq;
namespace Epam.FixAntenna.NetCore.FixEngine
{
public sealed class MessageStructure
{
/// <summary>
/// Special value for defining the size of field with variable value length
/// </summary>
public const int VariableLength = -1;
public List<int> Lengths { get; } = new List<int>();
public List<int> TagIds { get; } = new List<int>();
public List<ValueType> Types { get; } = new List<ValueType>();
/// <summary>
/// Specifies tagId and size of the tag value. All tags should be reserved in order like in generated message.
/// Use <c>MessageStructure.VariableLength</c> to define the tag value that could be increased dynamically for concrete parameter.
/// Keep in mind, this will slow performance and spawn new Objects in runtime.
/// Reserved field will be added to the end of the structure.
/// </summary>
/// <param name="tagId"> tagId </param>
/// <param name="length"> required amount of bytes for value or <c>MessageStructure.VariableLength</c> </param>
public void Reserve(int tagId, int length)
{
Reserve(tagId, length, ValueType.ByteArray);
}
public void ReserveString(int tagId)
{
ReserveString(tagId, VariableLength);
}
public void ReserveString(int tagId, int length)
{
Reserve(tagId, length, ValueType.String);
}
public void ReserveLong(int tagId, int length)
{
Reserve(tagId, length, ValueType.Long);
}
public void Reserve(int tagId, int length, ValueType type)
{
CheckLength(length);
TagIds.Add(tagId);
Lengths.Add(length);
Types.Add(type);
}
/// <summary>
/// Specifies tagId and size of the tag value. Reserved field will be inserted at specified position.
/// </summary>
/// <param name="position"> position to insent the tag </param>
/// <param name="tagId"> tagId </param>
/// <param name="length"> required amount of bytes for value or <see cref="VariableLength"/> </param>
/// <seealso cref="Reserve(int,int,int,ValueType)"> </seealso>
public void Reserve(int position, int tagId, int length)
{
Reserve(position, tagId, length, ValueType.ByteArray);
}
public void ReserveString(int position, int tagId, int length)
{
Reserve(position, tagId, length, ValueType.String);
}
public void ReserveLong(int position, int tagId, int length)
{
Reserve(position, tagId, length, ValueType.Long);
}
public void Reserve(int position, int tagId, int length, ValueType type)
{
CheckLength(length);
TagIds.Insert(position, tagId);
Lengths.Insert(position, length);
Types.Insert(position, type);
}
private void CheckLength(int length)
{
if (length <= 0 && length != VariableLength)
{
throw new ArgumentException(
"Invalid length for reserved field. Should be VARIABLE_LENGTH or positive integer.");
}
}
public int GetTagId(int index)
{
return TagIds[index];
}
public int GetLength(int index)
{
return Lengths[index];
}
public ValueType GetType(int index)
{
return Types[index];
}
public int Size => TagIds.Count;
public bool ContainsTagId(int tag)
{
return TagIds.Contains(tag);
}
public int IndexOfTag(int tag)
{
return IndexOf(tag);
}
/// <summary>
/// Append fields to the end of current structure.
/// </summary>
/// <param name="struct"> </param>
public void Append(MessageStructure @struct)
{
TagIds.AddRange(@struct.TagIds);
Lengths.AddRange(@struct.Lengths);
Types.AddRange(@struct.Types);
}
public void Merge(MessageStructure @struct)
{
var size = @struct.Size;
var origTagSize = TagIds.Count;
for (var i = 0; i < size; i++)
{
var id = @struct.GetTagId(i);
var pos = IndexOf(id);
if (pos < 0 || pos >= origTagSize)
{
TagIds.Add(id);
Lengths.Add(@struct.GetLength(i));
Types.Add(@struct.GetType(i));
}
else
{
SetLengthAtIndex(pos, @struct.GetLength(i));
SetTypeAtIndex(pos, @struct.GetType(i));
}
}
}
public int IndexOf(int tagId)
{
return TagIds.IndexOf(tagId);
}
/// <summary>
/// Change the length for specific tag.
/// </summary>
/// <param name="tagId"> </param>
/// <param name="length"> required amount of bytes for value or <see cref="VariableLength"/> </param>
public void SetLength(int tagId, int length)
{
SetLength(tagId, 1, length);
}
/// <summary>
/// Change the length for specific tag.
/// </summary>
/// <param name="tagId"> </param>
/// <param name="occurance"> </param>
/// <param name="length"> required amount of bytes for value or <see cref="VariableLength"/> </param>
public void SetLength(int tagId, int occurance, int length)
{
CheckLength(length);
var tagIndex = 0;
for (var i = occurance; i > 0; i--)
{
tagIndex = TagIds.Skip(tagIndex).Take(TagIds.Count).ToList().IndexOf(tagId);
if (tagIndex < 0)
{
throw new ArgumentException("There is no reserved field for tag " + tagId);
}
}
SetLengthAtIndex(tagIndex, length);
}
public void SetLengthAtIndex(int tagIndex, int length)
{
Lengths[tagIndex] = length;
}
public void SetType(int tagId, ValueType type)
{
SetType(tagId, 1, type);
}
public void SetType(int tagId, int occurance, ValueType type)
{
var tagIndex = 0;
for (var i = occurance; i > 0; i--)
{
tagIndex = TagIds.Skip(tagIndex).Take(TagIds.Count).ToList().IndexOf(tagId);
if (tagIndex < 0)
{
throw new ArgumentException("There is no reserved field for tag " + tagId);
}
}
SetTypeAtIndex(tagIndex, type);
}
public void SetTypeAtIndex(int tagIndex, ValueType type)
{
Types[tagIndex] = type;
}
public override bool Equals(object o)
{
if (this == o)
{
return true;
}
if (o == null || GetType() != o.GetType())
{
return false;
}
var that = (MessageStructure) o;
if (!Lengths.SequenceEqual(that.Lengths))
{
return false;
}
if (!TagIds.SequenceEqual(that.TagIds))
{
return false;
}
if (!Types.SequenceEqual(that.Types))
{
return false;
}
return true;
}
public override int GetHashCode()
{
var result = TagIds.GetHashCode();
foreach (var value in Lengths)
{
result = 31 * result + value.GetHashCode();
}
result = 31 * result + Types.GetHashCode();
return result;
}
}
}