diffusion
Version:
Diffusion JavaScript client
406 lines (405 loc) • 13.1 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.ROOT = exports.JSONPointer = exports.KeySegment = exports.IndexSegment = void 0;
var errors_1 = require("./../../../errors/errors");
var math_1 = require("./../../util/math");
/**
* JSONPointer segment representing an index
*/
var IndexSegment = /** @class */ (function () {
/**
* Create a new IndexSegment
*
* @param index the index of the segment
*/
function IndexSegment(index) {
this.index = index;
}
/**
* Compare two segments.
*
* Allows comparison of the IndexSegment with another IndexSegment or a
* {@link KeySegment}.
*
* @return 'true' if the other segment is an IndexSegment and the indexes are equal
*/
IndexSegment.prototype.equals = function (other) {
if (other === this) {
return true;
}
if (!other || !(other instanceof IndexSegment)) {
return false;
}
return other.index === this.index;
};
/**
* Convert the index segment to a string
*
* @returns a string representation of the segment
*/
IndexSegment.prototype.toString = function () {
return '/' + this.index;
};
return IndexSegment;
}());
exports.IndexSegment = IndexSegment;
var INDEX0 = new IndexSegment(0);
/**
* JSONPointer segment representing a key
* @param key
* @constructor
*/
var KeySegment = /** @class */ (function () {
/**
* Create a new KeySegment
*
* @param key the key of the segment
*/
function KeySegment(key) {
this.expression = '/' + escapeKey(key);
}
/**
* Compare two segments.
*
* Allows comparison of the KeySegment with another KeySegment or a
* {@link IndexSegment}.
*
* @return 'true' if the other segment is an KeySegment and the keys are equal
*/
KeySegment.prototype.equals = function (other) {
if (other === this) {
return true;
}
if (!other || !(other instanceof KeySegment)) {
return false;
}
return other.expression === this.expression;
};
/**
* Convert the key segment to a string
*
* @returns a string representation of the segment
*/
KeySegment.prototype.toString = function () {
return this.expression;
};
return KeySegment;
}());
exports.KeySegment = KeySegment;
/**
* A parser for parsing JSON pointer string expressions to JSON pointer objects
*/
var Parser = /** @class */ (function () {
/**
* Create a new parser
*
* @param expression the expression to be parsed
* @throws an {@link IllegalArgumentError} if the expression is not correctly formatted
*/
function Parser(expression) {
// each iteration leaves p at the next separator
this.pos = 0;
if (expression !== '' && expression.charAt(0) !== '/') {
throw new errors_1.IllegalArgumentError('JSON Pointer expression must be empty or start with \'/\' : ' + expression);
}
this.expression = expression;
}
/**
* Get the next segment string
*
* @return a string containing the next segment
*/
Parser.prototype.nextSegmentString = function () {
var start = this.pos;
for (; this.pos < this.expression.length; ++this.pos) {
var c = this.expression[this.pos];
if (c === '/') {
break;
}
else if (c === '~') {
var parts = [this.expression.substring(start, this.pos)];
var length_1 = this.expression.length;
for (; this.pos < length_1; ++this.pos) {
var c1 = this.expression.charAt(this.pos);
if (c1 === '/') {
break;
}
else if (c1 === '~' && this.pos !== length_1 - 1) {
++this.pos;
var c2 = this.expression.charAt(this.pos);
if (c2 === '0') {
parts.push('~');
}
else if (c2 === '1') {
parts.push('/');
}
else {
parts.push('~');
parts.push(c2);
}
}
else {
parts.push(c1);
}
}
return parts.join('');
}
}
return this.expression.substring(start, this.pos);
};
/**
* Create an index segment, if the string contains an index.
*
* The index must be the decimal representation of a positive 32 bit integer
* without leading `0` or `+`
*
* @return a new index segment if the string contained an index, otherwise `null`
*/
Parser.prototype.maybeIndex = function (s) {
var digits = s.length;
// numbers with more than ten digits are larger than MAX_INT32
if (digits === 0 || digits > 10) {
return null;
}
for (var i = 0; i < digits; ++i) {
var c = s.charAt(i);
if (c < '0' || c > '9') {
return null;
}
if (c === '0' && i === 0) {
// no leading zeroes allowed
return digits === 1 ? INDEX0 : null;
}
}
var index = parseInt(s, 10);
// cap at max int32
if (index > math_1.MAX_INT32) {
return null;
}
return new IndexSegment(index);
};
/**
* Get the next segment
*
* @return a key segment or an index segment. `null` if there are no more segments
*/
Parser.prototype.next = function () {
if (this.pos === this.expression.length) {
return null;
}
++this.pos;
var s = this.nextSegmentString();
var is = this.maybeIndex(s);
if (is !== null) {
return is;
}
return new KeySegment(s);
};
return Parser;
}());
/**
* JSON Pointer implementation that allows incremental building of pointers.
*
* @param nodes - Segments from parent context
* @constructor
* @since 5.9
*/
var JSONPointer = /** @class */ (function () {
/**
* Create a JSONPointer
*
* @param segments segments from parent context
*/
function JSONPointer(segments) {
this.segments = segments;
}
/**
* Parse a string containing a JSON pointer into a JSONPointer object.
*
* A segment that has only decimal digits will be interpreted as an index unless it has leading zeros
* (RFC 6901, section4), or its value is larger than Integer.MAX_VALUE
*
* @param expression a string containing the JSON pointer expression. If a JSONPointer object
* is supplied, then it will be returned unchanged
* @returns a JSONPointer that corresponds to the expression
* @throws an {@link IllegalArgumentError} if the expression is not correctly formatted
*/
JSONPointer.parse = function (expression) {
if (expression instanceof JSONPointer) {
return expression;
}
/* tslint:disable-next-line:no-use-before-declare */
var result = exports.ROOT;
var segmentParser = new Parser(expression);
// eslint-disable-next-line no-constant-condition
while (true) {
var s = segmentParser.next();
if (s === null) {
return result;
}
result = result.withSegment(s);
}
};
/**
* Append a key segment to the pointer
*
* @return a new instance equal to this instance with key appended
*/
JSONPointer.prototype.withKey = function (key) {
return this.withSegment(new KeySegment(key));
};
/**
* Append a index segment to the pointer
*
* @return a new instance equal to this instance with index appended
*/
JSONPointer.prototype.withIndex = function (index) {
return this.withSegment(new IndexSegment(index));
};
/**
* Append a segment to the pointer
*
* @return a new instance equal to this instance with segment appended
*/
JSONPointer.prototype.withSegment = function (newNode) {
var newNodes = this.segments.concat(newNode);
return new JSONPointer(newNodes);
};
/**
* Returns whether this JSONPointer is equal to another JSONPointer,
* ignoring the values of array index segments.
*
* This implements an equivalence relation between JSONPointers that
* considers two values equal if they have the same number of segments,
* corresponding segments are of the same type, and corresponding key
* segments are equal. It is used by the structural delta code to identify
* potential matches between two pointers to different values.
*
* @param other the other pointer to compare to
* @returns whether the other pointer is equal (ignoring indexes)
*/
JSONPointer.prototype.equalIgnoringIndexes = function (other) {
if (other === this) {
return true;
}
var length = this.segments.length;
if (length !== other.segments.length) {
return false;
}
for (var i = 0; i < length; ++i) {
var s = this.segments[i];
var o = other.segments[i];
if (s instanceof IndexSegment) {
if (o instanceof KeySegment) {
return false;
}
}
else if (!s.equals(o)) {
return false;
}
}
return true;
};
/**
* Convert the JSON pointer to a string
*
* @returns the JSONPointer expression
*/
JSONPointer.prototype.toString = function () {
var e_1, _a;
var parts = [];
try {
for (var _b = __values(this.segments), _c = _b.next(); !_c.done; _c = _b.next()) {
var segment = _c.value;
parts.push(segment.toString());
}
}
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 parts.join('');
};
/**
* Compare two JSONPointer objects
*
* Non-equal JSONPointer instances can have the same expression.
* E.g. ROOT.withIndex(1) is not equal to ROOT.withKey('1'), but both have the same expression '/1'
*
* @param other the other pointer instance to compare to
* @returns whether the two instances are equal
*/
JSONPointer.prototype.equals = function (other) {
if (other === this) {
return true;
}
if (!other || !(other instanceof JSONPointer)) {
return false;
}
if (this.segments.length !== other.segments.length) {
return false;
}
var length = this.segments.length;
for (var i = 0; i < length; ++i) {
var s1 = this.segments[i];
var s2 = other.segments[i];
if (!s1.equals(s2)) {
return false;
}
}
return true;
};
return JSONPointer;
}());
exports.JSONPointer = JSONPointer;
/**
* JSONPointer representing the root
*/
exports.ROOT = new JSONPointer([]);
/**
* Escape special characters in the JSON pointer key string
*
* @param key the key string to escape
* @return the escaped string
*/
function escapeKey(key) {
for (var i = 0; i < key.length; ++i) {
var c = key.charAt(i);
if (c === '/' || c === '~') {
var length_2 = key.length;
var parts = [];
parts.push(key.substring(0, i));
for (var j = i; j < length_2; ++j) {
var c1 = key.charAt(j);
if (c1 === '/') {
parts.push('~1');
}
else if (c1 === '~') {
parts.push('~0');
}
else {
parts.push(c1);
}
}
return parts.join('');
}
}
return key;
}