UNPKG

jspurefix

Version:
512 lines (458 loc) 16 kB
import { ISaxNode } from '../../dict-primitive' import { Dictionary } from '../../../collections' import { XsdParser } from './xsd-parser' import { ComponentFieldDefinition, SimpleFieldDefinition, GroupFieldDefinition, FixDefinitions, MessageDefinition } from '../../definition' import { ContainedSimpleField, ContainedComponentField, ContainedFieldSet, ContainedGroupField } from '../../contained' interface IElement { ref: string name: string type: string minOccurs: string maxOccurs: string substitutionGroup: string } interface IAttribute { name: string type: string use: string } interface IAttributeGroup { name: string attributes: IAttribute[] } interface IGroup { name: string elements: IElement[] } interface IAppInfo { Protocol: string MsgID: string name: string, ComponentType: string Category: string } /* <xs:complexContent> <xs:extension base="BaseHeader_t"> <xs:sequence> <xs:group ref="BatchHeaderElements"/> </xs:sequence> <xs:attributeGroup ref="BatchHeaderAttributes"/> </xs:extension> </xs:complexContent> */ interface IAnnotation { documentation: string } interface IComplexType { annotation: IAnnotation extensionBase: string // where a reference to base class element: IElement[] messageName: string name: string appInfo: IAppInfo group: string attributeGroup: string } export class ComponentsParser extends XsdParser { private readonly attributeGroups: Dictionary<IAttributeGroup> = new Dictionary<IAttributeGroup>() private readonly groups: Dictionary<IGroup> = new Dictionary<IGroup>() private readonly unboundElements: IElement[] = [] private readonly complexTypes: Dictionary<IComplexType> = new Dictionary<IComplexType>() private newComplexTypes: IComplexType[] private currentGroup: IGroup private currentAttributeGroupStack: IAttributeGroup[] = [] private currentComplexType: IComplexType private previousComplexType: IComplexType public constructor (public readonly definitions: FixDefinitions) { super(definitions) } private static getName (group: IGroup, attributeGroup: IAttributeGroup, type: IComplexType): string { let name: string if (type && type.appInfo) { name = type.appInfo.name } else if (group) { name = group.name } else if (attributeGroup) { name = attributeGroup.name } else { name = type.name } return name } public value (line: number, n: string, v: string): void { // do nothing switch (n) { case 'xs:documentation': { if (this.currentComplexType && this.currentComplexType.annotation) { this.currentComplexType.annotation.documentation = v } break } } this.pending = null } public close (line: number, node: string): void { switch (node) { case'xs:complexType': { if (this.currentComplexType != null) { const complex: IComplexType = this.currentComplexType this.previousComplexType = complex this.complexTypes.addUpdate(complex.name, complex) this.newComplexTypes.push(complex) this.currentComplexType = null } break } case 'xs:group': { if (this.currentGroup) { this.groups.addUpdate(this.currentGroup.name, this.currentGroup) this.currentGroup = null } break } case 'xs:attributeGroup': { const attributeStack = this.currentAttributeGroupStack if (attributeStack.length > 0) { const group: IAttributeGroup = attributeStack.pop() this.attributeGroups.addUpdate(group.name, group) } break } case 'xs:schema': { this.insertFields() break } } } public open (line: number, node: ISaxNode): void { switch (node.name) { case 'xs:schema': { this.newComplexTypes = [] break } case 'xs:group': { this.xsGroup(node) break } case 'xs:attributeGroup': { this.xsAttributeGroup(node) break } case 'xs:element': { this.xsElement(node) break } case 'xs:appinfo': { this.currentComplexType.appInfo = { } as IAppInfo break } case 'xs:attribute': { this.xsAttribute(node) break } case 'xs:complexType': { const unbound = this.unboundElements this.currentComplexType = { name: node.attributes.name || unbound[unbound.length - 1].name } as IComplexType break } case 'xs:extension': { const current = this.currentComplexType if (current && node.attributes.base) { current.extensionBase = node.attributes.base } break } case 'fm:Xref': { this.assign(node, this.currentComplexType.appInfo) break } case 'xs:annotation': { if (this.currentComplexType) { this.currentComplexType.annotation = {} as IAnnotation } break } case 'xs:documentation': { if (this.currentComplexType) { this.pending = node.name } break } } } private xsAttribute (node: ISaxNode) { const attribute: IAttribute = {} as IAttribute this.assign(node, attribute) const stack = this.currentAttributeGroupStack if (stack.length > 0) { const peek = stack[stack.length - 1] const groupAttributes: IAttribute[] = peek.attributes groupAttributes[groupAttributes.length] = attribute } } private xsGroup (node: ISaxNode) { if (node.attributes.name) { this.currentGroup = { name: node.attributes['name'], elements: [] as IElement[] } as IGroup } else if (node.attributes.ref) { if (this.currentComplexType) { this.currentComplexType.group = node.attributes.ref } else if (this.currentGroup) { // this is part of a sequence for a group const elements = this.currentGroup.elements elements[elements.length] = { type: node.name, name: node.attributes.ref } as IElement } } } private xsAttributeGroup (node: ISaxNode) { const attributeStack = this.currentAttributeGroupStack if (node.attributes.name) { attributeStack.push({ name: node.attributes.name, attributes: [] as IAttribute[] } as IAttributeGroup) } else if (node.attributes.ref) { if (this.currentComplexType) { this.currentComplexType.attributeGroup = node.attributes.ref } else if (attributeStack.length > 0) { const peek: IAttributeGroup = attributeStack[attributeStack.length - 1] peek.attributes[peek.attributes.length] = { type: node.name, name: node.attributes.ref } as IAttribute } } } private xsElement (node: ISaxNode) { const element: IElement = {} as IElement this.assign(node, element) const currentComplex = this.currentComplexType const currentGroup = this.currentGroup if (!currentGroup && currentComplex) { if (!currentComplex.element) { currentComplex.element = [] } const elements: IElement[] = currentComplex.element elements[elements.length] = element } else if (currentGroup) { const elements = currentGroup.elements elements[elements.length] = element } else { if (element.substitutionGroup && this.previousComplexType) { this.previousComplexType.messageName = element.name } else { this.unboundElements.push(element) } } } private addElement (set: ContainedFieldSet, element: IElement) { const minOccurs: number = parseInt(element.minOccurs, 10) const isGroup: boolean = element.maxOccurs === 'unbounded' const isComponent: boolean = element.maxOccurs === '1' || !isGroup const key = element.type || element.ref || element.name const containedType: IComplexType = this.complexTypes.get(key) if (containedType) { if (isComponent) { const containedDefinition: ComponentFieldDefinition = this.getComponent(containedType) const containedField: ContainedComponentField = new ContainedComponentField(containedDefinition, set.fields.length, minOccurs > 0, element.name) set.add(containedField) } else if (isGroup) { const containedDefinition: GroupFieldDefinition = this.getGroup(containedType) const containedField: ContainedGroupField = new ContainedGroupField(containedDefinition, set.fields.length, minOccurs > 0, element.name) set.add(containedField) } } else { if (key !== 'Message') { throw new Error(`cannot resolve component ${key} contained in ${set.name}`) } } } private addElements (set: ContainedFieldSet, elements: IElement[]) { if (elements) { elements.forEach((element: IElement) => { switch (element.type) { case 'xs:group': const groupElements: IGroup = this.groups.get(element.name) if (groupElements) { this.addElements(set, groupElements.elements) } else { throw new Error(`unable to get xs:group ${element.name}`) } break default: { this.addElement(set, element) } } }) } } private addSimpleAttribute (set: ContainedFieldSet, attribute: IAttribute): void { let sf: SimpleFieldDefinition = this.definitions.getSimple(attribute.type) if (!sf) { sf = this.definitions.getSimple(attribute.name, set.category) } if (sf) { const contained: ContainedSimpleField = new ContainedSimpleField(sf, set.fields.length, attribute.use !== 'optional', true, attribute.name) set.add(contained) } else if (set.name !== 'FixmlAttributes') { throw new Error(`unable to resolve simple attribute ${attribute.name}`) } } private addAttributes (set: ContainedFieldSet, attributes: IAttribute[]) { attributes.forEach((attribute: IAttribute) => { switch (attribute.type) { case 'xs:attributeGroup': { const attributeGroup: IAttributeGroup = this.attributeGroups.get(attribute.name) if (attributeGroup) { this.addAttributes(set, attributeGroup.attributes) } else { throw new Error(`unable to get xs:attributeGroup ${attribute.name}`) } break } default: { this.addSimpleAttribute(set, attribute) } } }) } private getGroup (type: IComplexType): GroupFieldDefinition { let group: IGroup = this.groups.get(type.group) const attributeGroup: IAttributeGroup = this.attributeGroups.get(type.attributeGroup) const name: string = ComponentsParser.getName(group, attributeGroup, type) const category: string = type.appInfo != null ? type.appInfo.Category : null const groupDefinition: GroupFieldDefinition = new GroupFieldDefinition(name, name, category, null, null) this.populateSet(type, groupDefinition) return groupDefinition } private getComponent (type: IComplexType): ComponentFieldDefinition { const definitions = this.definitions let group: IGroup = this.groups.get(type.group) const attributeGroup: IAttributeGroup = this.attributeGroups.get(type.attributeGroup) let name: string = ComponentsParser.getName(group, attributeGroup, type) const cached: ComponentFieldDefinition = definitions.component.get(name) if (cached) { return cached } const category: string = type.appInfo != null ? type.appInfo.Category : null if (type.extensionBase) { let base = this.complexTypes.get(type.extensionBase) if (base) { name = base.appInfo.name } } const component: ComponentFieldDefinition = new ComponentFieldDefinition(name, name, category, null) this.populateSet(type, component) definitions.component.addUpdate(component.name, component) definitions.component.addUpdate(type.name, component) return component } private getMessage (type: IComplexType): MessageDefinition { const definitions = this.definitions const messages = definitions.message const name: string = type.appInfo.name const message: MessageDefinition = new MessageDefinition(name, type.messageName, type.appInfo.MsgID, type.appInfo.Category, type.annotation.documentation) const abstractMessage: ComponentFieldDefinition = definitions.component.get('Message') abstractMessage.fields.forEach((f) => { message.add(f) }) this.populateSet(type, message) messages.addUpdate(message.name, message) if (type.messageName && type.messageName !== name) { messages.addUpdate(type.messageName, message) } return message } private getBaseAttributes (type: IComplexType): IAttributeGroup { const attributeGroups = this.attributeGroups let baseGroup: IAttributeGroup = null if (type.extensionBase) { let base = this.complexTypes.get(type.extensionBase) if (base) { baseGroup = attributeGroups.get(base.attributeGroup) } return baseGroup } return null } private populateSet (type: IComplexType, set: ContainedFieldSet): void { const group: IGroup = this.groups.get(type.group) const elements: IElement[] = group ? group.elements : type.element const attributeGroups = this.attributeGroups const attributeGroup: IAttributeGroup = attributeGroups.get(type.attributeGroup) const baseGroup: IAttributeGroup = this.getBaseAttributes(type) // if a base is specified add the attributes from there if (baseGroup) { this.addAttributes(set, baseGroup.attributes) } if (attributeGroup) { this.addAttributes(set, attributeGroup.attributes) } this.addElements(set, elements) } private constructType (type: IComplexType) { const componentType: string = type.appInfo != null ? type.appInfo.ComponentType : 'Block' switch (componentType) { case 'Message': { const message: MessageDefinition = this.getMessage(type) if (!message) { throw new Error(`cannot resolve ${type.name}`) } break } // these may be included from top level messages and added into definitions case 'Block': { const component: ComponentFieldDefinition = this.getComponent(type) if (!component) { throw new Error(`cannot resolve ${type.name}`) } break } case 'ImplicitBlock': case 'XMLDataBlock': case 'BlockRepeating': case 'ImplicitBlockRepeating': { break } default: throw new Error(`unknown type ${componentType}`) } } private insertFields (): void { this.newComplexTypes.forEach((type: IComplexType) => { this.constructType(type) }) this.unboundElements.forEach((e: IElement) => { const definitions = this.definitions const component = definitions.component.get(e.type) if (component) { definitions.component.addUpdate(e.name, component) } }) } }