UNPKG

postchain-client

Version:

Client library for accessing a Postchain node through REST.

239 lines 8.72 kB
"use strict"; var util = require('../formatter'); const { Buffer } = require('buffer'); var PathSet = require('./path').PathSet; var PathLeafElement = require('./path').PathLeafElement; var EmptyLeaf = require('./binarytree').EmptyLeaf; var Leaf = require('./binarytree').Leaf; var Node = require('./binarytree').Node; var BinaryTree = require('./binarytree').BinaryTree; var ArrayHeadNode = require('./binarytree').ArrayHeadNode; var DictHeadNode = require('./binarytree').DictHeadNode; const NO_PATHS = new PathSet([]); /** * The factory does the conversion between list of elements and tree of elements. * * Note: The idea is that you should sub class for each type of element you want to build. */ function BinaryTreeFactory() { } /** * Transforms the incoming leaf into an [BinaryTreeElement] * The idea with this function is that it can be recursive (if the leaf in turn is complex object with sub objects). * * Note: If we don't have a path here we can try to find the leaf in the cache. * * @param leaf the raw data we should wrap in a leaf * @param paths a collection of proof paths that might point to this leaf * @return the resulting [BinaryTreeElement] the leaf got converted to */ BinaryTreeFactory.prototype.handleLeaf = function (leaf, paths, isRoot = false) { if (paths.length == 0 && !isRoot) { return this.innerHandleLeaf(leaf, this.getEmptyPathSet()); } else { return this.innerHandleLeaf(leaf, paths); } }; /** * */ BinaryTreeFactory.prototype.getEmptyPathSet = function () { return NO_PATHS; }; /** * At this point we should have looked in cache. * * @param leaf we should turn into a tree element * @param {PathSet} paths * @return the tree element we created. */ BinaryTreeFactory.prototype.innerHandleLeaf = function (leaf, paths) { if (leaf == null) { return this.handlePrimitiveLeaf(leaf, paths); } if (Buffer.isBuffer(leaf)) { return this.handlePrimitiveLeaf(leaf, paths); } if (typeof leaf === 'string') { return this.handlePrimitiveLeaf(leaf, paths); } if (typeof leaf === 'number') { return this.handlePrimitiveLeaf(leaf, paths); } if (typeof leaf === 'bigint') { return this.handlePrimitiveLeaf(leaf, paths); } if (typeof leaf === 'boolean') { return this.handlePrimitiveLeaf(leaf ? 1 : 0, paths); } if (leaf.constructor === Array) { return this.buildFromArray(leaf, paths); } if (typeof leaf === 'object') { return this.buildFromDictionary(leaf, paths); } else { throw new Error("Unsupported data type"); } }; /** * Just like [handleLeaf] but we know that this leaf should not be a complex type, but something we can * immediately wrap * * @param leaf * @param {PathSet} paths */ BinaryTreeFactory.prototype.handlePrimitiveLeaf = function (leaf, paths) { var pathElem = paths.getPathLeafOrElseAnyCurrentPathElement(); if (pathElem != null && !(pathElem instanceof PathLeafElement)) { throw new Error("Path does not match the tree structure. We are at a leaf " + leaf + " but found path element " + pathElem); } return new Leaf(leaf, pathElem); }; /** * Calls itself until the return value only holds 1 element * * Note: This method can only create standard [Node] that fills up the area between the "top" and the leaves. * These "in-between" nodes cannot be "path leaf" or have any interesting properties. * * @param layer What layer we aim calculate * @param inList The args of nodes we should build from * @return All [BinaryTreeElement] nodes of the next layer */ BinaryTreeFactory.prototype.buildHigherLayer = function (layer, inList) { if (inList.length === 0) { throw new Error("Cannot work on empty arrays. Layer: " + layer); } else if (inList.length === 1) { return inList; } var returnArray = new Array(); var nrOfNodesToCreate = Math.floor(inList.length / 2); var leftValue = null; var isLeft = true; for (var i = 0; i < inList.length; i++) { if (isLeft) { leftValue = inList[i]; isLeft = false; } else { var tempNode = new Node(leftValue, inList[i]); returnArray.push(tempNode); nrOfNodesToCreate--; isLeft = true; leftValue = null; } } if (!isLeft) { // If there is odd number of nodes, then move the last node up one level returnArray.push(leftValue); } // Extra check if (nrOfNodesToCreate != 0) { util.logDebug("Why didn't we build exactly the correct amount? Layer: " + layer + " , residue: " + nrOfNodesToCreate + " , input args size: " + inList.length + "."); } return this.buildHigherLayer((layer + 1), returnArray); }; BinaryTreeFactory.prototype.build = function (data) { return this.buildWithPath(data, NO_PATHS); }; /** * @param {PathSet} paths */ BinaryTreeFactory.prototype.buildWithPath = function (data, paths) { var result = this.handleLeaf(data, paths, true); return new BinaryTree(result); }; /** * @param {Array} array * @param {PathSet} paths */ BinaryTreeFactory.prototype.buildFromArray = function (array, paths) { var pathElem = paths.getPathLeafOrElseAnyCurrentPathElement(); // 1. Build leaf layer if (array.length == 0) { return new ArrayHeadNode(new EmptyLeaf(), new EmptyLeaf(), array, 0, 0, pathElem); } var leafArray = this.buildLeafElements(array, paths); // 2. Build all higher layers var result = this.buildHigherLayer(1, leafArray); // 3. Fix and return the root node var orgRoot = result[0]; if (orgRoot instanceof Node) { return new ArrayHeadNode(orgRoot.left, orgRoot.right, array, array.length, pathElem); } if (orgRoot instanceof Leaf) { return this.buildFromOneLeaf(array, orgRoot, pathElem); } else { throw new Error("Should not find element of this type here"); } }; /** * */ BinaryTreeFactory.prototype.buildFromOneLeaf = function (array, orgRoot, pathElem) { if (array.length > 1) { throw new Error("How come we got a leaf returned when we had " + array.length + " elements is the args?"); } else { return new ArrayHeadNode(orgRoot, new EmptyLeaf(), array, array.length, pathElem); } }; /** * @param {PathSet} paths */ BinaryTreeFactory.prototype.buildLeafElements = function (leafList, paths) { var leafArray = new Array(); var onlyArrayPaths = paths.keepOnlyArrayPaths(); // For performance, since we will loop soon for (var i = 0; i < leafList.length; i++) { var pathsRelevantForThisLeaf = onlyArrayPaths.getTailIfFirstElementIsArrayOfThisIndexFromList(i); var leaf = leafList[i]; var binaryTreeElement = this.handleLeaf(leaf, pathsRelevantForThisLeaf); leafArray.push(binaryTreeElement); } return leafArray; }; /** * @param {PathSet} paths */ BinaryTreeFactory.prototype.buildFromDictionary = function (dict, paths) { var pathElem = paths.getPathLeafOrElseAnyCurrentPathElement(); // Needs to be sorted, or else the order is undefined var keys = Object.keys(dict).sort(); if (keys.length == 0) { return new DictHeadNode(new EmptyLeaf(), new EmptyLeaf(), dict, keys.length, 0, pathElem); } // 1. Build first (leaf) layer var leafArray = this.buildLeafElementFromDict(keys, dict, paths); // 2. Build all higher layers var result = this.buildHigherLayer(1, leafArray); // 3. Fix and return the root node var orgRoot = result[0]; if (orgRoot instanceof Node) { return new DictHeadNode(orgRoot.left, orgRoot.right, dict, keys.length, pathElem); } else { throw new Error("Should not find element of this type here: " + typeof orgRoot); } }; /** * @param {PathSet} paths */ BinaryTreeFactory.prototype.buildLeafElementFromDict = function (keys, dict, paths) { var leafArray = new Array(); var onlyDictPaths = paths.keepOnlyDictPaths(); // For performance, since we will loop soon for (var i = 0; i < keys.length; i++) { // The key cannot not be proved, so NO_PATHS var key = keys[i]; var keyElement = this.handleLeaf(key, NO_PATHS); leafArray.push(keyElement); var content = dict[key]; var pathsRelevantForThisLeaf = onlyDictPaths.getTailIfFirstElementIsDictOfThisKeyFromList(key); var contentElement = this.handleLeaf(content, pathsRelevantForThisLeaf); leafArray.push(contentElement); } return leafArray; }; module.exports = { BinaryTreeFactory }; //# sourceMappingURL=binarytreefactory.js.map