semantic-network
Version:
A utility library for manipulating a list of links that form a semantic interface to a network of resources.
149 lines • 6.25 kB
JavaScript
import anylogger from 'anylogger';
import { CanonicalOrSelf } from '../utils/comparators/canonicalOrSelf';
import { defaultEqualityMatcher } from '../utils/equalityUtil';
import { TrackedRepresentationUtil } from '../utils/trackedRepresentationUtil';
import { instanceOfTrackedRepresentation } from '../utils/instanceOf/instanceOfTrackedRepresentation';
import { SingletonMerger } from './singletonMerger';
const log = anylogger('CollectionMerger');
/**
* A helper class for manipulating items in a {@link CollectionRepresentation.items}.
*/
export class CollectionMerger {
/**
* Update existing items in the lvalue that are in the rvalue.
* Returns the lvalue (mutated).
*
* The equality operator is {@link CanonicalOrSelf}.
*/
static updateItems(lvalue, rvalue, options) {
var _a;
const { equalityOperator = CanonicalOrSelf, equalityMatcher = defaultEqualityMatcher, } = Object.assign({}, options);
const { items } = lvalue;
if (items && ((_a = rvalue.items) === null || _a === void 0 ? void 0 : _a.length) > 0) {
// find existing and update
for (const item of items) {
// TODO: implement eTag aware singletons
const found = rvalue.items.find(r => equalityMatcher(item, r, equalityOperator));
if (found) {
SingletonMerger.merge(item, found, options);
}
}
}
return lvalue;
}
/**
* Omit items in the lvalue that are not in the rvalue.
* Returns the lvalue (mutated).
*
* This method splices (mutates) the {@link CollectionRepresentation.items} in order to retain bindings if they exist.
*
* The equality operator is {@link CanonicalOrSelf}.
*/
static omitItems(lvalue, rvalue, options) {
var _a;
const { equalityOperator = CanonicalOrSelf, equalityMatcher = defaultEqualityMatcher, } = Object.assign({}, options);
const { items } = lvalue;
if (!items) {
log.debug('initialised collection items');
const { set } = Object.assign({}, options);
if (set) {
set(lvalue, 'items', []);
}
else {
lvalue.items = [];
}
}
else {
if (((_a = rvalue.items) === null || _a === void 0 ? void 0 : _a.length) > 0) {
const indexes = items
.map((item, index) => {
const found = rvalue.items.findIndex(r => {
return equalityMatcher(item, r, equalityOperator);
});
// when found in rvalue retain the corresponding lvalue index for removal
// mark -1 to those that are not to bbe removed
return found >= 0 ? -1 : index;
})
// keep just the indexes to remove
.filter(x => x >= 0)
// sorted tail --> head (descending)
.sort((a, b) => b - a);
// removed starting from the tail each item so that indexes remain correct
for (const index of indexes) {
lvalue.items.splice(index, 1);
}
}
else {
// clear out the list because rvalue is empty
lvalue.items.splice(0, lvalue.items.length);
}
}
return lvalue;
}
/**
* Extract items in the rvalue that are not in the lvalue to extend the lvalue.
* Returns the lvalue (mutated).
*
* This method splices (mutates) the {@link CollectionRepresentation.items} in order to retain bindings if they exist.
*
* The equality operator is {@link canonicalOrSelf}.
*/
static extractItems(lvalue, rvalue, options) {
const { equalityOperator = CanonicalOrSelf, equalityMatcher = defaultEqualityMatcher, } = Object.assign({}, options);
const { items } = lvalue;
if (!items) {
log.debug('initialised collection items');
const { set } = Object.assign({}, options);
if (set) {
set(lvalue, 'items', []);
}
else {
lvalue.items = [];
}
}
else if (rvalue.items) {
// create a set of items in rvalue that don't exist
const include = rvalue
.items
.filter(item => items.findIndex(r => equalityMatcher(item, r, equalityOperator)) < 0);
// add into the set at the tail
lvalue.items.splice(lvalue.items.length, 0, ...include);
}
return lvalue;
}
/**
* Return a lvalue
* 1. items that
* - only has items found in rvalue
* - where the lvalue already had a value leave the original lvalue item in place.
* 2. links in the lvalue with any new rvalue links [optional]
*
* The equality operator is {@link canonicalOrSelf}.
*
* Note: the identity operator does not need to know about state
*/
static merge(lvalue, rvalue, options) {
const { set, mergeLinks = true, mergeHeaders = true, } = Object.assign({}, options);
if (mergeLinks) {
if (set) {
// update the links
// Note: currently does not update other fields from incoming representation
// TODO: in the odd the case that a collection has attributes
set(lvalue, 'links', rvalue.links);
}
else {
Object.assign(lvalue.links, rvalue.links);
}
}
if (mergeHeaders) {
if (instanceOfTrackedRepresentation(lvalue) && instanceOfTrackedRepresentation(rvalue)) {
const { headers } = TrackedRepresentationUtil.getState(rvalue);
TrackedRepresentationUtil.setHeaders(lvalue, headers);
}
}
const omitted = this.omitItems(lvalue, rvalue, options);
const updated = this.updateItems(omitted, rvalue);
return this.extractItems(updated, rvalue, options);
}
}
//# sourceMappingURL=collectionMerger.js.map