import { create, has } from '../../core/object'; export const $NAME = '$name'; export const $CONSTRUCTOR = '$constructor'; export interface Constructor { new(...args: any[]): T; readonly name: string; [name: string]: any; } export interface MetaObject { [$NAME]: string; [$CONSTRUCTOR]?: Constructor; [name: string]: any; } export interface Configuration { [name: string]: any; } export class MetaContext { protected _names = create(); protected _constructors = new Map(); constructor(public config: Configuration = {}) { } register(props: any) { const constructor = props[$CONSTRUCTOR]; const name = props[$NAME]; if (!name) return; const oldProps = this.getName(name); if (oldProps === undefined) { const newProps = this.createMetaObject(props); if (constructor !== undefined) { const oldProps = this.getConstructor(constructor); if (oldProps !== undefined) throw new TypeError(`MetaContext.register(): constructor ${constructor} is already in use for name '${oldProps[$NAME]}'`); this._constructors.set(constructor, newProps); } this._names[name] = newProps; return newProps; } if (oldProps[$CONSTRUCTOR] !== constructor) throw new TypeError(`MetaContext.register(): constructor missmatch for name '${name}': ${oldProps[$CONSTRUCTOR]} !== ${constructor}`); return this.updateMetaObject(oldProps, props); } registerAll(propsList: any[]) { propsList.forEach(props => { this.register(props); }); return this; } getValue(value: any) { return this.getCustomValue(typeof value, value); } resolveValue(value: any) { const ret = this.getValue(value); if (ret === undefined) throw new TypeError(`MetaContext.resolveValue(): ${value} is not found`); return ret; } getName(name: string) { return this._names[name]; } resolveName(name: string) { if (!has(this._names, name)) throw new TypeError(`MetaContext.resolveName(): '${name}' is not found`); return this.getName(name)!; } getConstructor(constructor: Constructor) { return this._constructors.get(constructor); } resolveConstructor(constructor: Constructor) { if (!this._constructors.has(constructor)) throw new TypeError(`MetaContext.resolveConstructor(): ${constructor} is not found`); return this.getConstructor(constructor)!; } names() { return Object.keys(this._names); } constructors() { return [...this._constructors.keys()]; } protected getCustomValue(name: string, value: any) { if (name === 'object') { if (value === null) return this.getName('null'); const constructor = value.constructor; if (constructor !== undefined) return this.getConstructor(constructor); } return this.getName(name); } protected createMetaObject(props: any) { return { ...props } as MetaObject; } protected updateMetaObject(metaObj: MetaObject, props: any) { return Object.assign(metaObj, props) as MetaObject; } }