postchain-client
Version:
Client library for accessing a Postchain node through REST.
239 lines • 8.72 kB
JavaScript
"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