@eclipse-scout/core
Version:
Eclipse Scout runtime
153 lines (136 loc) • 6.33 kB
text/typescript
/*
* Copyright (c) 2010, 2025 BSI Business Systems Integration AG
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
import {BaseDoEntity, Constructor, ObjectFactory, strings} from '../index';
/**
* Holds all data object class mappings.
*
* Data object classes having an @typeName decorator do register themselves automatically into this registry.
* The code to do that is automatically created and injected at compile time.
* See DataObjectTransformer#_createDoInventoryAddStatement.
* Therefore, the API of this class must match the code generated by this transformer.
* If this class name, the method name 'get' (static accessor) or 'add' (to register a data object) are changed, the transformer must be adapted as well!
*/
export class DataObjectInventory {
protected static _INSTANCE: DataObjectInventory = null;
protected _constructorByTypeName = new Map<string, Constructor<BaseDoEntity>>();
protected _typeNameByObjectType = new Map<string, string>();
protected _objectTypeByTypeName = new Map<string, string>();
protected constructor() {
}
/**
* Adds a new dataobject to the registry.
* @param doClass The dataobject class to register.
* @param typeName Optional typeName (`_type` attribute) of this dataobject. E.g. `myNamespace.MyEntity`.
* If omitted, it will be detected from the given class by creating a new instance and reading the `_type` attribute.
* So this attribute must be set either by a `@typeName()` decorator on the class or as part of its constructor.
* @param objectType Optional object type of the dataobject. E.g. `myNamespace.MyEntityDo`.
* If omitted, it will be read from the `ObjectFactory`. So the constructor must have been registered to the `ObjectFactory` already.
* @returns true if the class could be completely registered. false otherwise (e.g. if the typeName or objectType is unknown).
*/
add(doClass: Constructor<BaseDoEntity>, typeName?: string, objectType?: string): boolean {
if (!doClass) {
return false;
}
typeName = typeName || this._readTypeName(doClass);
if (!typeName) {
return false;
}
if (this._constructorByTypeName.has(typeName)) {
throw new Error(`There is already a constructor registered for type name '${typeName}'.`);
}
this._constructorByTypeName.set(typeName, doClass);
objectType = objectType || ObjectFactory.get().getObjectType(doClass);
if (!objectType) {
return false;
}
objectType = strings.removePrefix(objectType, 'scout.'); // scout elements are in the map without namespace.
this._typeNameByObjectType.set(objectType, typeName);
this._objectTypeByTypeName.set(typeName, objectType);
return true;
}
/**
* Removes the dataobject given.
* @param item The dataobject class or the typeName (_type) of the dataobject to remove.
*/
remove(item: Constructor<BaseDoEntity> | string) {
if (typeof item === 'string') {
this._removeByTypeName(item);
} else {
this._removeByClass(item);
}
}
protected _removeByClass(doClass: Constructor<BaseDoEntity>) {
if (!doClass) {
return;
}
for (const [typeName, doConstructor] of this._constructorByTypeName) {
if (doConstructor === doClass) {
this._removeByTypeName(typeName);
}
}
}
protected _removeByTypeName(typeName: string) {
if (!typeName) {
return;
}
this._constructorByTypeName.delete(typeName);
const objectType = this._objectTypeByTypeName.get(typeName);
if (objectType) {
this._typeNameByObjectType.delete(objectType);
}
this._objectTypeByTypeName.delete(typeName);
}
protected _readTypeName(DoClass: Constructor<BaseDoEntity>): string {
return new DoClass()._type;
}
/**
* @returns All dataobject classes known to the registry.
*/
getKnownDataObjectClasses(): IterableIterator<Constructor<BaseDoEntity>> {
return this._constructorByTypeName.values();
}
/**
* @param typeNameOrObjectType The typeName (_type like 'scout.Topic') or objectType (like 'scout.TopicDo') for which the dataobject class should be returned.
* @returns the dataobject class for given typeName (_type) or objectType.
*/
toConstructor(typeNameOrObjectType: string): Constructor<BaseDoEntity> {
return this._constructorByTypeName.get(typeNameOrObjectType) || this._constructorByTypeName.get(this.toTypeName(typeNameOrObjectType));
}
/**
* @param objectType The objectType for which the typeName (_type) should be returned. E.g. 'scout.CodeDo' or 'myapp.MySpecialDo'.
* @returns the dataobject typeName (_type) for given objectType. E.g. returns 'scout.Topic' for 'scout.TopicDo'. Or 'myApp.MySpecial' for 'myApp.MySpecialDo'. This is the inverse operation of {@link toObjectType}.
*/
toTypeName(objectType: string): string {
objectType = strings.removePrefix(objectType, 'scout.'); // scout elements are in the map without namespace.
return this._typeNameByObjectType.get(objectType);
}
/**
* @param typeName The typeName (_type) for which the objectType should be returned.
* @returns the dataobject objectType for given typeName (_type). E.g. returns 'TopicDo' for 'scout.Topic'. Or 'myApp.MySpecialDo' for 'myApp.MySpecial'. This is the inverse operation of {@link toTypeName}.
*/
toObjectType(typeName: string): string {
return this._objectTypeByTypeName.get(typeName);
}
/**
* @returns the DataObjectInventory instance.
*/
static get(): DataObjectInventory {
if (!DataObjectInventory._INSTANCE) {
// Do not create using scout.create as this registry instance is already used very early during source code parsing.
// At this time no object factory is available yet.
DataObjectInventory._INSTANCE = new DataObjectInventory();
}
return DataObjectInventory._INSTANCE;
}
}
// proactively register this class on the window object to ensure it can be used by dataobjects to register themselves.
// See DataObjectTransformer#_createDoInventoryAddStatement.
window['scout'] = window['scout'] || {};
window['scout'].DataObjectInventory = DataObjectInventory;