Skip to content

Commit

Permalink
Merge pull request #13018 from calixteman/xfa_bind
Browse files Browse the repository at this point in the history
XFA - Create Form DOM in merging template and data trees
  • Loading branch information
brendandahl authored Mar 10, 2021
2 parents 5e3af62 + 3243672 commit 3d4bb5e
Show file tree
Hide file tree
Showing 9 changed files with 1,582 additions and 107 deletions.
593 changes: 593 additions & 0 deletions src/core/xfa/bind.js

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/core/xfa/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
import {
$cleanup,
$finalize,
$nsAttributes,
$onChild,
$resolvePrototypes,
XFAObject,
Expand Down Expand Up @@ -88,6 +89,25 @@ class Builder {
this._addNamespacePrefix(prefixes);
}

if (attributes.hasOwnProperty($nsAttributes)) {
// Only support xfa-data namespace.
const dataTemplate = NamespaceSetUp.datasets;
const nsAttrs = attributes[$nsAttributes];
let xfaAttrs = null;
for (const [ns, attrs] of Object.entries(nsAttrs)) {
const nsToUse = this._getNamespaceToUse(ns);
if (nsToUse === dataTemplate) {
xfaAttrs = { xfa: attrs };
break;
}
}
if (xfaAttrs) {
attributes[$nsAttributes] = xfaAttrs;
} else {
delete attributes[$nsAttributes];
}
}

const namespaceToUse = this._getNamespaceToUse(nsPrefix);
const node =
(namespaceToUse && namespaceToUse[$buildXFAObject](name, attributes)) ||
Expand Down
23 changes: 14 additions & 9 deletions src/core/xfa/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
* limitations under the License.
*/

import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
import {
$appendChild,
$global,
$namespaceId,
$nodeName,
$onChildCheck,
$onChild,
XFAObject,
XmlObject,
} from "./xfa_object.js";
import { $buildXFAObject, NamespaceIds } from "./namespaces.js";

const DATASETS_NS_ID = NamespaceIds.datasets.id;

Expand All @@ -37,15 +39,18 @@ class Datasets extends XFAObject {
this.Signature = null;
}

[$onChildCheck](child) {
[$onChild](child) {
const name = child[$nodeName];
if (name === "data") {
return child[$namespaceId] === DATASETS_NS_ID;
}
if (name === "Signature") {
return child[$namespaceId] === NamespaceIds.signature.id;
if (
(name === "data" && child[$namespaceId] === DATASETS_NS_ID) ||
(name === "Signature" &&
child[$namespaceId] === NamespaceIds.signature.id)
) {
this[name] = child;
} else {
child[$global] = true;
}
return false;
this[$appendChild](child);
}
}

Expand Down
33 changes: 33 additions & 0 deletions src/core/xfa/factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright 2021 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.
*/

import { Binder } from "./bind.js";
import { XFAParser } from "./parser.js";

class XFAFactory {
constructor(data) {
this.root = new XFAParser().parse(XFAFactory._createDocument(data));
this.form = new Binder(this.root).bind();
}

static _createDocument(data) {
if (!data["/xdp:xdp"]) {
return data["xdp:xdp"];
}
return Object.values(data).join("");
}
}

export { XFAFactory };
29 changes: 26 additions & 3 deletions src/core/xfa/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
* limitations under the License.
*/

import { $clean, $finalize, $onChild, $onText, $setId } from "./xfa_object.js";
import {
$clean,
$finalize,
$nsAttributes,
$onChild,
$onText,
$setId,
} from "./xfa_object.js";
import { XMLParserBase, XMLParserErrorCode } from "../xml_parser.js";
import { Builder } from "./builder.js";
import { warn } from "../../shared/util.js";
Expand Down Expand Up @@ -57,7 +64,7 @@ class XFAParser extends XMLParserBase {
// namespaces information.
let namespace = null;
let prefixes = null;
const attributeObj = Object.create(null);
const attributeObj = Object.create({});
for (const { name, value } of attributes) {
if (name === "xmlns") {
if (!namespace) {
Expand All @@ -72,7 +79,23 @@ class XFAParser extends XMLParserBase {
}
prefixes.push({ prefix, value });
} else {
attributeObj[name] = value;
const i = name.indexOf(":");
if (i === -1) {
attributeObj[name] = value;
} else {
// Attributes can have their own namespace.
// For example in data, we can have <foo xfa:dataNode="dataGroup"/>
let nsAttrs = attributeObj[$nsAttributes];
if (!nsAttrs) {
nsAttrs = attributeObj[$nsAttributes] = Object.create(null);
}
const [ns, attrName] = [name.slice(0, i), name.slice(i + 1)];
let attrs = nsAttrs[ns];
if (!attrs) {
attrs = nsAttrs[ns] = Object.create(null);
}
attrs[attrName] = value;
}
}
}

Expand Down
124 changes: 101 additions & 23 deletions src/core/xfa/som.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
*/

import {
$appendChild,
$getChildrenByClass,
$getChildrenByName,
$getParent,
$namespaceId,
XFAObject,
XFAObjectArray,
XmlObject,
} from "./xfa_object.js";
import { warn } from "../../shared/util.js";

Expand All @@ -33,17 +36,18 @@ const operators = {
};

const shortcuts = new Map([
["$data", root => root.datasets.data],
["$template", root => root.template],
["$connectionSet", root => root.connectionSet],
["$form", root => root.form],
["$layout", root => root.layout],
["$host", root => root.host],
["$dataWindow", root => root.dataWindow],
["$event", root => root.event],
["!", root => root.datasets],
["$xfa", root => root],
["xfa", root => root],
["$data", (root, current) => root.datasets.data],
["$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();
Expand Down Expand Up @@ -138,17 +142,24 @@ function parseExpression(expr, dotDotAllowed) {
return parsed;
}

function searchNode(root, container, expr, dotDotAllowed = true) {
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)];
root = [fn(root, container)];
i = 1;
} else {
isQualified = container === null;
Expand All @@ -163,13 +174,17 @@ function searchNode(root, container, expr, dotDotAllowed = true) {
continue;
}

let cached = somCache.get(node);
if (!cached) {
cached = new Map();
somCache.set(node, cached);
let children, cached;

if (useCache) {
cached = somCache.get(node);
if (!cached) {
cached = new Map();
somCache.set(node, cached);
}
children = cached.get(cacheName);
}

let children = cached.get(cacheName);
if (!children) {
switch (operator) {
case operators.dot:
Expand All @@ -189,7 +204,9 @@ function searchNode(root, container, expr, dotDotAllowed = true) {
default:
break;
}
cached.set(cacheName, children);
if (useCache) {
cached.set(cacheName, children);
}
}

if (children.length > 0) {
Expand Down Expand Up @@ -222,11 +239,72 @@ function searchNode(root, container, expr, dotDotAllowed = true) {
return null;
}

if (root.length === 1) {
return root[0];
return root;
}

function createNodes(root, path) {
let node = null;
for (const { name, index } of path) {
for (let i = 0; i <= index; i++) {
node = new XmlObject(root[$namespaceId], name);
root[$appendChild](node);
}

root = node;
}
return node;
}

return root;
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 { cacheName, index } = parsed[i];
if (!isFinite(index)) {
parsed[i].index = 0;
return createNodes(root, parsed.slice(i));
}

const cached = somCache.get(root);
if (!cached) {
warn(`XFA - createDataNode must be called after searchNode.`);
return null;
}

const children = cached.get(cacheName);
if (children.length === 0) {
return createNodes(root, parsed.slice(i));
}

if (index < children.length) {
const child = children[index];
if (!(child instanceof XFAObject)) {
warn(`XFA - Cannot create a node.`);
return null;
}
root = child;
} else {
parsed[i].index = children.length - index;
return createNodes(root, parsed.slice(i));
}
}
return null;
}

export { searchNode };
export { createDataNode, searchNode };
Loading

0 comments on commit 3d4bb5e

Please sign in to comment.