jspurefix
Version:
pure node js fix engine
292 lines (261 loc) • 9.54 kB
text/typescript
import { ILooseObject } from '../../../collections/collection'
import { SimpleFieldDefinition, GroupFieldDefinition, ComponentFieldDefinition, MessageDefinition, FixDefinitions } from '../../definition'
import { Dictionary } from '../../../collections'
import { ContainedFieldSet, ContainedComponentField, ContainedGroupField, ContainedSimpleField } from '../../contained'
import { ContainedSetType } from '../../dict-primitive'
import { FixDefinitionSource, FixVersion } from '../../fix-versions'
import { GetJsFixLogger, IJsFixLogger } from '../../../config'
export interface IRepositoryField extends ILooseObject {
Tag: string
Name: string
Type: string
Description: string
AbbrName: string
NotReqXML: string
BaseCategory: string
BaseCategoryAbbrName: string
}
export interface IRepositoryEnum extends ILooseObject {
Tag: string
Value: string
SymbolicName: string
Description: string
}
export interface IRepositoryComponent extends ILooseObject {
ComponentID: string
ComponentType: string
CategoryID: string
Name: string
AbbrName: string
NotReqXML: string
Description: string
}
export interface IRepositoryMessage extends ILooseObject {
ComponentID: string
MsgType: string
Name: string
CategoryID: string
SectionID: string
AbbrName: string
NotReqXML: string
Description: string
}
export interface IRepositoryMsgContent extends ILooseObject {
ComponentID: string
TagText: string
Indent: string
Position: string
Reqd: string
Description: string
}
export interface IRepositoryAbbreviation extends ILooseObject {
Term: string
AbbrTerm: string
}
export class Repository {
public Enums: IRepositoryEnum[]
public Fields: IRepositoryField[]
public Components: IRepositoryComponent[]
public Messages: IRepositoryMessage[]
public MsgContents: IRepositoryMsgContent[]
public Abbreviations: IRepositoryAbbreviation[]
public includesAbbreviations: boolean
// derived from above
public readonly definitions: FixDefinitions
private readonly groupLookup: Dictionary<GroupFieldDefinition> = new Dictionary<GroupFieldDefinition>()
private contentLookup: Dictionary<IRepositoryMsgContent[]>
private componentLookup: Dictionary<IRepositoryComponent>
private readonly logger: IJsFixLogger
constructor (public readonly version: FixVersion, public readonly getLogger: GetJsFixLogger) {
this.logger = getLogger('Repository')
switch (version) {
case FixVersion.FIX44:
case FixVersion.FIX50:
case FixVersion.FIX50SP1:
case FixVersion.FIX50SP2:
this.includesAbbreviations = true
break
default: {
this.includesAbbreviations = false
break
}
}
this.definitions = new FixDefinitions(FixDefinitionSource.FixRepo, version)
}
public assign (name: string, data: ILooseObject[]): void {
switch (name) {
case 'Fields': {
this.Fields = data as IRepositoryField[]
break
}
case 'Enums': {
this.Enums = data as IRepositoryEnum[]
break
}
case 'Components': {
this.Components = data as IRepositoryComponent[]
break
}
case 'Messages': {
this.Messages = data as IRepositoryMessage[]
break
}
case 'MsgContents': {
this.MsgContents = data as IRepositoryMsgContent[]
if (!this.includesAbbreviations) {
this.toDefinitions()
}
break
}
case 'Abbreviations': {
this.Abbreviations = data as IRepositoryAbbreviation[]
this.toDefinitions()
break
}
}
}
private summarise (): void {
const logger = this.logger
logger.info(`definitions: ${this.definitions.simple.count()} fields`)
logger.info(`definitions: ${this.definitions.component.count()} components`)
logger.info(`definitions: ${this.definitions.message.count()} messages`)
}
private toDefinitions (): void {
this.fields()
this.complex()
this.header()
this.trailer()
this.summarise()
}
private complex (): void {
this.contentLookup = this.contents()
this.componentLookup = this.components()
this.Components.forEach((c: IRepositoryComponent) => this.resolve(c))
this.Messages.forEach((m: IRepositoryMessage) => {
const msg = this.message(m)
this.logger.debug(`${msg.toString()}`)
this.definitions.addMessage(msg)
})
}
private header (): void {
const h: ComponentFieldDefinition = this.definitions.component.get('StandardHeader')
this.definitions.component.add('header', h)
}
private trailer (): void {
const t: ComponentFieldDefinition = this.definitions.component.get('StandardTrailer')
this.definitions.component.add('trailer', t)
}
private fields (): void {
const definitions = this.definitions
this.Fields.forEach((f: IRepositoryField) => {
definitions.addSimpleFieldDef(new SimpleFieldDefinition(
f.Tag,
f.Name,
f.AbbrName ? f.AbbrName : f.Name,
f.BaseCategory,
f.BaseCategoryAbbrName,
f.Type.toUpperCase(),
f.Description))
})
for (const e of this.Enums) {
const field: SimpleFieldDefinition =
definitions.tagToSimple[parseInt(e.Tag, 10)]
if (field == null) {
continue
}
if (e.Value && e.SymbolicName) {
field.addEnum(e.Value, e.SymbolicName, e.Description)
}
}
}
private contents (): Dictionary<IRepositoryMsgContent[]> {
return this.MsgContents.reduce((a, current) => {
let content: IRepositoryMsgContent[] = a.get(current.ComponentID)
if (!content) {
content = []
a.add(current.ComponentID, content)
}
content[content.length] = current
return a
}, new Dictionary<IRepositoryMsgContent[]>())
}
private resolveToFieldSet (content: IRepositoryMsgContent[], parentSet: ContainedFieldSet): void {
content.forEach((current: IRepositoryMsgContent) => {
const required: boolean = current.Reqd === '1'
const tag: number = parseInt(current.TagText, 10)
if (!isNaN(tag)) {
const sf: SimpleFieldDefinition = this.definitions.tagToSimple[tag]
if (sf) {
parentSet.add(new ContainedSimpleField(sf, parentSet.fields.length, required, false))
}
} else {
// is there a definition for this type yet create.
let childSet: ContainedFieldSet = this.definitions.component.get(current.TagText)
if (!childSet) {
const cl: IRepositoryComponent = this.componentLookup.get(current.TagText)
if (cl) {
childSet = this.resolve(cl)
}
}
switch (childSet.type) {
case ContainedSetType.Component: {
parentSet.add(new ContainedComponentField(childSet, parentSet.fields.length, required))
break
}
case ContainedSetType.Group: {
parentSet.add(new ContainedGroupField(childSet as GroupFieldDefinition, parentSet.fields.length, required))
break
}
default: {
throw new Error(`unknown set type ${childSet.type}`)
}
}
}
return parentSet
})
}
private resolve (c: IRepositoryComponent): ContainedFieldSet {
switch (c.ComponentType) {
case 'ImplicitBlockRepeating':
case 'BlockRepeating': {
const content: IRepositoryMsgContent[] = this.contentLookup.get(c.ComponentID)
const noField: SimpleFieldDefinition = this.definitions.tagToSimple[parseInt(content[0].TagText, 10)]
let def: GroupFieldDefinition = this.groupLookup.get(c.ComponentID)
if (!def) {
def = new GroupFieldDefinition(c.Name, c.AbbrName, c.CategoryID, noField, c.Description)
this.resolveToFieldSet(content.slice(1), def)
this.groupLookup.add(c.ComponentID, def)
}
return def
}
default: {
const content: IRepositoryMsgContent[] = this.contentLookup.get(c.ComponentID)
let def: ComponentFieldDefinition = this.definitions.component.get(c.Name)
if (!def) {
def = new ComponentFieldDefinition(c.Name, c.AbbrName, c.CategoryID, c.Description)
this.resolveToFieldSet(content, def)
this.definitions.addComponentFieldDef(def)
}
return def
}
}
}
private message (m: IRepositoryMessage): MessageDefinition {
const definitions = this.definitions
const content: IRepositoryMsgContent[] = this.contentLookup.get(m.ComponentID)
let def: MessageDefinition = definitions.message.get(m.Name)
if (!def) {
def = new MessageDefinition(m.Name, m.AbbrName, m.MsgType, m.CategoryID, m.Description)
this.resolveToFieldSet(content, def)
definitions.addComponentFieldDef(def)
}
return def
}
private components (): Dictionary<IRepositoryComponent> {
return this.Components.reduce((a: Dictionary<IRepositoryComponent>, current: IRepositoryComponent) => {
a.add(current.Name, current)
a.add(current.ComponentID, current)
return a
}, new Dictionary<IRepositoryComponent>())
}
}