340 lines
16 KiB
JavaScript
340 lines
16 KiB
JavaScript
"use strict";
|
|
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
|