@makakwastaken/ts-edifact
Version:
Edifact parser library
150 lines • 5.74 kB
JavaScript
"use strict";
/**
* @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.Tracker = exports.Pointer = void 0;
/**
* A utility class representing the current position in a segment group.
*/
class Pointer {
array;
position;
count;
constructor(array, position) {
this.array = array;
this.position = position || 0;
this.count = 0;
}
content() {
return this.array[this.position].content;
}
mandatory() {
return this.array[this.position].mandatory;
}
repetition() {
return this.array[this.position].repetition;
}
name() {
return this.array[this.position].name;
}
section() {
if (this.array[this.position] && this.array[this.position].section) {
return this.array[this.position].section;
}
return undefined;
}
}
exports.Pointer = Pointer;
class Tracker {
stack;
/**
* Construct a new tracker pointing to the first segment in the table.
*
* @constructs Tracker
* @param table The segment table to track against.
*/
constructor(table) {
this.stack = [new Pointer(table, 0)];
}
/**
* Reset the tracker to the initial position of the current segment table.
*/
reset() {
this.stack.length = 1;
this.stack[0].position = 0;
this.stack[0].count = 0;
}
/**
* Match a segment to the message structure and update the current
* position of the tracker.
*
* @param segment The segment name.
* @throws {Error} Throws if a mandatory segment was omitted.
* @throws {Error} Throws if unidentified segments are encountered.
* @throws {Error} Throws if a segment is repeated too much.
*/
accept(segment) {
let current = this.stack[this.stack.length - 1];
let optionals = [];
let probe = 0;
while (segment !== current?.content() ||
current.count === current.repetition()) {
if (Array.isArray(current?.content()) &&
current.count < current.repetition()) {
// Enter the subgroup.
probe++;
if (!current.mandatory()) {
optionals.push(this.stack.length);
}
current.count++;
current = new Pointer(current?.content(), 0);
this.stack.push(current);
}
else {
// Check if we are omitting mandatory content
if (current.mandatory() && current.count === 0) {
if (optionals.length === 0) {
// We will never encounter groups here, so we can safely use the
// name of the segment stored in it's content property
throw new Error(`A mandatory segment ${current?.content()} is missing`);
}
// If we are omitting mandatory content inside a conditional group,
// we just skip the entire group
probe = probe - this.stack.length;
this.stack.length = optionals.pop();
current = this.stack[this.stack.length - 1];
probe = probe + this.stack.length;
}
current.position++;
current.count = 0;
if (current.position === current.array.length) {
this.stack.pop();
current = this.stack[this.stack.length - 1];
if (this.stack.length === 0) {
throw new Error('Reached the end of the segment table');
}
if (probe === 0 && current.count < current.repetition()) {
// If we are not currently probing (meaning the tracker actually
// accepted the group), we should retry the current group, except if
// the maximum number of repetition was reached
probe++;
optionals = [this.stack.length];
current.count++;
current = new Pointer(current?.content(), 0);
this.stack.push(current);
}
else {
if (!current.mandatory() || current.count > 1) {
optionals.pop();
}
// Decrease the probing level only if the tracker is currently in a
// probing state.
probe = probe > 0 ? probe - 1 : 0;
// Make sure the tracker won't enter the current group again by
// setting an appropriate count value on the groups pointer
current.count = current.repetition();
}
}
}
}
current.count += 1;
return;
}
}
exports.Tracker = Tracker;
//# sourceMappingURL=tracker.js.map