334 lines
8.2 KiB
Go
334 lines
8.2 KiB
Go
// Package datadictionary provides support for parsing and organizing FIX Data Dictionaries
|
|
package datadictionary
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// DataDictionary models FIX messages, components, and fields.
|
|
type DataDictionary struct {
|
|
FIXType string
|
|
Major int
|
|
Minor int
|
|
ServicePack int
|
|
FieldTypeByTag map[int]*FieldType
|
|
FieldTypeByName map[string]*FieldType
|
|
Messages map[string]*MessageDef
|
|
ComponentTypes map[string]*ComponentType
|
|
Header *MessageDef
|
|
Trailer *MessageDef
|
|
}
|
|
|
|
// MessagePart can represent a Field, Repeating Group, or Component.
|
|
type MessagePart interface {
|
|
Name() string
|
|
Required() bool
|
|
}
|
|
|
|
// messagePartWithFields is a MessagePart with multiple Fields.
|
|
type messagePartWithFields interface {
|
|
MessagePart
|
|
Fields() []*FieldDef
|
|
RequiredFields() []*FieldDef
|
|
}
|
|
|
|
// ComponentType is a grouping of fields.
|
|
type ComponentType struct {
|
|
name string
|
|
parts []MessagePart
|
|
fields []*FieldDef
|
|
requiredFields []*FieldDef
|
|
requiredParts []MessagePart
|
|
}
|
|
|
|
// NewComponentType returns an initialized component type.
|
|
func NewComponentType(name string, parts []MessagePart) *ComponentType {
|
|
comp := ComponentType{
|
|
name: name,
|
|
parts: parts,
|
|
}
|
|
|
|
for _, part := range parts {
|
|
|
|
if part.Required() {
|
|
comp.requiredParts = append(comp.requiredParts, part)
|
|
}
|
|
|
|
switch f := part.(type) {
|
|
case messagePartWithFields:
|
|
comp.fields = append(comp.fields, f.Fields()...)
|
|
|
|
if f.Required() {
|
|
comp.requiredFields = append(comp.requiredFields, f.RequiredFields()...)
|
|
}
|
|
case *FieldDef:
|
|
comp.fields = append(comp.fields, f)
|
|
|
|
if f.Required() {
|
|
comp.requiredFields = append(comp.requiredFields, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &comp
|
|
}
|
|
|
|
// Name returns the name of this component type.
|
|
func (c ComponentType) Name() string { return c.name }
|
|
|
|
// Fields returns all fields contained in this component. Includes fields
|
|
// encapsulated in components of this component.
|
|
func (c ComponentType) Fields() []*FieldDef { return c.fields }
|
|
|
|
// RequiredFields returns those fields that are required for this component.
|
|
func (c ComponentType) RequiredFields() []*FieldDef { return c.requiredFields }
|
|
|
|
// RequiredParts returns those parts that are required for this component.
|
|
func (c ComponentType) RequiredParts() []MessagePart { return c.requiredParts }
|
|
|
|
// Parts returns all parts in declaration order contained in this component.
|
|
func (c ComponentType) Parts() []MessagePart { return c.parts }
|
|
|
|
// TagSet is set for tags.
|
|
type TagSet map[int]struct{}
|
|
|
|
// Add adds a tag to the tagset.
|
|
func (t TagSet) Add(tag int) {
|
|
t[tag] = struct{}{}
|
|
}
|
|
|
|
// Component is a Component as it appears in a given MessageDef.
|
|
type Component struct {
|
|
*ComponentType
|
|
required bool
|
|
}
|
|
|
|
// NewComponent returns an initialized Component instance.
|
|
func NewComponent(ct *ComponentType, required bool) *Component {
|
|
return &Component{
|
|
ComponentType: ct,
|
|
required: required,
|
|
}
|
|
}
|
|
|
|
// Required returns true if this component is required for the containing
|
|
// MessageDef.
|
|
func (c Component) Required() bool { return c.required }
|
|
|
|
// Field models a field or repeating group in a message.
|
|
type Field interface {
|
|
Tag() int
|
|
}
|
|
|
|
// FieldDef models a field belonging to a message.
|
|
type FieldDef struct {
|
|
*FieldType
|
|
required bool
|
|
|
|
Parts []MessagePart
|
|
Fields []*FieldDef
|
|
requiredParts []MessagePart
|
|
requiredFields []*FieldDef
|
|
}
|
|
|
|
// NewFieldDef returns an initialized FieldDef.
|
|
func NewFieldDef(fieldType *FieldType, required bool) *FieldDef {
|
|
return &FieldDef{
|
|
FieldType: fieldType,
|
|
required: required,
|
|
}
|
|
}
|
|
|
|
// NewGroupFieldDef returns an initialized FieldDef for a repeating group.
|
|
func NewGroupFieldDef(fieldType *FieldType, required bool, parts []MessagePart) *FieldDef {
|
|
field := FieldDef{
|
|
FieldType: fieldType,
|
|
required: required,
|
|
Parts: parts,
|
|
}
|
|
|
|
for _, part := range parts {
|
|
if part.Required() {
|
|
field.requiredParts = append(field.requiredParts, part)
|
|
}
|
|
|
|
if comp, ok := part.(Component); ok {
|
|
field.Fields = append(field.Fields, comp.Fields()...)
|
|
|
|
if comp.required {
|
|
field.requiredFields = append(field.requiredFields, comp.requiredFields...)
|
|
}
|
|
} else {
|
|
if child, ok := part.(*FieldDef); ok {
|
|
field.Fields = append(field.Fields, child)
|
|
|
|
if child.required {
|
|
field.requiredFields = append(field.requiredFields, child)
|
|
}
|
|
} else {
|
|
panic("unknown part")
|
|
}
|
|
}
|
|
}
|
|
|
|
return &field
|
|
}
|
|
|
|
// Required returns true if this FieldDef is required for the containing
|
|
// MessageDef.
|
|
func (f FieldDef) Required() bool { return f.required }
|
|
|
|
// IsGroup is true if the field is a repeating group.
|
|
func (f FieldDef) IsGroup() bool {
|
|
return len(f.Fields) > 0
|
|
}
|
|
|
|
// RequiredParts returns those parts that are required for this FieldDef. IsGroup
|
|
// must return true.
|
|
func (f FieldDef) RequiredParts() []MessagePart { return f.requiredParts }
|
|
|
|
// RequiredFields returns those fields that are required for this FieldDef. IsGroup
|
|
// must return true.
|
|
func (f FieldDef) RequiredFields() []*FieldDef { return f.requiredFields }
|
|
|
|
func (f FieldDef) childTags() []int {
|
|
tags := make([]int, 0, len(f.Fields))
|
|
|
|
for _, f := range f.Fields {
|
|
tags = append(tags, f.Tag())
|
|
tags = append(tags, f.childTags()...)
|
|
}
|
|
|
|
return tags
|
|
}
|
|
|
|
// FieldType holds information relating to a field. Includes Tag, type, and enums, if defined.
|
|
type FieldType struct {
|
|
name string
|
|
tag int
|
|
Type string
|
|
Enums map[string]Enum
|
|
}
|
|
|
|
// NewFieldType returns a pointer to an initialized FieldType.
|
|
func NewFieldType(name string, tag int, fixType string) *FieldType {
|
|
return &FieldType{
|
|
name: name,
|
|
tag: tag,
|
|
Type: fixType,
|
|
}
|
|
}
|
|
|
|
// Name returns the name for this FieldType.
|
|
func (f FieldType) Name() string { return f.name }
|
|
|
|
// Tag returns the tag for this fieldType.
|
|
func (f FieldType) Tag() int { return f.tag }
|
|
|
|
// Enum is a container for value and description.
|
|
type Enum struct {
|
|
Value string
|
|
Description string
|
|
}
|
|
|
|
// MessageDef can apply to header, trailer, or body of a FIX Message.
|
|
type MessageDef struct {
|
|
Name string
|
|
MsgType string
|
|
Fields map[int]*FieldDef
|
|
// Parts are the MessageParts of contained in this MessageDef in declaration order.
|
|
Parts []MessagePart
|
|
requiredParts []MessagePart
|
|
|
|
RequiredTags TagSet
|
|
Tags TagSet
|
|
}
|
|
|
|
// RequiredParts returns those parts that are required for this Message.
|
|
func (m MessageDef) RequiredParts() []MessagePart { return m.requiredParts }
|
|
|
|
// NewMessageDef returns a pointer to an initialized MessageDef.
|
|
func NewMessageDef(name, msgType string, parts []MessagePart) *MessageDef {
|
|
msg := MessageDef{
|
|
Name: name,
|
|
MsgType: msgType,
|
|
Fields: make(map[int]*FieldDef),
|
|
RequiredTags: make(TagSet),
|
|
Tags: make(TagSet),
|
|
Parts: parts,
|
|
}
|
|
|
|
processField := func(field *FieldDef, allowRequired bool) {
|
|
msg.Fields[field.Tag()] = field
|
|
msg.Tags.Add(field.Tag())
|
|
for _, t := range field.childTags() {
|
|
msg.Tags.Add(t)
|
|
}
|
|
|
|
if allowRequired && field.Required() {
|
|
msg.RequiredTags.Add(field.Tag())
|
|
}
|
|
}
|
|
|
|
for _, part := range parts {
|
|
if part.Required() {
|
|
msg.requiredParts = append(msg.requiredParts, part)
|
|
}
|
|
|
|
switch pType := part.(type) {
|
|
case messagePartWithFields:
|
|
for _, f := range pType.Fields() {
|
|
// Field if required in component is required in message only if
|
|
// component is required.
|
|
processField(f, pType.Required())
|
|
}
|
|
|
|
case *FieldDef:
|
|
processField(pType, true)
|
|
|
|
default:
|
|
panic("Unknown Part")
|
|
}
|
|
}
|
|
|
|
return &msg
|
|
}
|
|
|
|
// Parse loads and build a datadictionary instance from an xml file.
|
|
func Parse(path string) (*DataDictionary, error) {
|
|
var xmlFile *os.File
|
|
var err error
|
|
xmlFile, err = os.Open(path)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "problem opening file: %v", path)
|
|
}
|
|
defer xmlFile.Close()
|
|
|
|
return ParseSrc(xmlFile)
|
|
}
|
|
|
|
// ParseSrc loads and build a datadictionary instance from an xml source.
|
|
func ParseSrc(xmlSrc io.Reader) (*DataDictionary, error) {
|
|
doc := new(XMLDoc)
|
|
decoder := xml.NewDecoder(xmlSrc)
|
|
decoder.CharsetReader = func(_ string, input io.Reader) (io.Reader, error) {
|
|
return input, nil
|
|
}
|
|
|
|
if err := decoder.Decode(doc); err != nil {
|
|
return nil, errors.Wrapf(err, "problem parsing XML file")
|
|
}
|
|
|
|
b := new(builder)
|
|
dict, err := b.build(doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return dict, nil
|
|
}
|