isoxml-angular
Version:
JavaScript library to parse and generate ISOXML (ISO11783-10) files
236 lines (235 loc) • 10.4 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());
});
};
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;