// (C) Copyright 2015 Moodle Pty Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { CoreItemsManagerSource } from './items-manager-source'; type SourceConstructor = { getSourceId(...args: unknown[]): string; new (...args: unknown[]): T; }; type InstanceTracking = { instance: CoreItemsManagerSource; references: unknown[] }; type Instances = Record; /** * Tracks CoreItemsManagerSource instances to reuse between pages. */ export class CoreItemsManagerSourcesTracker { private static instances: WeakMap = new WeakMap(); private static instanceIds: WeakMap = new WeakMap(); /** * Create an instance of the given source or retrieve one if it's already in use. * * @param constructor Source constructor. * @param constructorArguments Arguments to create a new instance, used to find out if an instance already exists. * @returns Source. */ static getOrCreateSource( constructor: SourceConstructor, constructorArguments: ConstructorParameters>, ): T { const id = constructor.getSourceId(...constructorArguments); const constructorInstances = this.getConstructorInstances(constructor); return constructorInstances[id]?.instance as T ?? this.createInstance(id, constructor, constructorArguments); } /** * Track an object referencing a source. * * @param source Source. * @param reference Object referncing this source. */ static addReference(source: CoreItemsManagerSource, reference: unknown): void { const constructorInstances = this.getConstructorInstances(source.constructor as SourceConstructor); const instanceId = this.instanceIds.get(source); if (!instanceId) { return; } if (!(instanceId in constructorInstances)) { constructorInstances[instanceId] = { instance: source, references: [], }; } constructorInstances[instanceId].references.push(reference); } /** * Remove a reference to an existing source, freeing it from memory if it's not referenced elsewhere. * * @param source Source. * @param reference Object that was referncing this source. */ static removeReference(source: CoreItemsManagerSource, reference: unknown): void { const constructorInstances = this.instances.get(source.constructor as SourceConstructor); const instanceId = this.instanceIds.get(source); const index = constructorInstances?.[instanceId ?? '']?.references.indexOf(reference) ?? -1; if (!constructorInstances || !instanceId || index === -1) { return; } constructorInstances[instanceId].references.splice(index, 1); if (constructorInstances[instanceId].references.length === 0) { delete constructorInstances[instanceId]; } } /** * Get instances for a given constructor. * * @param constructor Source constructor. * @returns Constructor instances. */ private static getConstructorInstances(constructor: SourceConstructor): Instances { return this.instances.get(constructor) ?? this.initialiseConstructorInstances(constructor); } /** * Initialise instances for a given constructor. * * @param constructor Source constructor. * @returns Constructor instances. */ private static initialiseConstructorInstances(constructor: SourceConstructor): Instances { const constructorInstances = {}; this.instances.set(constructor, constructorInstances); return constructorInstances; } /** * Create a new source instance. * * @param id Source id. * @param constructor Source constructor. * @param constructorArguments Source constructor arguments. * @returns Source instance. */ private static createInstance( id: string, constructor: SourceConstructor, constructorArguments: ConstructorParameters>, ): T { const instance = new constructor(...constructorArguments); this.instanceIds.set(instance, id); return instance; } }