UNPKG

@allgemein/schema-api

Version:
317 lines (316 loc) 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClassRef = void 0; const lodash_1 = require("lodash"); const base_1 = require("@allgemein/base"); const SchemaUtils_1 = require("./SchemaUtils"); const Constants_1 = require("./Constants"); const LookupRegistry_1 = require("./LookupRegistry"); const RegistryFactory_1 = require("./registry/RegistryFactory"); const AbstractRef_1 = require("./AbstractRef"); const MetadataRegistry_1 = require("./registry/MetadataRegistry"); /** * Reflective reference to a class function * * There can exist multiple ClassRef depending of context for a single class function. * The context is defined by the lookup registry. * It can be also a placeholder, for a dummy or later loaded class. * * This is a default implementation for IClassRef * class can be in different namespaces */ class ClassRef extends AbstractRef_1.AbstractRef { constructor(klass, namespace = Constants_1.DEFAULT_NAMESPACE) { super(Constants_1.METATYPE_CLASS_REF, ClassRef.getClassName(klass), null, namespace); this._isPlaceholder = false; this._isAnonymous = false; this.extends = []; this._isAnonymous = (0, lodash_1.isFunction)(klass) && klass.name === 'anonymous'; this.idx = ClassRef.__inc++; if ((0, lodash_1.isString)(klass)) { this.originalValue = klass; this._isPlaceholder = true; } else { this.originalValue = base_1.ClassUtils.getFunction(klass); } } isPlaceholder() { return this._isPlaceholder; } isAnonymous() { return this._isAnonymous; } getClassRefFor(object, type) { return ClassRef.get(object, this.namespace, type == Constants_1.METATYPE_PROPERTY); } static getClassName(k) { if (k[Constants_1.__CLASS__]) { return k[Constants_1.__CLASS__]; } return base_1.ClassUtils.getClassName(k); } updateClass(cls) { this.originalValue = base_1.ClassUtils.getFunction(cls); this._isPlaceholder = false; } get className() { return this.name; } get storingName() { let name = this.getOptions(Constants_1.C_PROP_NAME, this.className); return (0, lodash_1.snakeCase)(name); } set storingName(v) { this.setOption(Constants_1.C_PROP_NAME, v); } /** * Check if name is passed by options */ hasName() { return this.hasOption(Constants_1.C_PROP_NAME); } /** * Return the namespace for/of this class */ getNamespace() { return this.namespace; } /** * TODO implement * * @param namespace */ switchNamespace(namespace) { // this.getRegistry().remove(METATYPE_CLASS_REF, (x: IClassRef) => x.getClass() === this.getClass()); // this.namespace = namespace; // this.getRegistry().add(METATYPE_CLASS_REF, this); } isOf(instance) { const name = ClassRef.getClassName(instance); // if(name) if (name && name === this.name) { return true; } else if ((0, lodash_1.has)(instance, Constants_1.__CLASS__) && instance[Constants_1.__CLASS__] === this.name) { return true; } else { return this.getPropertyRefs() .map(x => (0, lodash_1.has)(instance, x.name)) .reduce((previousValue, currentValue) => previousValue && currentValue, true); } return false; } get machineName() { return (0, lodash_1.snakeCase)(this.className); } getClass(create = false) { if ((0, lodash_1.isFunction)(this.originalValue)) { return this.originalValue; } else if ((0, lodash_1.isString)(this.originalValue) && this.isPlaceholder) { if (create) { this.originalValue = SchemaUtils_1.SchemaUtils.clazz(this.originalValue); return this.originalValue; } } throw new base_1.NotYetImplementedError('getClass for ' + this.originalValue); } static find(klass, namespace = Constants_1.DEFAULT_NAMESPACE) { const registry = namespace === Constants_1.GLOBAL_NAMESPACE ? LookupRegistry_1.LookupRegistry : LookupRegistry_1.LookupRegistry.$(namespace); let className = ClassRef.getClassName(klass); let classRef = null; if ((0, lodash_1.isString)(klass)) { classRef = registry.find(Constants_1.METATYPE_CLASS_REF, (c) => c.className === className); } else { classRef = registry.find(Constants_1.METATYPE_CLASS_REF, (c) => c.getClass(true) === klass || (c.isPlaceholder() && c.className === className)); } return classRef; } /** * get all class refs for some class name * * @param klass */ static getAllByClassName(klass) { let name = ClassRef.getClassName(klass); return LookupRegistry_1.LookupRegistry.filter(Constants_1.METATYPE_CLASS_REF, (c) => c.name == name); } /** * filter function for classrefs * * @param klass */ static filter(fn) { return LookupRegistry_1.LookupRegistry.filter(Constants_1.METATYPE_CLASS_REF, fn); } static checkIfFunctionCallback(klass) { if ((0, lodash_1.isFunction)(klass)) { // maybe function which return type like () => type let name = ClassRef.getClassName(klass); if ((0, lodash_1.isEmpty)(name)) { let fn = null; try { fn = klass(); } catch (e) { } if (fn) { let name = ClassRef.getClassName(fn); if (!(0, lodash_1.isEmpty)(name)) { klass = fn; } } } } return klass; } /** * check if class reference already exists for given string or Function, if not create a new one * * @param klass * @param namespace * @param resolve */ static get(klass, namespace = Constants_1.GLOBAL_NAMESPACE, options) { const resolve = (0, lodash_1.isBoolean)(options) ? options : (0, lodash_1.get)(options || {}, 'resolve', false); if (resolve) { klass = this.checkIfFunctionCallback(klass); } const checkNs = (0, lodash_1.isObjectLike)(options) ? (0, lodash_1.get)(options || {}, 'checkNamespace', false) : false; const isAnonymous = (0, lodash_1.isFunction)(klass) && klass.name === 'anonymous'; if (checkNs) { const ns = MetadataRegistry_1.MetadataRegistry.$().getByContextAndTarget(Constants_1.METATYPE_NAMESPACE, (0, lodash_1.isFunction)(klass) ? klass.name : klass); if (ns.length > 0) { namespace = (0, lodash_1.get)((0, lodash_1.first)(ns), 'attributes.' + Constants_1.METATYPE_NAMESPACE, namespace); } } let classRef = this.find(klass, namespace); if (classRef) { if (classRef.isPlaceholder && (0, lodash_1.isFunction)(klass) && !isAnonymous) { classRef.updateClass(klass); } return classRef; } if (namespace === Constants_1.GLOBAL_NAMESPACE) { namespace = Constants_1.DEFAULT_NAMESPACE; } return this.createRef(klass, namespace); } /** * Create a class ref. * * @param klass * @param namespace */ static createRef(klass, namespace = Constants_1.GLOBAL_NAMESPACE) { const classRef = new ClassRef(klass, namespace); if ((0, lodash_1.isFunction)(klass)) { const proto = SchemaUtils_1.SchemaUtils.getInherited(klass); if (proto) { // is extends // TODO how to handle cirlces const extendRef = ClassRef.get(proto, namespace); classRef.addExtend(extendRef); } } return classRef.getRegistry().add(Constants_1.METATYPE_CLASS_REF, classRef); } /** * return global class reference * * @param klass * @param resolve */ static getGlobal(klass, resolve = false) { return this.get(klass, Constants_1.GLOBAL_NAMESPACE, resolve); } getRegistry() { return RegistryFactory_1.RegistryFactory.get(this.namespace); } getEntityRef() { if ((0, lodash_1.isUndefined)(this._cacheEntity)) { this._cacheEntity = this.getRegistry().find(Constants_1.METATYPE_ENTITY, (x) => x.getClassRef() === this); } return this._cacheEntity; } hasEntityRef() { return !!this.getEntityRef(); } create(addinfo = true) { return this.new(addinfo); } new(addinfo = true) { let klass = this.getClass(); let instance = Reflect.construct(klass, []); if (addinfo) { Reflect.defineProperty(instance, Constants_1.__NS__, { value: this.namespace, enumerable: true }); Reflect.defineProperty(instance, Constants_1.__CLASS__, { value: this.className, enumerable: true }); } return instance; } getPropertyRefs() { const inheritedProps = [].concat(...this.getExtends().map(x => x.getPropertyRefs())); const registeredProps = this.getRegistry().getPropertyRefs(this); inheritedProps.forEach((x) => { if (!registeredProps.find(z => x.name === z.name)) { registeredProps.push(x); } }); return registeredProps; } getPropertyRef(name) { let registeredProp = this.getRegistry().getPropertyRef(this, name); if (registeredProp) { return registeredProp; } else { for (const clsRef of this.getExtends()) { registeredProp = clsRef.getPropertyRef(name); if (registeredProp) { return registeredProp; } } } return null; } id() { return [this.namespace, this.className].map(x => (0, lodash_1.kebabCase)(x)).join(Constants_1.XS_ID_SEPARATOR); } build(data, options = {}) { return SchemaUtils_1.SchemaUtils.transform(this, data, options); } /** * Return base inherited class for the underlying class * ``` * class X exnteds Y { } * ``` * Y would be returned. * */ getExtend() { return !(0, lodash_1.isEmpty)(this.extends) ? (0, lodash_1.first)(this.extends) : null; } /** * Return all inherited classes for underlying class. Mostly added manually. * */ getExtends() { return this.extends; } /** * Append extend class ref. * @param ref */ addExtend(ref) { this.extends.push(ref); return ref; } } exports.ClassRef = ClassRef; ClassRef.__inc = 0; //# sourceMappingURL=ClassRef.js.map