UNPKG

isoxml-angular

Version:

JavaScript library to parse and generate ISOXML (ISO11783-10) files

236 lines (235 loc) 10.4 kB
"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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ISOXMLManager = void 0; const jszip_1 = __importDefault(require("jszip")); const classRegistry_1 = require("./classRegistry"); require("./baseEntities"); require("./entities"); const ISO11783TaskDataFile_1 = require("./entities/ISO11783TaskDataFile"); const xmlManager_1 = require("./xmlManager"); const MAIN_FILENAME = 'TASKDATA.XML'; const ROOT_FOLDER = 'TASKDATA'; class ISOXMLManager { constructor(options = {}) { this.nextIds = {}; this.parsingWarnings = []; this.xmlReferences = {}; this.filesToSave = {}; this.options = { version: 4, fmisTitle: 'FMIS', fmisVersion: '1.0', rootFolder: ROOT_FOLDER, realm: 'main' }; this.updateOptions(options); this.rootElement = ISO11783TaskDataFile_1.ExtendedISO11783TaskDataFile.fromISOXMLManagerOptions(this); } parseXmlId(xmlId) { const match = xmlId.match(/([A-Z]{3})(-?\d+)/); if (!match) { return null; } return { tag: match[1], id: parseInt(match[2], 10) }; } generateUniqueFilename(xmlTag) { const indexes = Object.keys(this.filesToSave) .map(filename => filename.match(new RegExp(`^${xmlTag}(\\d{5})\\.\\w{3}$`))) .filter(e => e) .map(matchResults => parseInt(matchResults[1], 10)); const nextIndex = indexes.length ? Math.max.apply(null, indexes) + 1 : 1; return xmlTag + ('0000' + nextIndex).substr(-5); } addFileToSave(data, filenameWithExtension) { this.filesToSave[filenameWithExtension] = data; } /** * if "xmlId" is provided: * - if such reference exists, update its content with "entity" and "fmis" * - otherwise, generate new reference * if "xmlId" is not provided, do the following: * - try to find the "entity" (the same JS object) in references * - try to find the entity with the same type and "fmisId" if provided * - if both above failed, create new reference * Returns undefined if failed to create reference */ registerEntity(entity, xmlId, fmisId) { if (!entity && !xmlId) { return; } if (!xmlId) { const tag = entity.tag; const existingReference = Object.values(this.xmlReferences) .filter(ref => ref.xmlId.startsWith(tag)) .find(ref => ref.entity === entity || (fmisId && fmisId === ref.fmisId)); if (existingReference) { xmlId = existingReference.xmlId; } else { this.nextIds[entity.tag] = this.nextIds[entity.tag] || 1; xmlId = `${entity.tag}${this.nextIds[entity.tag]++}`; } } else { const parsedXmlId = this.parseXmlId(xmlId); if (!parsedXmlId) { return; } // check consistency between xmlId and entity if (entity && entity.tag !== parsedXmlId.tag) { return; } this.nextIds[parsedXmlId.tag] = Math.max(this.nextIds[parsedXmlId.tag] || 1, parsedXmlId.id + 1); } this.xmlReferences[xmlId] = this.xmlReferences[xmlId] || { xmlId }; const ref = this.xmlReferences[xmlId]; if (entity) { ref.entity = entity; } if (fmisId) { ref.fmisId = fmisId; } return ref; } createEntityFromXML(tagName, xml, internalId) { const entityClass = (0, classRegistry_1.getEntityClassByTag)(this.options.realm, tagName); if (!entityClass) { return null; } return entityClass.fromXML(xml, this, internalId); } createEntityFromAttributes(tagName, attrs) { const entityClass = (0, classRegistry_1.getEntityClassByTag)(this.options.realm, tagName); if (!entityClass) { return null; } return new entityClass(attrs, this); } parseISOXMLFile(data, dataType) { var _a; return __awaiter(this, void 0, void 0, function* () { if (dataType === 'application/xml' || dataType === 'text/xml') { this.options.rootFolder = ''; const mainXml = (0, xmlManager_1.xml2js)(data); if (!mainXml['ISO11783_TaskData']) { throw new Error('Incorrect structure of TASKDATA.XML'); } this.rootElement = (yield (0, classRegistry_1.getEntityClassByTag)(this.options.realm, "ISO11783_TaskData" /* TAGS.ISO11783TaskDataFile */) .fromXML(mainXml["ISO11783_TaskData" /* TAGS.ISO11783TaskDataFile */][0], this, '')); } else if (dataType === 'application/zip') { this.originalZip = yield jszip_1.default.loadAsync(data); const mainFilenames = Object.keys(this.originalZip.files).filter(path => { const splitted = path.split('/'); return splitted[splitted.length - 1].toLowerCase() === MAIN_FILENAME.toLowerCase(); }); if (mainFilenames.length === 0) { throw new Error("ZIP file doesn't contain TASKDATA.XML"); } if (mainFilenames.length > 1) { this.addWarning("More than one TASKDATA.XML files found in ZIP file - selecting one of them"); } const mainFilename = (_a = mainFilenames.find(filename => filename.toLowerCase() === `${ROOT_FOLDER}/${MAIN_FILENAME}`.toLowerCase())) !== null && _a !== void 0 ? _a : mainFilenames[0]; if (mainFilename.slice(-MAIN_FILENAME.length) !== MAIN_FILENAME) { this.addWarning(`Name of the main file must be uppercase (real name: ${mainFilename})`); } this.options.rootFolder = mainFilename.slice(0, -MAIN_FILENAME.length); const mainFile = this.originalZip.file(mainFilename); const mainXmlString = yield mainFile.async('string'); const mainXml = (0, xmlManager_1.xml2js)(mainXmlString); if (!mainXml['ISO11783_TaskData']) { throw new Error('Incorrect structure of TASKDATA.XML'); } this.rootElement = (yield (0, classRegistry_1.getEntityClassByTag)(this.options.realm, "ISO11783_TaskData" /* TAGS.ISO11783TaskDataFile */) .fromXML(mainXml["ISO11783_TaskData" /* TAGS.ISO11783TaskDataFile */][0], this, '')); } else { throw new Error('This data type is not supported'); } }); } saveISOXML() { return __awaiter(this, void 0, void 0, function* () { this.filesToSave = {}; if (this.options.fmisURI) { this.rootElement.addLinkListFile(); } const json = { ISO11783_TaskData: this.rootElement.toXML() }; const mainXML = (0, xmlManager_1.js2xml)(json); const zipWriter = new jszip_1.default(); zipWriter.file(`${this.options.rootFolder}${MAIN_FILENAME}`, mainXML); Object.keys(this.filesToSave).forEach(filename => { const data = this.filesToSave[filename]; zipWriter.file(`${this.options.rootFolder}${filename}`, data, { binary: typeof data !== 'string' }); }); return zipWriter.generateAsync({ type: 'uint8array' }); }); } getReferenceByEntity(entity) { return Object.values(this.xmlReferences).find(ref => ref.entity === entity); } getReferenceByXmlId(xmlId) { return this.xmlReferences[xmlId]; } getEntityByXmlId(xmlId) { var _a; if (!((_a = this.xmlReferences[xmlId]) === null || _a === void 0 ? void 0 : _a.entity)) { return null; } return this.xmlReferences[xmlId].entity; } getEntitiesOfTag(tag) { return Object.values(this.xmlReferences) .filter(ref => this.parseXmlId(ref.xmlId).tag === tag && ref.entity) .map(ref => ref.entity); } updateOptions(newOptions) { this.options = Object.assign(Object.assign({}, this.options), newOptions); // normalize root folder if (this.options.rootFolder && !this.options.rootFolder.endsWith('/')) { this.options.rootFolder += '/'; } } getParsedFile(filenameWithExtension, isBinary, addLetterCaseWarning = true) { if (!this.originalZip) { return null; } let file = null; this.originalZip.folder(this.options.rootFolder).forEach((relativePath, f) => { if (relativePath.toUpperCase() === filenameWithExtension.toUpperCase()) { if (relativePath !== filenameWithExtension && addLetterCaseWarning) { this.addWarning(`Letter case of filename ${filenameWithExtension} doesn't match (real file: ${relativePath})`); } file = f; } }); if (!file) { return null; } return file.async(isBinary ? 'uint8array' : 'string'); } addWarning(warning) { this.parsingWarnings.push(warning); } getWarnings() { return this.parsingWarnings; } } exports.ISOXMLManager = ISOXMLManager;