UNPKG

diffusion

Version:

Diffusion JavaScript client

489 lines (488 loc) 15.7 kB
"use strict"; /** * @module diffusion.datatypes */ var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JSONPointerMap = exports.EntryIterator = exports.Entry = void 0; var errors_1 = require("./../../../errors/errors"); var json_pointer_1 = require("./../../data/json/json-pointer"); var require_non_null_1 = require("./../../util/require-non-null"); /** * Returned through {@link JSONPointerMap.getEntry} and iterators. * * Following a sequence of put operations, all entries will have a value, or * children, or both. Through the {@link Entry.removeDescendants()} method, * it is possible for an entry to have neither a value nor children. */ var Entry = /** @class */ (function () { /** * * * @param segment the segment of the entry, `null` if the entry is the root entry * @param pointer the pointer to the location if the entry * @param context the map that this entry belongs to */ function Entry(segment, pointer, context) { this.segment = segment; this.pointer = pointer; this.children = []; this.value = null; this.context = context; } /** * Recursively count the number of entries in the tree * * @return the number of entries that contain a value */ Entry.prototype.count = function () { var e_1, _a; var result = this.value ? 1 : 0; try { for (var _b = __values(this.children), _c = _b.next(); !_c.done; _c = _b.next()) { var child = _c.value; result += child.count(); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return result; }; /** * Add a child entry * * @param s the segment of the new entry, relative to this entry's segment * @return the newly created child */ Entry.prototype.addChild = function (s) { var c = new Entry(s, this.pointer.withSegment(s), this.context); this.children.push(c); return c; }; /** * Find an immediate child by the segment * * @param s the segment to search for * @return the child that matches the segment, `null` if not child was found */ Entry.prototype.findChild = function (s) { var e_2, _a; try { for (var _b = __values(this.children), _c = _b.next(); !_c.done; _c = _b.next()) { var child = _c.value; if (s.equals(child.segment)) { return child; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } return null; }; /** * Set the value of the entry * * @param newValue the new value of the entry * @return the old value of the entry */ Entry.prototype.setValue = function (newValue) { var previous = this.value; if (previous === null) { this.context.size++; } this.value = newValue; return previous; }; /** * Remove all descendants of the entry * * @return the total number of removed descendants that contained a value */ Entry.prototype.removeDescendants = function () { var e_3, _a; var removed = 0; try { for (var _b = __values(this.children), _c = _b.next(); !_c.done; _c = _b.next()) { var child = _c.value; removed += child.count(); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_3) throw e_3.error; } } this.children = []; this.context.size -= removed; return removed; }; /** * Calculate the number of direct children that contain a value * * @return the number of children with a value */ Entry.prototype.numberOfChildren = function () { var e_4, _a; var result = 0; try { for (var _b = __values(this.children), _c = _b.next(); !_c.done; _c = _b.next()) { var child = _c.value; result += (child.value ? 1 : 0); } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_4) throw e_4.error; } } return result; }; /** * Convert the entry to a string * * @returns a string representation of the entry */ Entry.prototype.toString = function () { return this.pointer + '=' + this.value; }; return Entry; }()); exports.Entry = Entry; /** * A state for the post-order iterator over {@link JSONPointerMap} entries. */ var PostOrderState = /** @class */ (function () { /** * Create a post-order iterator state with the current map entry * * @param node the current map entry */ function PostOrderState(node) { /** * The next child index to return */ this.nextChildIndex = 0; this.node = node; } /** * Get the next child * * @return the next child, `null` if there are no more children */ PostOrderState.prototype.nextChild = function () { var children = this.node.children; if (this.nextChildIndex >= children.length) { return null; } return children[this.nextChildIndex++]; }; return PostOrderState; }()); /** * An iterator over {@link JSONPointerMap} entries */ var EntryIterator = /** @class */ (function () { /** * Create an entry iterator * * @param nextWithValue a function that returns the next entry with a value */ function EntryIterator(nextWithValue) { this.nextEntry = nextWithValue(); this.nextWithValue = nextWithValue; } /** * Check if there is another entry * * @return `true` if the iterator can return a non-null next entry */ EntryIterator.prototype.hasNext = function () { return this.nextEntry !== null; }; /** * Get the next entry * * @return the next entry * @throws a {@link InternalError} if no next entry exists */ EntryIterator.prototype.next = function () { if (this.nextEntry === null) { throw new errors_1.InternalError('No such element'); } var result = this.nextEntry; this.nextEntry = this.nextWithValue(); return result; }; return EntryIterator; }()); exports.EntryIterator = EntryIterator; /** * A hierarchical map of {@link JSONPointer}s. */ var JSONPointerMap = /** @class */ (function () { function JSONPointerMap() { /** * The root pointer */ this.root = new Entry(null, json_pointer_1.ROOT, this); /** * The number of entries, not counting the root */ this.size = 0; } /** * Add an entry. * * By design, no constraints are applied to the supplied pointer. E.g. it is * possible to add 'x/foo' followed by 'x/1', even though these * cannot be valid paths in a single document. This supports the structural * diff use case where we might 'add('x/foo', REMOVE)', then 'add('x/1', INSERT)'. * * @param pointer the pointer to the location that the value is stored * @param value may not be null * @returns the previous value * @throws a {@link NullValueError} if value is `null` or `undefined` */ JSONPointerMap.prototype.put = function (pointer, value) { require_non_null_1.requireNonNull(value, 'value'); var node = this.root; var i = 0; for (; i < pointer.segments.length;) { var s = pointer.segments[i++]; var c = node.findChild(s); if (c === null) { node = node.addChild(s); break; } node = c; } for (; i < pointer.segments.length; i++) { node = node.addChild(pointer.segments[i]); } return node.setValue(value); }; /** * Check if the map contains value for a given pointer * * @param pointer the pointer to check * @returns `true` if this map has a value for pointer */ JSONPointerMap.prototype.contains = function (pointer) { return this.get(pointer) !== null; }; /** * Get the value at a given position * * @param pointer the pointer to the location that the value is stored * @returns the element for pointer, or null if there is no value; */ JSONPointerMap.prototype.get = function (pointer) { var entry = this.getEntry(pointer); if (entry) { return entry.value; } return null; }; /** * Returns the entry for pointer. Entries reflect the tree structure, and might not have a value. * * @param pointer the pointer to the location that the entry is stored * @returns the entry for pointer, or null if there is no entry */ JSONPointerMap.prototype.getEntry = function (pointer) { var e_5, _a; var result = this.root; try { for (var _b = __values(pointer.segments), _c = _b.next(); !_c.done; _c = _b.next()) { var segment = _c.value; result = result.findChild(segment); if (result === null) { return null; } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_5) throw e_5.error; } } return result; }; /** * Get the descendants of a given location. * * The resulting map contains only the path segments of the `pointer` * together with all direct and indirect descendant entries of the * `pointer`'s last segment. * * @param pointer the pointer to check * @returns a pointer map containing only the descendant nodes */ JSONPointerMap.prototype.descendants = function (pointer) { var result = new JSONPointerMap(); var thisNode = this.root; var resultNode = result.root; var segments = pointer.segments; var length = segments.length; if (length === 0) { return this; } for (var i = 0; i < length - 1; ++i) { var s = segments[i]; thisNode = thisNode.findChild(s); if (thisNode === null) { /* tslint:disable-next-line:no-use-before-declare */ return EMPTY; } resultNode = resultNode.addChild(s); } var last = thisNode.findChild(segments[length - 1]); if (last === null) { /* tslint:disable-next-line:no-use-before-declare */ return EMPTY; } resultNode.children.push(last); result.size = last.count(); return result; }; /** * Get the intersection of the pointer map with a given pointer * * @param pointer the pointer to check * @returns a pointer map containing only the nodes along the segments * specified by the argument */ JSONPointerMap.prototype.intersection = function (pointer) { var result = new JSONPointerMap(); var thisNode = this.root; var resultNode = result.root; var segments = pointer.segments; var length = segments.length; if (length === 0) { return this; } if (thisNode.value !== null) { resultNode.setValue(thisNode.value); } for (var i = 0; i < length - 1; ++i) { var s = segments[i]; thisNode = thisNode.findChild(s); if (thisNode === null) { return result; } resultNode = resultNode.addChild(s); if (thisNode.value !== null) { resultNode.setValue(thisNode.value); } } var last = thisNode.findChild(segments[length - 1]); if (last === null) { return result; } resultNode.children.push(last); result.size += last.count(); return result; }; /** * Return a pre-order iterator over the entries * * @return a pre-order {@link EntryIterator} over the map's entries */ JSONPointerMap.prototype.iterator = function () { var stack = [this.root]; return new EntryIterator(function () { for (;;) { var r = stack.shift(); if (r === undefined) { return null; } var n = r.children.length; for (; n > 0; --n) { stack.unshift(r.children[n - 1]); } if (r.value !== null) { return r; } } }); }; /** * Return a post-order iterator over the entries * * @return a post-order {@link EntryIterator} over the map's entries */ JSONPointerMap.prototype.postOrder = function () { var stack = [new PostOrderState(this.root)]; return new EntryIterator(function () { for (;;) { var r = stack[0]; /* tslint:disable-next-line:strict-type-predicates */ if (r === undefined) { return null; } var c = r.nextChild(); if (c === null) { stack.shift(); if (r.node.value !== null) { return r.node; } } else if (c.children.length === 0) { return c; } else { stack.unshift(new PostOrderState(c)); } } }); }; /** * Convert the JSON pointer map to a string * * @returns a string representation of the pointer map */ JSONPointerMap.prototype.toString = function () { var parts = ['{']; var iter = this.iterator(); while (iter.hasNext()) { if (parts.length > 1) { parts.push(', '); } parts.push(iter.next()); } parts.push('}'); return parts.join(''); }; return JSONPointerMap; }()); exports.JSONPointerMap = JSONPointerMap; /** * An empty {@link JSONPointerMap} */ var EMPTY = new JSONPointerMap();