ts-edifact
Version:
Edifact parser library
340 lines • 16.8 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNECEMessageStructureParser = exports.EdifactMessageSpecificationImpl = void 0;
const validator_1 = require("../validator");
const httpClient_1 = require("../httpClient");
const htmlparser2_1 = require("htmlparser2");
const util_1 = require("../util");
class EdifactMessageSpecificationImpl {
constructor(messageType, version, release, controllingAgency) {
this.segmentTable = new validator_1.Dictionary();
this.elementTable = new validator_1.Dictionary();
this.messageStructureDefinition = [];
this.messageType = messageType;
this.version = version;
this.release = release;
this.controllingAgency = controllingAgency;
}
type() {
return this.version + this.release + "_" + this.messageType;
}
versionAbbr() {
return this.version + this.release;
}
}
exports.EdifactMessageSpecificationImpl = EdifactMessageSpecificationImpl;
var Part;
(function (Part) {
Part[Part["BeforeStructureDef"] = 0] = "BeforeStructureDef";
Part[Part["RefLink"] = 1] = "RefLink";
Part[Part["Pos"] = 2] = "Pos";
Part[Part["Tag"] = 3] = "Tag";
Part[Part["Deprecated"] = 4] = "Deprecated";
Part[Part["Name"] = 5] = "Name";
Part[Part["AfterStructureDef"] = 6] = "AfterStructureDef";
})(Part || (Part = {}));
var SegmentPart;
(function (SegmentPart) {
SegmentPart[SegmentPart["BeforeStructureDef"] = 0] = "BeforeStructureDef";
SegmentPart[SegmentPart["Data"] = 1] = "Data";
SegmentPart[SegmentPart["AfterStructureDef"] = 2] = "AfterStructureDef";
})(SegmentPart || (SegmentPart = {}));
class UNECEMessageStructureParser {
constructor(version, type) {
this.version = version.toLowerCase();
this.type = type.toLowerCase();
const baseUrl = "https://service.unece.org/trade/untdid/" + this.version + "/trmd/" + this.type + "_c.htm";
this.httpClient = new httpClient_1.HttpClient(baseUrl);
}
extractTextValue(text, regex, index = 0) {
const arr = regex.exec(text);
if ((0, util_1.isDefined)(arr)) {
return arr[index];
}
return "";
}
loadPage(page) {
return __awaiter(this, void 0, void 0, function* () {
const data = yield this.httpClient.get(page);
return data;
});
}
parseSegmentDefinitionPage(segment, page, definition) {
return __awaiter(this, void 0, void 0, function* () {
if (definition.segmentTable.contains(segment)) {
return Promise.resolve(definition);
}
const segEntry = { "requires": 0, "elements": [] };
let state = SegmentPart.BeforeStructureDef;
let dataSection = false;
let skipAddingElement = false;
let overflowLine = null;
let complexEleId = null;
let complexEleEntry = null;
for (let line of page.split("\n")) {
line = line.trimRight();
if (overflowLine !== null) {
line = overflowLine.trimRight() + " " + line.trim();
overflowLine = null;
}
if (state === SegmentPart.BeforeStructureDef && line.includes('<HR>')) {
dataSection = true;
}
else if (state === SegmentPart.BeforeStructureDef &&
(line.includes("<H3>") || (dataSection && line.includes('<B>')))) {
state = SegmentPart.Data;
}
else if (state === SegmentPart.Data && !line.includes("<P>")) {
const regexp = /^([\d]*)\s*?([X|\\*]?)\s*<A.*>([a-zA-Z0-9]*)<\/A>([a-zA-Z0-9 ,\-\\/&]{44,})([M|C])\s*([\d]*)\s*([a-zA-Z0-9\\.]*).*$/g;
const arr = regexp.exec(line);
if ((0, util_1.isDefined)(arr)) {
const segGroupId = arr[1] === "" ? undefined : arr[1];
const id = arr[3];
const mandatory = arr[5] === "M" ? true : false;
const elementDef = arr[7] === "" ? undefined : arr[7];
if (segGroupId) {
if (id === "") {
console.warn(`Could not determine element ID based on line ${line}`);
continue;
}
segEntry.elements.push(id);
skipAddingElement = false;
if (mandatory) {
segEntry.requires = segEntry.requires + 1;
}
if (elementDef) {
if (complexEleEntry !== null && complexEleId !== null) {
definition.elementTable.add(complexEleId, complexEleEntry);
}
complexEleId = null;
complexEleEntry = null;
if (definition.elementTable.contains(id)) {
continue;
}
const eleEntry = { "requires": 0, "components": [] };
if (mandatory) {
eleEntry.requires = eleEntry.requires + 1;
}
eleEntry.components.push(elementDef);
definition.elementTable.add(id, eleEntry);
}
else {
if (complexEleEntry !== null && complexEleId !== null) {
definition.elementTable.add(complexEleId, complexEleEntry);
}
if (definition.elementTable.contains(id)) {
skipAddingElement = true;
continue;
}
complexEleId = id;
complexEleEntry = { "requires": 0, "components": [] };
}
}
else {
if (!skipAddingElement) {
if (complexEleEntry !== null && elementDef) {
complexEleEntry.components.push(elementDef);
complexEleEntry.requires = mandatory ? complexEleEntry.requires + 1 : complexEleEntry.requires;
}
else {
if (definition.elementTable.contains(id)) {
continue;
}
const eleEntry = { "requires": 0, "components": [] };
if (mandatory) {
eleEntry.requires = eleEntry.requires + 1;
}
if (elementDef) {
eleEntry.components.push(elementDef);
}
definition.elementTable.add(id, eleEntry);
}
}
}
}
else {
const regexpAlt = /^([\d]*)\s*([X|\\*]?)\s*<A.*>([a-zA-Z0-9]*)<\/A>\s*([a-zA-Z0-9 \\-\\/&]*)/g;
const arrAlt = regexpAlt.exec(line);
if ((0, util_1.isDefined)(arrAlt)) {
overflowLine = line;
}
}
}
else if (state === SegmentPart.Data && line.includes("<P>")) {
state = SegmentPart.AfterStructureDef;
break;
}
}
if (complexEleEntry !== null && complexEleId !== null) {
definition.elementTable.add(complexEleId, complexEleEntry);
}
if (segment !== "") {
definition.segmentTable.add(segment, segEntry);
}
return Promise.resolve(definition);
});
}
parsePage(page) {
return __awaiter(this, void 0, void 0, function* () {
let definition;
const handler = new htmlparser2_1.DomHandler();
let state = Part.BeforeStructureDef;
let section = "header";
const segStack = [];
const lookupSegmentPromises = [];
const nextState = () => {
if (state === Part.RefLink) {
state = Part.Pos;
}
else if (state === Part.Pos) {
state = Part.Deprecated;
}
else if (state === Part.Deprecated) {
state = Part.Tag;
}
else if (state === Part.Tag) {
state = Part.Name;
}
else if (state === Part.Name) {
state = Part.RefLink;
}
};
handler.ontext = (text) => {
if (text.includes("Message Type") && text.includes("Version") && text.includes("Release")) {
const messageType = this.extractTextValue(text, /Message Type\s*: ([A-Z]*)\s/g, 1);
const version = this.extractTextValue(text, /Version\s*: ([A-Z]*)\s/g, 1);
const release = this.extractTextValue(text, /Release\s*: ([0-9A-Z]*)\s/g, 1);
const controllingAgency = this.extractTextValue(text, /Contr. Agency\s*: ([0-9A-Z]*)\s/g, 1);
definition = new EdifactMessageSpecificationImpl(messageType, version, release, controllingAgency);
segStack.push(definition.messageStructureDefinition);
}
else if (text.includes("Message structure")) {
state = Part.RefLink;
}
else if (state !== Part.BeforeStructureDef && state !== Part.AfterStructureDef) {
if (state === Part.RefLink) {
}
else if (state === Part.Pos) {
}
else if (state === Part.Deprecated) {
if (text.includes("- Segment group")) {
const regex = /^[\s*+-]*-* (Segment group \d*)\s*-*\s*([M|C])\s*(\d*)([-|\\+|\\|]*).*/g;
const arr = regex.exec(text);
if ((0, util_1.isDefined)(arr)) {
const groupArray = [];
const group = {
content: groupArray,
mandatory: arr[2] === "M" ? true : false,
repetition: parseInt(arr[3]),
name: arr[1],
section: (0, util_1.isDefined)(section) ? section : undefined
};
section = null;
segStack[segStack.length - 1].push(group);
segStack.push(groupArray);
}
state = Part.RefLink;
}
else {
nextState();
}
}
else if (state === Part.Tag) {
const _section = section !== null ? section : undefined;
let _data;
if (definition) {
_data = text === "UNH" ? [definition.versionAbbr(), definition.messageType] : undefined;
}
const segment = {
content: text,
mandatory: false,
repetition: 0,
data: _data,
section: _section
};
if (definition) {
segStack[segStack.length - 1].push(segment);
}
section = null;
}
else if (state === Part.Name) {
const regex = /^([a-zA-Z /\\-]*)\s*?([M|C])\s*?([0-9]*?)([^0-9]*)$/g;
const arr = regex.exec(text);
if ((0, util_1.isDefined)(arr)) {
const sMandatory = arr[2];
const sRepetition = arr[3];
const remainder = arr[4];
const segArr = segStack[segStack.length - 1];
const segData = segArr[segArr.length - 1];
segData.mandatory = sMandatory === "M" ? true : false;
segData.repetition = parseInt(sRepetition);
if (remainder.includes("-") && remainder.includes("+")) {
for (let i = 0; i < remainder.split("+").length - 1; i++) {
segStack.pop();
}
}
nextState();
}
if (text.includes("DETAIL SECTION")) {
section = "detail";
}
else if (text.includes("SUMMARY SECTION")) {
section = "summary";
}
}
else {
console.warn(`Unknown part: ${text}`);
}
}
};
handler.onopentag = (name, attribs) => {
if (name === "p" && state !== Part.BeforeStructureDef && state !== Part.AfterStructureDef) {
state = Part.AfterStructureDef;
}
if (state === Part.Tag && attribs.href !== undefined) {
if (definition) {
const end = attribs.href.indexOf(".htm");
const curSeg = attribs.href.substring(end - 3, end).toUpperCase();
if (curSeg !== "UNH" && curSeg !== "UNS" && curSeg !== "UNT") {
const def = definition;
lookupSegmentPromises.push(this.loadPage(attribs.href)
.then(result => this.parseSegmentDefinitionPage(curSeg, result, def)));
}
}
}
};
handler.onclosetag = () => {
nextState();
};
const parser = new htmlparser2_1.Parser(handler);
parser.write(page);
parser.end();
if (definition) {
return Promise.resolve({ specObj: definition, promises: lookupSegmentPromises });
}
return Promise.reject(new Error("Could not extract values from read page successfully"));
});
}
loadTypeSpec() {
const url = "./" + this.type + "_c.htm";
return this.loadPage(url)
.then((page) => this.parsePage(page))
.then((result) => Promise.all(result.promises)
.then(() => result.specObj)
.catch((error) => {
console.warn(`Error while processing segment definition promises: Reason ${error.message}`);
return result.specObj;
}));
}
}
exports.UNECEMessageStructureParser = UNECEMessageStructureParser;
//# sourceMappingURL=messageStructureParser.js.map
;