UNPKG

jspurefix

Version:
315 lines (283 loc) 10.8 kB
import { ILooseObject } from '../../../collections/collection' import { SimpleFieldDefinition, GroupFieldDefinition, ComponentFieldDefinition, MessageDefinition, FixDefinitions } from '../../definition' import { IContainedSet, ContainedComponentField, ContainedGroupField, ContainedSimpleField, ContainedSetBuilder } from '../../contained' import { FixVersion } from '../../fix-versions' import { GetJsFixLogger, IJsFixLogger } from '../../../config' import { FixDefinitionSource } from '../../fix-definition-source' import { ContainedSetType } from '../../contained-set-type' import { IRepositoryMessage } from './repository-message' import { IRepositoryEnum } from './repository-enum' import { IRepositoryDataType } from './repository-data-type' import { IRepositoryComponent } from './repository-component' import { IRepositoryField } from './repository-field' import { IRepositoryAbbreviation } from './repository-abbreviation' import { IRepositoryMsgContent } from './repository-msg-content' export class Repository { public Enums: IRepositoryEnum[] public Fields: IRepositoryField[] public DataTypes: IRepositoryDataType[] 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: Map<string, GroupFieldDefinition> = new Map<string, GroupFieldDefinition>() private contentLookup: Map<string, IRepositoryMsgContent[]> private componentLookup: Map<string, IRepositoryComponent> private dataTypeLookup: Map<string, IRepositoryDataType> private readonly logger: IJsFixLogger constructor (public readonly version: FixVersion, public readonly getLogger: GetJsFixLogger) { this.logger = getLogger('Repository') this.includesAbbreviations = Repository.doesIncludeAbbreviations(version) this.definitions = new FixDefinitions(FixDefinitionSource.FixRepo, version) } private static doesIncludeAbbreviations (version: FixVersion): boolean { let includesAbbreviations: boolean switch (version) { case FixVersion.FIX44: case FixVersion.FIX50: case FixVersion.FIX50SP1: case FixVersion.FIX50SP2: includesAbbreviations = true break default: { includesAbbreviations = false break } } return includesAbbreviations } 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 'Datatypes': { this.DataTypes = data as IRepositoryDataType[] 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 const definitions = this.definitions logger.info(`definitions: ${definitions.simple.size} fields`) logger.info(`definitions: ${definitions.component.size} components`) logger.info(`definitions: ${definitions.message.size} 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 | undefined = this.definitions.component.get('StandardHeader') if (h) { this.definitions.component.set('header', h) } } private trailer (): void { const t: ComponentFieldDefinition | undefined = this.definitions.component.get('StandardTrailer') if (t) { this.definitions.component.set('trailer', t) } } private static isNative (f: IRepositoryField): boolean { return f.Type === 'Boolean' || f.Type === 'data' || f.Type === 'LocalMktDate' || f.Type === 'UTCDateOnly' || f.Type === 'UTCTimestamp' } private static makeSimple (f: IRepositoryField, type: string): SimpleFieldDefinition { return new SimpleFieldDefinition( f.Tag, f.Name, f.AbbrName ? f.AbbrName : f.Name, f.BaseCategory, f.BaseCategoryAbbrName, type.toUpperCase(), f.Description) } private getType (f: IRepositoryField): string { const types = this.dataTypeLookup const nativeType = Repository.isNative(f) let type = f.Type const mapped = !nativeType && types.get(type) if (f.Type !== 'Length' && mapped) { type = mapped.BaseType ?? type } return type } private fieldEnums (): void { const definitions = this.definitions 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 fields (): void { this.dataTypeLookup = this.types() const definitions = this.definitions const types = this.dataTypeLookup types.delete('boolean') types.delete('data') this.Fields.forEach((f: IRepositoryField) => { const type = this.getType(f) definitions.addSimpleFieldDef(Repository.makeSimple(f, type)) }) this.fieldEnums() } private contents (): Map<string, IRepositoryMsgContent[]> { return this.MsgContents.reduce((a, current) => { let content: IRepositoryMsgContent[] | undefined = a.get(current.ComponentID) if (!content) { content = [] a.set(current.ComponentID, content) } content[content.length] = current return a }, new Map<string, IRepositoryMsgContent[]>()) } private resolveToFieldSet (content: IRepositoryMsgContent[], parentSet: IContainedSet): void { const builder = new ContainedSetBuilder(parentSet) 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) { builder.add(new ContainedSimpleField(sf, parentSet.fields.length, required, false)) } } else { // is there a definition for this type yet create. let childSet: IContainedSet | undefined = this.definitions.component.get(current.TagText) if (!childSet) { const cl: IRepositoryComponent | undefined = this.componentLookup.get(current.TagText) if (cl) { childSet = this.resolve(cl) } } if (childSet) { switch (childSet.type) { case ContainedSetType.Component: { builder.add(new ContainedComponentField(childSet as ComponentFieldDefinition, parentSet.fields.length, required)) break } case ContainedSetType.Group: { builder.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): IContainedSet { switch (c.ComponentType) { case 'ImplicitBlockRepeating': case 'BlockRepeating': { const content: IRepositoryMsgContent[] | undefined = this.contentLookup.get(c.ComponentID) ?? [] const noField: SimpleFieldDefinition = this.definitions.tagToSimple[parseInt(content[0].TagText, 10)] let def: GroupFieldDefinition | undefined = 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.set(c.ComponentID, def) } return def } default: { const content: IRepositoryMsgContent[] | null = this.contentLookup.get(c.ComponentID) ?? [] let def: ComponentFieldDefinition | undefined = 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 | undefined = 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 (): Map<string, IRepositoryComponent> { return this.Components.reduce((a: Map<string, IRepositoryComponent>, current: IRepositoryComponent) => { a.set(current.Name, current) a.set(current.ComponentID, current) return a }, new Map<string, IRepositoryComponent>()) } private types (): Map<string, IRepositoryDataType> { return this.DataTypes.reduce((a: Map<string, IRepositoryDataType>, current: IRepositoryDataType) => { a.set(current.Name, current) return a }, new Map<string, IRepositoryDataType>()) } }