// Copyright (c) quickfixengine.org All rights reserved. // // This file may be distributed under the terms of the quickfixengine.org // license as defined by quickfixengine.org and appearing in the file // LICENSE included in the packaging of this file. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING // THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. // // See http://www.quickfixengine.org/LICENSE for licensing information. // // Contact ask@quickfixengine.org if any conditions of this licensing // are not clear to you. package quickfix import ( "fmt" "math" "strconv" ) // GroupItem interface is used to construct repeating group templates. type GroupItem interface { // Tag returns the tag identifying this GroupItem. Tag() Tag // Read Parameter to Read is tagValues. For most fields, only the first tagValue will be required. // The length of the slice extends from the tagValue mapped to the field to be read through the // following fields. This can be useful for GroupItems made up of repeating groups. // // The Read function returns the remaining tagValues not processed by the GroupItem. If there was a // problem reading the field, an error may be returned. Read([]TagValue) ([]TagValue, error) // Clone makes a copy of this GroupItem. Clone() GroupItem } type protoGroupElement struct { tag Tag } func (t protoGroupElement) Tag() Tag { return t.tag } func (t protoGroupElement) Read(tv []TagValue) ([]TagValue, error) { if tv[0].tag == t.tag { return tv[1:], nil } return tv, nil } func (t protoGroupElement) Clone() GroupItem { return t } // GroupElement returns a GroupItem made up of a single field. func GroupElement(tag Tag) GroupItem { return protoGroupElement{tag: tag} } // GroupTemplate specifies the group item order for a RepeatingGroup. type GroupTemplate []GroupItem // Clone makes a copy of this GroupTemplate. func (gt GroupTemplate) Clone() GroupTemplate { clone := make(GroupTemplate, len(gt)) for i := range gt { clone[i] = gt[i].Clone() } return clone } // Group is a group of fields occurring in a repeating group. type Group struct{ FieldMap } // RepeatingGroup is a FIX Repeating Group type. type RepeatingGroup struct { tag Tag template GroupTemplate groups []*Group } // NewRepeatingGroup returns an initilized RepeatingGroup instance. func NewRepeatingGroup(tag Tag, template GroupTemplate) *RepeatingGroup { return &RepeatingGroup{ tag: tag, template: template, } } // Tag returns the Tag for this repeating Group. func (f RepeatingGroup) Tag() Tag { return f.tag } // Clone makes a copy of this RepeatingGroup (tag, template). func (f RepeatingGroup) Clone() GroupItem { return &RepeatingGroup{ tag: f.tag, template: f.template.Clone(), } } // Len returns the number of Groups in this RepeatingGroup. func (f RepeatingGroup) Len() int { return len(f.groups) } // Get returns the ith group in this RepeatingGroup. func (f RepeatingGroup) Get(i int) *Group { return f.groups[i] } // Add appends a new group to the RepeatingGroup and returns the new Group. func (f *RepeatingGroup) Add() *Group { g := new(Group) g.initWithOrdering(f.groupTagOrder()) f.groups = append(f.groups, g) return g } // Write returns tagValues for all Items in the repeating group ordered by // Group sequence and Group template order. func (f RepeatingGroup) Write() []TagValue { tvs := make([]TagValue, 1) tvs[0].init(f.tag, []byte(strconv.Itoa(len(f.groups)))) for _, group := range f.groups { tags := group.sortedTags() group.rwLock.RLock() for _, tag := range tags { if fields, ok := group.tagLookup[tag]; ok { tvs = append(tvs, fields...) } } group.rwLock.RUnlock() } return tvs } func (f RepeatingGroup) findItemInGroupTemplate(t Tag) (item GroupItem, ok bool) { for _, templateField := range f.template { if t == templateField.Tag() { ok = true item = templateField.Clone() break } } return } func (f RepeatingGroup) groupTagOrder() tagOrder { tagMap := make(map[Tag]int) for i, f := range f.template { tagMap[f.Tag()] = i } return func(i, j Tag) bool { orderi := math.MaxInt32 orderj := math.MaxInt32 if iIndex, ok := tagMap[i]; ok { orderi = iIndex } if jIndex, ok := tagMap[j]; ok { orderj = jIndex } return orderi < orderj } } func (f RepeatingGroup) delimiter() Tag { return f.template[0].Tag() } func (f RepeatingGroup) isDelimiter(t Tag) bool { return t == f.delimiter() } func (f *RepeatingGroup) Read(tv []TagValue) ([]TagValue, error) { expectedGroupSize, err := atoi(tv[0].value) if err != nil { return tv, err } if expectedGroupSize == 0 { return tv[1:], nil } tv = tv[1:cap(tv)] tagOrdering := f.groupTagOrder() group := new(Group) group.initWithOrdering(tagOrdering) for len(tv) > 0 { gi, ok := f.findItemInGroupTemplate(tv[0].tag) if !ok { break } tvRange := tv if tv, err = gi.Read(tv); err != nil { return tv, err } if f.isDelimiter(gi.Tag()) { group = new(Group) group.initWithOrdering(tagOrdering) f.groups = append(f.groups, group) } group.rwLock.Lock() group.tagLookup[tvRange[0].tag] = tvRange group.tags = append(group.tags, gi.Tag()) group.rwLock.Unlock() } if len(f.groups) != expectedGroupSize { return tv, repeatingGroupFieldsOutOfOrder(f.tag, fmt.Sprintf("group %v: template is wrong or delimiter %v not found: expected %v groups, but found %v", f.tag, f.delimiter(), expectedGroupSize, len(f.groups))) } return tv, err }