@allgemein/schema-api
Version:
Library for schema api
317 lines (316 loc) • 11.1 kB
JavaScript
"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