/** * @licstart The following is the entire license notice for the * JavaScript code in this page * * Copyright 2022 Mozilla Foundation * * 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. * * @licend The above is the entire license notice for the * JavaScript code in this page */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createDataNode = createDataNode; exports.searchNode = searchNode; var _xfa_object = require("./xfa_object.js"); var _namespaces = require("./namespaces.js"); var _util = require("../../shared/util.js"); const namePattern = /^[^.[]+/; const indexPattern = /^[^\]]+/; const operators = { dot: 0, dotDot: 1, dotHash: 2, dotBracket: 3, dotParen: 4 }; const shortcuts = new Map([["$data", (root, current) => root.datasets ? root.datasets.data : root], ["$record", (root, current) => (root.datasets ? root.datasets.data : root)[_xfa_object.$getChildren]()[0]], ["$template", (root, current) => root.template], ["$connectionSet", (root, current) => root.connectionSet], ["$form", (root, current) => root.form], ["$layout", (root, current) => root.layout], ["$host", (root, current) => root.host], ["$dataWindow", (root, current) => root.dataWindow], ["$event", (root, current) => root.event], ["!", (root, current) => root.datasets], ["$xfa", (root, current) => root], ["xfa", (root, current) => root], ["$", (root, current) => current]]); const somCache = new WeakMap(); const NS_DATASETS = _namespaces.NamespaceIds.datasets.id; function parseIndex(index) { index = index.trim(); if (index === "*") { return Infinity; } return parseInt(index, 10) || 0; } function parseExpression(expr, dotDotAllowed, noExpr = true) { let match = expr.match(namePattern); if (!match) { return null; } let [name] = match; const parsed = [{ name, cacheName: "." + name, index: 0, js: null, formCalc: null, operator: operators.dot }]; let pos = name.length; while (pos < expr.length) { const spos = pos; const char = expr.charAt(pos++); if (char === "[") { match = expr.slice(pos).match(indexPattern); if (!match) { (0, _util.warn)("XFA - Invalid index in SOM expression"); return null; } parsed.at(-1).index = parseIndex(match[0]); pos += match[0].length + 1; continue; } let operator; switch (expr.charAt(pos)) { case ".": if (!dotDotAllowed) { return null; } pos++; operator = operators.dotDot; break; case "#": pos++; operator = operators.dotHash; break; case "[": if (noExpr) { (0, _util.warn)("XFA - SOM expression contains a FormCalc subexpression which is not supported for now."); return null; } operator = operators.dotBracket; break; case "(": if (noExpr) { (0, _util.warn)("XFA - SOM expression contains a JavaScript subexpression which is not supported for now."); return null; } operator = operators.dotParen; break; default: operator = operators.dot; break; } match = expr.slice(pos).match(namePattern); if (!match) { break; } [name] = match; pos += name.length; parsed.push({ name, cacheName: expr.slice(spos, pos), operator, index: 0, js: null, formCalc: null }); } return parsed; } function searchNode(root, container, expr, dotDotAllowed = true, useCache = true) { const parsed = parseExpression(expr, dotDotAllowed); if (!parsed) { return null; } const fn = shortcuts.get(parsed[0].name); let i = 0; let isQualified; if (fn) { isQualified = true; root = [fn(root, container)]; i = 1; } else { isQualified = container === null; root = [container || root]; } for (let ii = parsed.length; i < ii; i++) { const { name, cacheName, operator, index } = parsed[i]; const nodes = []; for (const node of root) { if (!(node instanceof _xfa_object.XFAObject)) { continue; } let children, cached; if (useCache) { cached = somCache.get(node); if (!cached) { cached = new Map(); somCache.set(node, cached); } children = cached.get(cacheName); } if (!children) { switch (operator) { case operators.dot: children = node[_xfa_object.$getChildrenByName](name, false); break; case operators.dotDot: children = node[_xfa_object.$getChildrenByName](name, true); break; case operators.dotHash: children = node[_xfa_object.$getChildrenByClass](name); if (children instanceof _xfa_object.XFAObjectArray) { children = children.children; } else { children = [children]; } break; default: break; } if (useCache) { cached.set(cacheName, children); } } if (children.length > 0) { nodes.push(children); } } if (nodes.length === 0 && !isQualified && i === 0) { const parent = container[_xfa_object.$getParent](); container = parent; if (!container) { return null; } i = -1; root = [container]; continue; } if (isFinite(index)) { root = nodes.filter(node => index < node.length).map(node => node[index]); } else { root = nodes.flat(); } } if (root.length === 0) { return null; } return root; } function createNodes(root, path) { let node = null; for (const { name, index } of path) { for (let i = 0, ii = !isFinite(index) ? 0 : index; i <= ii; i++) { const nsId = root[_xfa_object.$namespaceId] === NS_DATASETS ? -1 : root[_xfa_object.$namespaceId]; node = new _xfa_object.XmlObject(nsId, name); root[_xfa_object.$appendChild](node); } root = node; } return node; } function createDataNode(root, container, expr) { const parsed = parseExpression(expr); if (!parsed) { return null; } if (parsed.some(x => x.operator === operators.dotDot)) { return null; } const fn = shortcuts.get(parsed[0].name); let i = 0; if (fn) { root = fn(root, container); i = 1; } else { root = container || root; } for (let ii = parsed.length; i < ii; i++) { const { name, operator, index } = parsed[i]; if (!isFinite(index)) { parsed[i].index = 0; return createNodes(root, parsed.slice(i)); } let children; switch (operator) { case operators.dot: children = root[_xfa_object.$getChildrenByName](name, false); break; case operators.dotDot: children = root[_xfa_object.$getChildrenByName](name, true); break; case operators.dotHash: children = root[_xfa_object.$getChildrenByClass](name); if (children instanceof _xfa_object.XFAObjectArray) { children = children.children; } else { children = [children]; } break; default: break; } if (children.length === 0) { return createNodes(root, parsed.slice(i)); } if (index < children.length) { const child = children[index]; if (!(child instanceof _xfa_object.XFAObject)) { (0, _util.warn)(`XFA - Cannot create a node.`); return null; } root = child; } else { parsed[i].index = index - children.length; return createNodes(root, parsed.slice(i)); } } return null; }