diffusion
Version:
Diffusion JavaScript client
489 lines (488 loc) • 15.7 kB
JavaScript
"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();