adding quickfix library
This commit is contained in:
333
quickfix/datadictionary/datadictionary.go
Normal file
333
quickfix/datadictionary/datadictionary.go
Normal file
@ -0,0 +1,333 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user