import {Id} from '../shared'; import {EndpointNode} from './node'; import {ModelType} from './universe'; import {FieldSet, identifyObjectIdentifier, ModelSet} from './util'; /** * Model inside a modeling. */ export class Model { private readonly _name: string; private readonly _base: string; private readonly _target: string; /** * Description of this model. */ public description?: string; /** * Object ID of the corresponding object. */ public object?: Id; /** * Type of this model. See also {@linkcode ModelType}. */ public type?: ModelType; /** * Map mapping names of abstracts (aka parents) of this model to their model instances. */ public abstracts: Map; /** * Map mapping names of specials (aka extensions) of this model to their model instances. */ public specials: Map; /** * Target fields of this model. */ public targetFields: Map; /** * Origin fields of this model. */ public originFields: Map; /** * Reference to a field instance, if this model is a field, `undefined` otherwise. */ public field?: Field; /** * Nodes which are suited to represent this model. */ public nodes: Map; public constructor(name: string, base: string, target: string) { this._name = name; this._base = base; this._target = target; this.abstracts = new Map(); this.specials = new Map(); this.targetFields = new Map(); this.originFields = new Map(); this.nodes = new Map(); } /** * Name of this model. */ public get name(): string { return this._name; } /** * Name of the modeling this model was originally defined. */ public get base(): string { return this._base; } /** * Name of the modeling this model resides in. */ public get target(): string { return this._target; } /** * Obtains the fully qualified name of this model. * @returns identifier of this model */ public getIdentifier(): string { return identifyObjectIdentifier(this); } /** * Returns abstract models. * @param self specifies whether to include this model in the result set * @param direct specifies whether to include direct abstracts in the result set * @param indirect specifies whether to include indirect abstracts (e.g., abstracts of abstracts) in the result set */ public getAbstracts(self = false, direct = true, indirect = true): ModelSet { const abstracts: Map = new Map(); if (self) { abstracts.set(this.getIdentifier(), this); } if (direct || indirect) { for (const dAbs of this.abstracts.values()) { const abs = dAbs.getAbstracts(direct, indirect, indirect); for (const diAbs of abs.map.entries()) { abstracts.set(diAbs[0], diAbs[1]); } } } return new ModelSet(abstracts); } /** * Returns special models. * @param self specifies whether to include this model in the result set * @param direct specifies whether to include direct specials in the result set * @param indirect specifies whether to include indirect specials (e.g., specials of specials) in the result set */ public getSpecials(self = false, direct = true, indirect = true): ModelSet { const specials: Map = new Map(); if (self) { specials.set(this.getIdentifier(), this); } if (direct || indirect) { for (const dSpec of this.specials.values()) { const specs = dSpec.getSpecials(direct, indirect, indirect); for (const diSpec of specs.map.entries()) { specials.set(diSpec[0], diSpec[1]); } } } return new ModelSet(specials); } } /** * Field of a model. */ export class Field { private readonly _origin: Model; private readonly _relation: Model; private readonly _target: Model; constructor(origin: Model, relation: Model, target: Model) { this._origin = origin; this._relation = relation; this._target = target; } /** * Model instance this field is attached to. * Should appear in its {@linkcode Model.fields}. */ public get origin(): Model { return this._origin; } /** * Field model. * Should have this instance as {@linkcode Model.field}. */ public get relation(): Model { return this._relation; } /** * Instance of the model this field points to. */ public get target(): Model { return this._target; } /** * Name of the field without the origin model prefix. */ public get name(): string { const name = this.relation.name; const posHash = name.indexOf('#'); if (posHash === -1) { return name; } return name.substring(posHash + 1); } /** * Object ID of the corresponding relation. */ public get object(): Id | undefined { return this.relation.object; } /** * Obtains the fully qualified name of this field. * @returns identifier of this field */ public getIdentifier(): string { return identifyObjectIdentifier(this.relation); } /** * Returns abstract fields. * @param self specifies whether to include this model in the result set * @param direct specifies whether to include direct abstracts in the result set * @param indirect specifies whether to include indirect abstracts (e.g., abstracts of abstracts) in the result set */ public getAbstracts(self = false, direct = true, indirect = true): FieldSet { const abstracts: Map = new Map(); if (self) { abstracts.set(this.getIdentifier(), this); } if (direct || indirect) { for (const dAbs of this.relation.specials.values()) { const abs = dAbs.field?.getAbstracts(direct, indirect, indirect); if (!abs) { continue; } for (const diAbs of abs.map.entries()) { abstracts.set(diAbs[0], diAbs[1]); } } } return new FieldSet(abstracts); } /** * Returns special fields. * @param self specifies whether to include this model in the result set * @param direct specifies whether to include direct specials in the result set * @param indirect specifies whether to include indirect specials (e.g., specials of specials) in the result set */ public getSpecials(self = false, direct = true, indirect = true): FieldSet { const specials: Map = new Map(); if (self) { specials.set(this.getIdentifier(), this); } if (direct || indirect) { for (const dSpec of this.relation.specials.values()) { const specs = dSpec.field?.getSpecials(direct, indirect, indirect); if (!specs) { continue; } for (const diSpec of specs.map.entries()) { specials.set(diSpec[0], diSpec[1]); } } } return new FieldSet(specials); } }