diff --git a/field_map.go b/field_map.go index 337aad5b7..1df5dbf84 100644 --- a/field_map.go +++ b/field_map.go @@ -8,7 +8,7 @@ import ( //FieldMap is a collection of fix fields that make up a fix message. type FieldMap struct { - tagLookup map[Tag][]TagValue + tagLookup map[Tag][]tagValue tagOrder } @@ -68,7 +68,7 @@ func trailerFieldOrder(i, j Tag) bool { } func (m *FieldMap) init(ordering tagOrder) { - m.tagLookup = make(map[Tag][]TagValue) + m.tagLookup = make(map[Tag][]tagValue) m.tagOrder = ordering } @@ -109,7 +109,7 @@ func (m FieldMap) GetGroup(parser *RepeatingGroup) MessageRejectError { return ConditionallyRequiredFieldMissing(parser.Tag) } - if _, err := parser.Read(tagValues); err != nil { + if _, err := parser.read(tagValues); err != nil { return incorrectDataFormatForValue(parser.Tag) } @@ -117,7 +117,7 @@ func (m FieldMap) GetGroup(parser *RepeatingGroup) MessageRejectError { } func (m FieldMap) SetField(tag Tag, field FieldValueWriter) FieldMap { - tValues := make([]TagValue, 1) + tValues := make([]tagValue, 1) tValues[0].init(tag, field.Write()) m.tagLookup[tag] = tValues return m @@ -125,18 +125,18 @@ func (m FieldMap) SetField(tag Tag, field FieldValueWriter) FieldMap { //Clear purges all fields from field map func (m *FieldMap) Clear() { - m.tagLookup = make(map[Tag][]TagValue) + m.tagLookup = make(map[Tag][]tagValue) } func (m FieldMap) Set(field FieldWriter) FieldMap { - tValues := make([]TagValue, 1) + tValues := make([]tagValue, 1) tValues[0].init(field.Tag(), field.Write()) m.tagLookup[field.Tag()] = tValues return m } func (m FieldMap) SetGroup(field RepeatingGroup) FieldMap { - m.tagLookup[field.Tag] = field.TagValues() + m.tagLookup[field.Tag] = field.tagValues() return m } diff --git a/message.go b/message.go index 68ac28b1b..7deedd8fa 100644 --- a/message.go +++ b/message.go @@ -22,9 +22,12 @@ type Message struct { bodyBytes []byte //field bytes as they appear in the raw message - fields []TagValue + fields []tagValue } +//Message marshals itself +func (m Message) Marshal() Message { return m } + //parseError is returned when bytes cannot be parsed as a FIX message. type parseError struct { OrigError string @@ -57,7 +60,7 @@ func parseMessage(rawMessage []byte) (Message, error) { fieldCount++ } } - msg.fields = make([]TagValue, fieldCount) + msg.fields = make([]tagValue, fieldCount) fieldIndex := 0 var err error @@ -177,7 +180,7 @@ func (m Message) reverseRoute() Message { return reverseMsg } -func extractSpecificField(field *TagValue, expectedTag Tag, buffer []byte) (remBuffer []byte, err error) { +func extractSpecificField(field *tagValue, expectedTag Tag, buffer []byte) (remBuffer []byte, err error) { remBuffer, err = extractField(field, buffer) switch { case err != nil: @@ -190,7 +193,7 @@ func extractSpecificField(field *TagValue, expectedTag Tag, buffer []byte) (remB return } -func extractField(parsedFieldBytes *TagValue, buffer []byte) (remBytes []byte, err error) { +func extractField(parsedFieldBytes *tagValue, buffer []byte) (remBytes []byte, err error) { endIndex := bytes.IndexByte(buffer, '\001') if endIndex == -1 { err = parseError{OrigError: "extractField: No Trailing Delim in " + string(buffer)} diff --git a/registry.go b/registry.go index 92fc16e34..765793b3a 100644 --- a/registry.go +++ b/registry.go @@ -4,8 +4,14 @@ import ( "fmt" ) -//Send determines the session to send msgBuilder using header fields BeginString, TargetCompID, SenderCompID -func Send(msg Message) (err error) { +//Marshaler Marshals self to quickfix.Message type +type Marshaler interface { + Marshal() Message +} + +//Send determines the session to send Marshaler using header fields BeginString, TargetCompID, SenderCompID +func Send(m Marshaler) (err error) { + msg := m.Marshal() var beginString FIXString if err := msg.Header.GetField(tagBeginString, &beginString); err != nil { return err @@ -27,7 +33,8 @@ func Send(msg Message) (err error) { return SendToTarget(msg, sessionID) } -func SendToTarget(msg Message, sessionID SessionID) error { +func SendToTarget(m Marshaler, sessionID SessionID) error { + msg := m.Marshal() session, err := LookupSession(sessionID) if err != nil { return err diff --git a/repeating_group.go b/repeating_group.go index dcdf09039..1dbdbb814 100644 --- a/repeating_group.go +++ b/repeating_group.go @@ -11,13 +11,13 @@ type GroupItem interface { //Tag returns the tag identifying this GroupItem Tag() Tag - //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 + //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 + //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(TagValues) (TagValues, error) + read(tagValues) (tagValues, error) } type protoGroupElement struct { @@ -25,7 +25,7 @@ type protoGroupElement struct { } func (t protoGroupElement) Tag() Tag { return t.tag } -func (t protoGroupElement) Read(tv TagValues) (TagValues, error) { +func (t protoGroupElement) read(tv tagValues) (tagValues, error) { if tv[0].Tag == t.tag { return tv[1:], nil } @@ -59,10 +59,10 @@ func (f *RepeatingGroup) Add() Group { return g } -//TagValues returns TagValues for all Items in the repeating group ordered by +//tagValues returns tagValues for all Items in the repeating group ordered by //Group sequence and Group template order -func (f RepeatingGroup) TagValues() TagValues { - tvs := make(TagValues, 1, 1) +func (f RepeatingGroup) tagValues() tagValues { + tvs := make(tagValues, 1, 1) tvs[0].init(f.Tag, []byte(strconv.Itoa(len(f.Groups)))) for _, group := range f.Groups { @@ -116,7 +116,7 @@ func (f RepeatingGroup) isDelimiter(t Tag) bool { return t == f.GroupTemplate[0].Tag() } -func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { +func (f *RepeatingGroup) read(tv tagValues) (tagValues, error) { expectedGroupSize, err := atoi(tv[0].Value) if err != nil { return tv, err @@ -137,7 +137,7 @@ func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { } tvRange := tv - if tv, err = field.Read(tv); err != nil { + if tv, err = field.read(tv); err != nil { return tv, err } diff --git a/repeating_group_test.go b/repeating_group_test.go index 2db129e9d..0053769e6 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -76,7 +76,7 @@ func TestRepeatingGroup_TagValues(t *testing.T) { for _, test := range tests { tvbytes := []byte{} - for _, tv := range test.f.TagValues() { + for _, tv := range test.f.tagValues() { tvbytes = append(tvbytes, tv.bytes...) } @@ -89,26 +89,26 @@ func TestRepeatingGroup_TagValues(t *testing.T) { func TestRepeatingGroup_ReadError(t *testing.T) { singleFieldTemplate := GroupTemplate{GroupElement(1)} tests := []struct { - tv TagValues + tv tagValues expectedGroupNum int }{ { - TagValues{ - TagValue{Value: []byte("1")}, - TagValue{Tag: Tag(2), Value: []byte("not in template")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValues{ + tagValue{Value: []byte("1")}, + tagValue{Tag: Tag(2), Value: []byte("not in template")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, }, 0}, { - TagValues{ - TagValue{Value: []byte("2")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(2), Value: []byte("not in template")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValues{ + tagValue{Value: []byte("2")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValue{Tag: Tag(2), Value: []byte("not in template")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, }, 1}} for _, s := range tests { f := RepeatingGroup{GroupTemplate: singleFieldTemplate} - _, err := f.Read(s.tv) + _, err := f.read(s.tv) if err == nil || len(f.Groups) != s.expectedGroupNum { t.Errorf("Should have raised an error because expected group number is wrong: %v instead of %v", len(f.Groups), s.expectedGroupNum) } @@ -122,55 +122,55 @@ func TestRepeatingGroup_Read(t *testing.T) { tests := []struct { groupTemplate GroupTemplate - tv TagValues - expectedGroupTvs []TagValues + tv tagValues + expectedGroupTvs []tagValues }{ - {singleFieldTemplate, TagValues{TagValue{Value: []byte("0")}}, - []TagValues{}}, - {singleFieldTemplate, TagValues{TagValue{Value: []byte("1")}, TagValue{Tag: Tag(1), Value: []byte("hello")}}, - []TagValues{TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + {singleFieldTemplate, tagValues{tagValue{Value: []byte("0")}}, + []tagValues{}}, + {singleFieldTemplate, tagValues{tagValue{Value: []byte("1")}, tagValue{Tag: Tag(1), Value: []byte("hello")}}, + []tagValues{tagValues{tagValue{Tag: Tag(1), Value: []byte("hello")}}}}, {singleFieldTemplate, - TagValues{TagValue{Value: []byte("1")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(2), Value: []byte("not in group")}}, - []TagValues{ - TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + tagValues{tagValue{Value: []byte("1")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValue{Tag: Tag(2), Value: []byte("not in group")}}, + []tagValues{ + tagValues{tagValue{Tag: Tag(1), Value: []byte("hello")}}}}, {singleFieldTemplate, - TagValues{TagValue{Value: []byte("2")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(1), Value: []byte("world")}}, - []TagValues{ - TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, - TagValues{TagValue{Tag: Tag(1), Value: []byte("world")}}, + tagValues{tagValue{Value: []byte("2")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValue{Tag: Tag(1), Value: []byte("world")}}, + []tagValues{ + tagValues{tagValue{Tag: Tag(1), Value: []byte("hello")}}, + tagValues{tagValue{Tag: Tag(1), Value: []byte("world")}}, }}, {multiFieldTemplate, - TagValues{ - TagValue{Value: []byte("2")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}, + tagValues{ + tagValue{Value: []byte("2")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}, }, - []TagValues{ - TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, - TagValues{TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}}, + []tagValues{ + tagValues{tagValue{Tag: Tag(1), Value: []byte("hello")}}, + tagValues{tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}}, }}, {multiFieldTemplate, - TagValues{ - TagValue{Value: []byte("3")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}, - TagValue{Tag: Tag(1), Value: []byte("another")}, + tagValues{ + tagValue{Value: []byte("3")}, + tagValue{Tag: Tag(1), Value: []byte("hello")}, + tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}, + tagValue{Tag: Tag(1), Value: []byte("another")}, }, - []TagValues{ - TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, - TagValues{TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}}, - TagValues{TagValue{Tag: Tag(1), Value: []byte("another")}}, + []tagValues{ + tagValues{tagValue{Tag: Tag(1), Value: []byte("hello")}}, + tagValues{tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}}, + tagValues{tagValue{Tag: Tag(1), Value: []byte("another")}}, }}, } for _, test := range tests { f := RepeatingGroup{GroupTemplate: test.groupTemplate} - _, err := f.Read(test.tv) + _, err := f.read(test.tv) if err != nil { t.Error(err) } diff --git a/tag_value.go b/tag_value.go index 01c045f80..d659282fa 100644 --- a/tag_value.go +++ b/tag_value.go @@ -6,17 +6,17 @@ import ( "strconv" ) -//TagValue is a low-level FIX field abstraction -type TagValue struct { +//tagValue is a low-level FIX field abstraction +type tagValue struct { Tag Value []byte bytes []byte } -//TagValues is a slice of TagValue -type TagValues []TagValue +//tagValues is a slice of tagValue +type tagValues []tagValue -func (tv *TagValue) init(tag Tag, value []byte) { +func (tv *tagValue) init(tag Tag, value []byte) { var buf bytes.Buffer buf.WriteString(strconv.Itoa(int(tag))) buf.WriteString("=") @@ -28,18 +28,18 @@ func (tv *TagValue) init(tag Tag, value []byte) { tv.Value = value } -func (tv *TagValue) parse(rawFieldBytes []byte) (err error) { +func (tv *tagValue) parse(rawFieldBytes []byte) (err error) { sepIndex := bytes.IndexByte(rawFieldBytes, '=') if sepIndex == -1 { - err = fmt.Errorf("TagValue.Parse: No '=' in '%s'", rawFieldBytes) + err = fmt.Errorf("tagValue.Parse: No '=' in '%s'", rawFieldBytes) return } parsedTag, err := atoi(rawFieldBytes[:sepIndex]) if err != nil { - err = fmt.Errorf("TagValue.Parse: %s", err.Error()) + err = fmt.Errorf("tagValue.Parse: %s", err.Error()) return } @@ -50,11 +50,11 @@ func (tv *TagValue) parse(rawFieldBytes []byte) (err error) { return } -func (tv TagValue) String() string { +func (tv tagValue) String() string { return string(tv.bytes) } -func (tv TagValue) total() int { +func (tv tagValue) total() int { total := 0 for _, b := range []byte(tv.bytes) { @@ -64,6 +64,6 @@ func (tv TagValue) total() int { return total } -func (tv TagValue) length() int { +func (tv tagValue) length() int { return len(tv.bytes) } diff --git a/tag_value_test.go b/tag_value_test.go index 8a2d1e338..ce2bac023 100644 --- a/tag_value_test.go +++ b/tag_value_test.go @@ -6,7 +6,7 @@ import ( ) func TestTagValue_init(t *testing.T) { - var tv TagValue + var tv tagValue tv.init(Tag(8), []byte("blahblah")) expectedData := []byte("8=blahblah") @@ -21,7 +21,7 @@ func TestTagValue_init(t *testing.T) { func TestTagValue_parse(t *testing.T) { stringField := "8=FIX.4.0" - tv := TagValue{} + tv := tagValue{} err := tv.parse([]byte(stringField)) if err != nil { @@ -43,7 +43,7 @@ func TestTagValue_parse(t *testing.T) { func TestTagValue_parseFail(t *testing.T) { stringField := "not_tag_equal_value" - tv := TagValue{} + tv := tagValue{} err := tv.parse([]byte(stringField)) @@ -60,7 +60,7 @@ func TestTagValue_parseFail(t *testing.T) { func TestTagValue_String(t *testing.T) { stringField := "8=FIX.4.0" - tv := TagValue{} + tv := tagValue{} tv.parse([]byte(stringField)) if tv.String() != "8=FIX.4.0" { @@ -70,7 +70,7 @@ func TestTagValue_String(t *testing.T) { func TestTagValue_length(t *testing.T) { stringField := "8=FIX.4.0" - tv := TagValue{} + tv := tagValue{} tv.parse([]byte(stringField)) if tv.length() != len(stringField) { @@ -80,7 +80,7 @@ func TestTagValue_length(t *testing.T) { func TestTagValue_total(t *testing.T) { stringField := "1=hello" - tv := TagValue{} + tv := tagValue{} tv.parse([]byte(stringField)) if tv.total() != 643 { t.Error("Total is the summation of the ascii byte values of the field string, got ", tv.total()) diff --git a/validation.go b/validation.go index 842aeadb6..a21e0561c 100644 --- a/validation.go +++ b/validation.go @@ -97,7 +97,7 @@ func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadiction return nil } -func validateWalkComponent(messageDef *datadictionary.MessageDef, fields []TagValue) ([]TagValue, MessageRejectError) { +func validateWalkComponent(messageDef *datadictionary.MessageDef, fields []tagValue) ([]tagValue, MessageRejectError) { var fieldDef *datadictionary.FieldDef var ok bool var err MessageRejectError @@ -123,7 +123,7 @@ func validateWalkComponent(messageDef *datadictionary.MessageDef, fields []TagVa return fields, nil } -func validateVisitField(fieldDef *datadictionary.FieldDef, fields []TagValue) ([]TagValue, MessageRejectError) { +func validateVisitField(fieldDef *datadictionary.FieldDef, fields []tagValue) ([]tagValue, MessageRejectError) { var err MessageRejectError if fieldDef.IsGroup() { @@ -135,7 +135,7 @@ func validateVisitField(fieldDef *datadictionary.FieldDef, fields []TagValue) ([ return fields[1:], nil } -func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []TagValue) ([]TagValue, MessageRejectError) { +func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []tagValue) ([]tagValue, MessageRejectError) { numInGroupTag := fieldStack[0].Tag var numInGroup FIXInt @@ -256,7 +256,7 @@ func validateFields(transportDD *datadictionary.DataDictionary, appDD *datadicti return nil } -func validateField(d *datadictionary.DataDictionary, validFields datadictionary.TagSet, field TagValue) MessageRejectError { +func validateField(d *datadictionary.DataDictionary, validFields datadictionary.TagSet, field tagValue) MessageRejectError { if len(field.Value) == 0 { return tagSpecifiedWithoutAValue(field.Tag) } diff --git a/validation_test.go b/validation_test.go index c3ff8b7c3..4fc035637 100644 --- a/validation_test.go +++ b/validation_test.go @@ -260,7 +260,7 @@ func (s *ValidationTests) TestValidateVisitField(c *C) { fieldType := &datadictionary.FieldType{Name: "myfield", Tag: 11, Type: "STRING"} fieldDef := &datadictionary.FieldDef{FieldType: fieldType} - TagValues := make([]TagValue, 1) + TagValues := make([]tagValue, 1) TagValues[0].init(Tag(11), []byte("value")) remFields, reject := validateVisitField(fieldDef, TagValues) c.Check(len(remFields), Equals, 0) @@ -277,61 +277,61 @@ func (s *ValidationTests) TestValidateVisitFieldGroup(c *C) { groupFieldType := &datadictionary.FieldType{Name: "mygroupfield", Tag: 1, Type: "INT"} groupFieldDef := &datadictionary.FieldDef{FieldType: groupFieldType, ChildFields: []*datadictionary.FieldDef{fieldDef1, fieldDef2}} - var repField1 TagValue - var repField2 TagValue + var repField1 tagValue + var repField2 tagValue repField1.init(Tag(2), []byte("a")) repField2.init(Tag(3), []byte("a")) //non-repeating - var groupID TagValue + var groupID tagValue groupID.init(Tag(1), []byte("1")) - fields := []TagValue{groupID, repField1} + fields := []tagValue{groupID, repField1} remFields, reject := validateVisitGroupField(groupFieldDef, fields) c.Check(len(remFields), Equals, 0) c.Check(reject, IsNil) - fields = []TagValue{groupID, repField1, repField2} + fields = []tagValue{groupID, repField1, repField2} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(len(remFields), Equals, 0) c.Check(reject, IsNil) //test with trailing tag not in group - var otherField TagValue + var otherField tagValue otherField.init(Tag(500), []byte("blah")) - fields = []TagValue{groupID, repField1, repField2, otherField} + fields = []tagValue{groupID, repField1, repField2, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(len(remFields), Equals, 1) c.Check(reject, IsNil) //repeats groupID.init(Tag(1), []byte("2")) - fields = []TagValue{groupID, repField1, repField2, repField1, repField2, otherField} + fields = []tagValue{groupID, repField1, repField2, repField1, repField2, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(len(remFields), Equals, 1) c.Check(reject, IsNil) groupID.init(Tag(1), []byte("3")) - fields = []TagValue{groupID, repField1, repField2, repField1, repField2, repField1, repField2, otherField} + fields = []tagValue{groupID, repField1, repField2, repField1, repField2, repField1, repField2, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(len(remFields), Equals, 1) c.Check(reject, IsNil) //REJECT: group size declared > actual group size groupID.init(Tag(1), []byte("3")) - fields = []TagValue{groupID, repField1, repField2, repField1, repField2, otherField} + fields = []tagValue{groupID, repField1, repField2, repField1, repField2, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(reject, NotNil) c.Check(reject.RejectReason(), Equals, rejectReasonIncorrectNumInGroupCountForRepeatingGroup) groupID.init(Tag(1), []byte("3")) - fields = []TagValue{groupID, repField1, repField1, otherField} + fields = []tagValue{groupID, repField1, repField1, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(reject, NotNil) c.Check(reject.RejectReason(), Equals, rejectReasonIncorrectNumInGroupCountForRepeatingGroup) //REJECT: group size declared < actual group size groupID.init(Tag(1), []byte("1")) - fields = []TagValue{groupID, repField1, repField2, repField1, repField2, otherField} + fields = []tagValue{groupID, repField1, repField2, repField1, repField2, otherField} remFields, reject = validateVisitGroupField(groupFieldDef, fields) c.Check(reject, NotNil) c.Check(reject.RejectReason(), Equals, rejectReasonIncorrectNumInGroupCountForRepeatingGroup)