@makakwastaken/ts-edifact
Version:
Edifact parser library
227 lines • 10.4 kB
JavaScript
"use strict";
/* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Reader = void 0;
const cache_1 = require("./cache");
const components_1 = require("./components");
const configuration_1 = require("./configuration");
const parser_1 = require("./parser");
const segments_1 = require("./segments");
const util_1 = require("./util");
const validator_1 = require("./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.
*/
class Reader {
result;
elements;
element;
validator;
parser;
defined = false;
validationTables = [];
componentValues = new validator_1.Dictionary();
definitionCache = new cache_1.Cache(15);
unbCharsetDefined = false;
separators;
constructor(messageSpecDir, config = {}) {
const configuration = new configuration_1.Configuration({
validator: new validator_1.ValidatorImpl(config.throwOnMissingSegments || false),
});
this.parser = new parser_1.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 ((0, util_1.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 ((0, util_1.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 = (0, util_1.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 segments_1.SegmentTableBuilder(messageType);
let componentValueTableBuilder = new components_1.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(segments_1.SegmentTableBuilder.enrichWithDefaultSegments(new validator_1.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;
}
}
exports.Reader = Reader;
//# sourceMappingURL=reader.js.map