import { addTag, } from '../../tags/addTag'; import { EpistemicTypes, } from '../epistemology/EpistemicTypes'; import { getStructuredTags, } from '../../tags/getStructuredTags'; import { getTag, } from '../../tags/getTag'; import { IEpistemology, } from '../epistemology/IEpistemology'; import { IFindBaseArgs, } from '../querying/FindModelArgs'; import { IModel, } from './IModel'; import { IModelConstructorArgs, } from './IModelConstructorArgs'; import { IOntology, } from '../ontology/IOntology'; import { ISerializedModel, } from './ISerializedModel'; import { ITag, } from '../../tags/ITag'; import { IWorld, } from '../world/IWorld'; import { ModelType, } from './ModelType'; import { OnticTypes, } from '../ontology/OnticTypes'; import { removeTag, } from '../../tags/removeTag'; import { Tag, } from '../../tags/Tag'; import { assert, assertValid, } from 'ts-assertions'; export abstract class ModelBase< Type extends ModelType, Being extends OnticTypes = any, Knowledge extends ModelType = any, > implements IModel { public abstract readonly being: Type extends OnticTypes ? IOntology : null; public abstract readonly knowledge: Type extends EpistemicTypes ? IEpistemology : null; private readonly __name: string; public get name() { return this.__name; } private __tags: readonly ITag[] = Object.freeze([]); public get tags() { return this.__tags; } protected readonly __type: Type; public get type() { return this.__type; } private readonly __world: IWorld; public get world() { return this.__world; } constructor( world: IWorld, args: IModelConstructorArgs, ) { this.__world = assertValid( world, 'The world argument was not provided to the ModelBase constructor.', ); assert( args, 'The args argument was not provided to the ModelBase constructor.', ); const { name, tags, type, } = args; assert( name && typeof name === 'string', 'The name property of the argument provided to the ModelBase ' + 'constructor was not a string with content.', ); assert( Object.values(ModelType).includes(type), 'The type property of the argument provided to the ModelBase ' + 'constructor was not a valid ModelType.', ); this.__name = name; this.__type = type; if (tags) { this.__tags = this.tags.concat(getStructuredTags(tags)); } this.initialize(this as any); } public readonly addTag = (tag: Tag) => ( void (this.__tags = Object.freeze(addTag(tag, this.tags))) ); public readonly clone = ( self: IModel, ): IModel => { const copy = Object.assign( Object.create(Object.getPrototypeOf(self)), self, ); copy.__being = self.being ? self.being.clone(self.being as any) : null; copy.__name = self.name; copy.__knowledge = self.knowledge ? self.knowledge.clone(self.knowledge!) : null; copy.__tags = self.tags.slice(); copy.__type = self.type; copy.__world = self.world; return copy; }; public readonly destroy = (self: IModel): void => { ((self: any) => { self.finalize(self); if (self.being) { self.being.destroy(self.being); } if (self.knowledge) { self.knowledge.destroy(self.knowledge); } self.tags.forEach(self.removeTag); delete self.__being; delete self.being; delete self.__knowledge; delete self.knowledge; delete self.__tags; delete self.tags; delete self.__type; delete self.type; delete self.__world; delete self.world; })(self); }; public readonly finalize = (self: IModel): void => { if (self.being && typeof self.being.finalize === 'function') { self.being.finalize(self.being as any); } if (self.knowledge && typeof self.knowledge.finalize === 'function') { self.knowledge.finalize(self.knowledge!); } }; public readonly find = ( args: string | IFindBaseArgs, ): IModel | null => this.findAllGenerator( typeof args === 'string' ? { name: args } : args, ).next().value || null; public readonly findAll = ( args: '*' | IFindBaseArgs, ): readonly IModel[] => { const ret = []; for (const model of this.findAllGenerator(args)) { ret.push(model); } return ret; }; public abstract readonly findAllGenerator: ( args: '*' | IFindBaseArgs, ) => IterableIterator>; public readonly getTag = (toSearch: Tag) => ( getTag(this.tags, toSearch) ); public readonly initialize = (self: IModel): void => { if (self.being && typeof self.being.initialize === 'function') { self.being.initialize(self.being as any); } if (self.knowledge && typeof self.knowledge.initialize === 'function') { self.knowledge.initialize(self.knowledge!); } }; public readonly removeTag = (tag: Tag) => ( void (this.__tags = Object.freeze(removeTag(this.tags, tag))) ); public readonly serialize = ( self: IModel, spaces: number = 0, ) => JSON.stringify(self.serializeToObject(self), null, spaces); public readonly serializeToObject = ( self: IModel, ): ISerializedModel => ({ being: self.being ? self.being.serializeToObject(self.being as any) : null, knowledge: self.knowledge ? self.knowledge.serializeToObject(self.knowledge!) : null, name: self.name, tags: [ ...self.tags ], type: self.type, }); }