UNPKG

@makakwastaken/ts-edifact

Version:
223 lines 10.2 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /** * @author Roman Vottner * @copyright 2020 Roman Vottner * @license Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Cache } from './cache'; import { ComponentValueTableBuilder } from './components'; import { Configuration } from './configuration'; import { Parser } from './parser'; import { SegmentTableBuilder } from './segments'; import { findElement, isDefined } from './util'; import { Dictionary, ValidatorImpl, } from './validator'; /** * The `Reader` class is included for backwards compatibility. It translates an * UN/EDIFACT document to an array of segments. Each segment has a `name` and * `elements` property where `elements` is an array consisting of component * arrays. The class exposes a `parse()` method which accepts the document as a * string. */ export class Reader { result; elements; element; validator; parser; defined = false; validationTables = []; componentValues = new Dictionary(); definitionCache = new Cache(15); unbCharsetDefined = false; separators; constructor(messageSpecDir, config = {}) { const configuration = new Configuration({ validator: new ValidatorImpl(config.throwOnMissingSegments || false), }); this.parser = new Parser(configuration); this.validator = configuration.validator; // Holds the results this.result = []; // Holds the elements this.elements = []; let components = []; // Holds the temporary element components. let componentIndex = 0; let activeSegment; // When a new segment is opened, reset all things element and set the current segment this.parser.onOpenSegment = (segment, segmentEntry) => { this.elements = []; this.element = undefined; // Set the currently active segments activeSegment = segmentEntry ? { id: segment, segmentEntry: segmentEntry } : null; }; this.parser.onElement = (newElement) => { // Add the previous element if (this.element) { this.elements.push({ ...this.element, components }); } // Set the current element this.element = newElement; // Reset component values components = []; componentIndex = 0; }; this.parser.onComponent = (value) => { if (activeSegment?.id === 'UNB' && !this.unbCharsetDefined) { this.parser.updateCharset(value); this.unbCharsetDefined = true; } // Replace value at index with correct one if (this.element) { const component = this.element.components[componentIndex]; // Check if it is a coded value if (this.componentValues.contains(component.id)) { const componentFormatRegex = /(a)?(n)?(\.\.)?([0-9]*)?/g; const componentFormat = componentFormatRegex.exec(component.format); if (isDefined(componentFormat)) { const upto = componentFormat[3] === '..'; const componentValues = this.componentValues.get(component.id); const componentValue = componentValues[value]; if (!componentValue) { if (upto) { // If the value is marked as 'upto' it is not required to be in the component value table as it can be '' components.push({ ...component, value: { id: value, value: 'No code provided', // Can't be '' as it is marked as failure if id and value are the same description: 'No code provided', }, }); } else if (config.throwOnInvalidComponentValue) { throw new Error(`Invalid component value '${value}' for component '${component.id}'`); } else { components.push({ ...component, value: { id: value, value, description: 'Code not found', }, }); } } else { components.push({ ...component, value: componentValue }); } } } else { components.push({ ...component, value }); } componentIndex++; } }; this.parser.onCloseSegment = () => { if (isDefined(activeSegment)) { if (this.element) { // Add the final element, when a segment ends (Prevents the final element from missing) this.elements.push({ ...this.element, components }); } // Update the respective segment and element definitions once we know the exact version // of the document if (activeSegment.id === 'UNH') { const messageIdentifier = findElement(this.elements, 'S009')?.components; const messageType = messageIdentifier?.[0]?.value; const messageVersion = messageIdentifier?.[1]?.value; const messageRelease = messageIdentifier?.[2]?.value; const key = `${messageVersion + messageRelease}_${messageType}`; if (this.definitionCache.contains(key)) { const { segmentTable, componentValueTable } = this.definitionCache.get(key); this.componentValues = componentValueTable; this.validator.define(segmentTable); } else { // Get the segments and component definitions let segmentTableBuilder = new SegmentTableBuilder(messageType); let componentValueTableBuilder = new ComponentValueTableBuilder(messageType); const version = (messageVersion + messageRelease).toUpperCase(); segmentTableBuilder = segmentTableBuilder.forVersion(version); componentValueTableBuilder = componentValueTableBuilder.forVersion(version); if (messageSpecDir) { segmentTableBuilder = segmentTableBuilder.specLocation(messageSpecDir); componentValueTableBuilder.specLocation(messageSpecDir); } else { segmentTableBuilder = segmentTableBuilder.specLocation('./'); componentValueTableBuilder = componentValueTableBuilder.specLocation('./'); } const segmentTable = segmentTableBuilder.build(); const componentValueTable = componentValueTableBuilder.build(); this.componentValues = componentValueTable; this.validator.define(segmentTable); this.definitionCache.insert(key, { segmentTable, componentValueTable, }); } } // Add the current elements to the results array this.result.push({ name: activeSegment.id, elements: this.elements, }); activeSegment = null; } }; // will initialize default separators this.separators = this.parser.separators(); } /** * Provide the underlying `Validator` with segment or element definitions. * * @summary Define segment and element structures. * @param definitions An object containing the definitions. */ define(definitions) { this.validator.define(definitions); } initializeIfNeeded() { if (!this.defined) { if (this.validationTables.length > 0) { for (const table of this.validationTables) { this.validator.define(table); } } else { // basic Edifact envelop validation, i.e. UNB, UNH, UNS and UNZ this.validator.define(SegmentTableBuilder.enrichWithDefaultSegments(new Dictionary())); } this.defined = true; } } parse(document) { this.initializeIfNeeded(); this.result = []; this.parser.write(document); this.parser.end(); // update separators in case the document contained a UNA header // with custom separators this.separators = this.parser.separators(); return this.result; } } //# sourceMappingURL=reader.js.map