142 lines
4.9 KiB
TypeScript
142 lines
4.9 KiB
TypeScript
|
// (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<T extends CoreItemsManagerSource = CoreItemsManagerSource> = {
|
||
|
getSourceId(...args: unknown[]): string;
|
||
|
new (...args: unknown[]): T;
|
||
|
};
|
||
|
type InstanceTracking = { instance: CoreItemsManagerSource; references: unknown[] };
|
||
|
type Instances = Record<string, InstanceTracking>;
|
||
|
|
||
|
/**
|
||
|
* Tracks CoreItemsManagerSource instances to reuse between pages.
|
||
|
*/
|
||
|
export class CoreItemsManagerSourcesTracker {
|
||
|
|
||
|
private static instances: WeakMap<SourceConstructor, Instances> = new WeakMap();
|
||
|
private static instanceIds: WeakMap<CoreItemsManagerSource, string> = 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<T extends CoreItemsManagerSource>(
|
||
|
constructor: SourceConstructor<T>,
|
||
|
constructorArguments: ConstructorParameters<SourceConstructor<T>>,
|
||
|
): 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<T extends CoreItemsManagerSource>(
|
||
|
id: string,
|
||
|
constructor: SourceConstructor<T>,
|
||
|
constructorArguments: ConstructorParameters<SourceConstructor<T>>,
|
||
|
): T {
|
||
|
const instance = new constructor(...constructorArguments);
|
||
|
|
||
|
this.instanceIds.set(instance, id);
|
||
|
|
||
|
return instance;
|
||
|
}
|
||
|
|
||
|
}
|