UNPKG

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
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