2021-11-09 13:08:56 +01:00
|
|
|
// (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;
|
|
|
|
};
|
2021-11-17 12:31:05 +01:00
|
|
|
type SourceConstuctorInstance<T> = T extends { new(...args: unknown[]): infer P } ? P : never;
|
2021-11-09 13:08:56 +01:00
|
|
|
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.
|
|
|
|
*/
|
2021-11-17 12:31:05 +01:00
|
|
|
static getOrCreateSource<T extends CoreItemsManagerSource, C extends SourceConstructor<T>>(
|
|
|
|
constructor: C,
|
|
|
|
constructorArguments: ConstructorParameters<C>,
|
|
|
|
): SourceConstuctorInstance<C> {
|
2021-11-09 13:08:56 +01:00
|
|
|
const id = constructor.getSourceId(...constructorArguments);
|
|
|
|
const constructorInstances = this.getConstructorInstances(constructor);
|
|
|
|
|
2021-11-17 12:31:05 +01:00
|
|
|
return constructorInstances[id]?.instance as SourceConstuctorInstance<C>
|
2021-11-09 13:08:56 +01:00
|
|
|
?? 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);
|
|
|
|
|
2021-11-17 12:35:44 +01:00
|
|
|
if (instanceId === undefined) {
|
2021-11-09 13:08:56 +01:00
|
|
|
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;
|
|
|
|
|
2021-11-17 12:35:44 +01:00
|
|
|
if (!constructorInstances || instanceId === undefined || index === -1) {
|
2021-11-09 13:08:56 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|