@kephas/reflection
Version:
Provides reflection capabilities, like TypeInfoRegistry, ITypeInfo, and IProperty.
475 lines (463 loc) • 15.7 kB
JavaScript
import { __decorate, __metadata } from 'tslib';
import { Requires, Serializable, AppService, Priority, SingletonAppServiceContract } from '@kephas/core';
/**
* Provides display information.
*
* @export
* @class DisplayInfo
*/
class DisplayInfo {
}
/**
* Signals a reflection error.
*
* @export
* @class ReflectionError
* @extends {Error}
*/
class ReflectionError extends Error {
/**
* Creates an instance of ReflectionError.
* @param {string} message The error message.
* @memberof ReflectionError
*/
constructor(message) {
super(message);
}
}
/**
* Provides basic implementation of reflection elements.
*
* @export
* @class ElementInfo
*/
class ElementInfo {
/**
* Creates an instance of ElementInfo.
*
* @param {string} name The element name.
* @param {string} [fullName] Optional. The full name of the element.
* @param {DisplayInfo} [displayInfo] Optional. The display information.
* @param {ITypeInfoRegistry} [registry] The root type info registry.
* @memberof ElementInfo
*/
constructor({ name, fullName, displayInfo, registry, ...args }) {
/**
* Gets the element name.
*
* @type {string}
* @memberof IElementInfo
*/
this.name = '';
if (!name) {
throw new ReflectionError('The name must be provided.');
}
this.name = name;
this.fullName = fullName || this.name;
this.displayInfo = displayInfo || new DisplayInfo();
Object.assign(this, args);
}
}
/**
* Provides a list of well known type names.
*
* @export
* @class TypeName
*/
class TypeName {
}
/**
* The name of the 'any' type.
*
* @static
* @memberof TypeName
*/
TypeName.AnyTypeName = 'any';
/**
* The name of the 'boolean' type.
*
* @static
* @memberof TypeName
*/
TypeName.BooleanTypeName = 'boolean';
/**
* The name of the 'number' type.
*
* @static
* @memberof TypeName
*/
TypeName.NumberTypeName = 'number';
/**
* The name of the 'string' type.
*
* @static
* @memberof TypeName
*/
TypeName.StringTypeName = 'string';
/**
* The name of the 'array' type.
*
* @static
* @memberof TypeName
*/
TypeName.ArrayTypeName = 'array';
/**
* The name of the 'any[]' type.
*
* @static
* @memberof TypeName
*/
TypeName.ArrayOfAnyTypeName = 'any[]';
/**
* The name of the 'Uint8Array' type.
*
* @static
* @memberof TypeName
*/
TypeName.ArrayOfByteTypeName = 'Uint8Array';
/**
* The name of the 'symbol' type.
*
* @static
* @memberof TypeName
*/
TypeName.SymbolTypeName = 'symbol';
/**
* The name of the 'Date' type.
*
* @static
* @memberof TypeName
*/
TypeName.DateTypeName = 'Date';
var TypeInfoRegistry_1;
/**
* Provides centralized access to the application's type system.
*
* @export
* @class TypeInfoRegistry
*/
let TypeInfoRegistry = TypeInfoRegistry_1 = class TypeInfoRegistry {
/**
* Creates an instance of TypeInfoRegistry.
* @memberof TypeInfoRegistry
*/
constructor() {
/**
* Gets the registerd types.
*
* @type {ITypeInfo[]}
* @memberof TypeInfoRegistry
*/
this.types = [];
this._typesByFullName = {};
this._typesByName = {};
this.types = [];
this.initialize(this);
}
/**
* Gets the singleton instance of the type registry.
*
* @static
* @type {TypeInfoRegistry}
* @memberof TypeInfoRegistry
*/
static get Instance() {
return TypeInfoRegistry_1._instance || (TypeInfoRegistry_1._instance = new TypeInfoRegistry_1());
}
/**
* Gets the type in the registry by its name.
*
* @param {string | Function} typeRef The full name of the type or the runtime type.
* @param {boolean} [throwOnNotFound=true] True to throw if the type cannot be found.
* @returns {TypeInfo}
* @memberof TypeInfoRegistry
*/
getType(typeRef, throwOnNotFound) {
Requires.HasValue(typeRef, 'typeRef');
if (throwOnNotFound === undefined) {
throwOnNotFound = true;
}
let fullName = typeof typeRef === 'function'
? Serializable.getTypeFullName(typeRef)
: typeRef;
if (fullName && fullName.endsWith('[]')) {
fullName = TypeName.ArrayOfAnyTypeName;
}
let type = fullName ? this._typesByFullName[fullName] : undefined;
if (!type && fullName) {
type = this._typesByName[fullName];
}
if (!type && throwOnNotFound) {
throw new ReflectionError(`The type with name '${typeRef}' was not found.`);
}
return type;
}
/**
* Registers the provided types.
*
* @param {ITypeInfo[]} types The types to register.
* @returns {this} This registry.
* @memberof TypeInfoRegistry
*/
register(...types) {
if (!types) {
return this;
}
for (const type of types) {
const typeKey = type.fullName || type.name;
if (this._typesByFullName[typeKey]) {
throw new ReflectionError(`The type ${typeKey} is already registered.`);
}
this._typesByFullName[typeKey] = type;
this._typesByName[type.name] = type;
}
this.types.push.apply(this.types, types);
return this;
}
/**
* Initializes the registry.
*
* @protected
* @param {TypeInfoRegistry} registry
* @memberof TypeInfoRegistry
*/
initialize(registry) {
}
};
TypeInfoRegistry = TypeInfoRegistry_1 = __decorate([
AppService({ overridePriority: Priority.Low, provider: _ => TypeInfoRegistry_1.Instance }),
SingletonAppServiceContract(),
__metadata("design:paramtypes", [])
], TypeInfoRegistry);
/**
* Reflective element information holding a value.
*
* @export
* @class ValueElementInfo
* @extends {ElementInfo}
*/
class ValueElementInfo extends ElementInfo {
/**
* Creates an instance of ValueElementInfo.
*
* @param {string} name The element name.
* @param {string} [fullName] Optional. The full name of the element.
* @param {DisplayInfo} [displayInfo] Optional. The display information.
* @param {ITypeInfo} [valueType] The value type.
* @param {ITypeInfoRegistry} [registry] The root type info registry.
* @memberof ValueElementInfo
*/
constructor({ name, fullName, displayInfo, valueType, registry, ...args }) {
super({ name, fullName, displayInfo, registry, ...args });
if (!valueType) {
this._valueTypeGetter =
() => (this._valueType || (this._valueType = this.getValueType(TypeName.AnyTypeName, registry)));
}
else if (typeof valueType === 'string') {
this._valueTypeGetter =
() => (this._valueType || (this._valueType = this.getValueType(valueType, registry)));
}
else {
this._valueType = valueType;
}
}
/**
* Gets the type of the element's value.
*
* @type {TypeInfo}
* @memberof ValueElementInfo
*/
get valueType() {
return this._valueType || (this._valueType = this._valueTypeGetter());
}
/**
* Gets the value type based on the type name.
*
* @protected
* @param {string} valueType The name or full name of the value type.
* @param {ITypeInfoRegistry} [registry] The type info registry. If not provided, TypeInfoRegistry.Instance will be used.
* @returns
* @memberof ValueElementInfo
*/
getValueType(valueType, registry) {
return (registry || TypeInfoRegistry.Instance).getType(valueType);
}
}
/**
* Provides reflection information about a property.
*
* @export
* @class PropertyInfo
* @extends {ElementInfo}
* @implements {IPropertyInfo}
*/
class PropertyInfo extends ValueElementInfo {
/**
* Creates an instance of PropertyInfo.
*
* @param {ITypeInfo} declaringType The declaring type.
* @param {string} name The element name.
* @param {string} [fullName] Optional. The full name of the element.
* @param {DisplayInfo} [displayInfo] Optional. The display information.
* @param {ITypeInfo} [valueType] The value type.
* @param {boolean} [canRead] True if the property can be read.
* @param {boolean} [canWrite] True if the property can be written.
* @param {boolean} [isRequired] True if the property requires a value to be set.
* @param {*} [defaultValue] The default value of the property.
* @param {ITypeInfoRegistry} [registry] The root type info registry.
* @memberof PropertyInfo
*/
constructor({ declaringType, name, fullName, displayInfo, valueType, canRead, canWrite, isRequired, isStatic, defaultValue, registry, ...args }) {
super({ name, fullName, displayInfo, valueType, registry, ...args });
/**
* Gets a value indicating whether the property can be written to.
*
* @type {boolean}
* @memberof IPropertyInfo
*/
this.canWrite = true;
/**
* Gets a value indicating whether the property value can be read.
*
* @type {boolean}
* @memberof IPropertyInfo
*/
this.canRead = true;
/**
* Gets a value indicating whether a value is required for this property.
*
* @type {boolean}
* @memberof PropertyInfo
*/
this.isRequired = false;
/**
* Gets a value indicating whether this property is class bound, not instance bound.
*
* @type {boolean}
* @memberof PropertyInfo
*/
this.isStatic = false;
if (!declaringType) {
throw new ReflectionError('The declaring type is not set.');
}
this.declaringType = declaringType;
this.canRead = canRead === undefined ? true : canRead;
this.canWrite = canWrite === undefined ? true : canWrite;
this.isRequired = !!isRequired;
this.isStatic = !!isStatic;
this.defaultValue = defaultValue;
}
}
/**
* Provides reflective information about a type.
*
* @export
* @class TypeInfo
* @extends {ElementInfo}
*/
class TypeInfo extends ElementInfo {
/**
* Creates an instance of TypeInfo.
*
* @param {string} name The type name.
* @param {string} [namespace] The type namespace.
* @param {string} [fullName] Optional. The full name of the type.
* @param {DisplayInfo} [displayInfo] Optional. The display information.
* @param {IPropertyInfo[]} [properties] Optional. The properties.
* @param {Type<*>} [type] Optional. The instantiable type.
* @param {boolean} [isArray] Optional. Indicates whether the type is an array.
* @param {boolean} [isEnum] Optional. Indicates whether the type is an enumeration.
* @param {*} [defaultValue] Optional. The type's default value.
* @param {ITypeInfoRegistry} [registry] The root type info registry.
* @memberof TypeInfo
*/
constructor({ name, namespace, fullName, displayInfo, properties, type, isArray, isEnum, defaultValue, registry, ...args }) {
super({
name: TypeInfo._getName(name, type),
fullName: fullName || TypeInfo._getFullName(name, namespace, type),
displayInfo,
registry,
...args
});
this.type = type;
this.namespace = TypeInfo._getNamespace(namespace, type);
properties = properties && properties.map(p => new PropertyInfo({ ...p, declaringType: this, registry }));
this.properties = (properties || []);
if (this.type) {
Serializable.setTypeFullName(this.type, this.fullName);
}
this.isEnum = !!isEnum;
this.isArray = !!isArray;
this.defaultValue = defaultValue;
}
/**
* Gets a value indicating whether this type is the boolean type.
*
* @type {boolean} True if the type is the boolean type, false otherwise.
* @memberof TypeInfo
*/
get isBoolean() {
return this.fullName === TypeName.BooleanTypeName;
}
/**
* Gets a value indicating whether this type is the number type.
*
* @type {boolean} True if the type is the number type, false otherwise.
* @memberof TypeInfo
*/
get isNumber() {
return this.fullName === TypeName.NumberTypeName;
}
/**
* Gets a value indicating whether this type is the string type.
*
* @type {boolean} True if the type is the string type, false otherwise.
* @memberof TypeInfo
*/
get isString() {
return this.fullName === TypeName.StringTypeName;
}
/**
* Gets a value indicating whether this type is the symbol type.
*
* @type {boolean} True if the type is the symbol type, false otherwise.
* @memberof TypeInfo
*/
get isSymbol() {
return this.fullName === TypeName.SymbolTypeName;
}
/**
* Gets a value indicating whether this type is the any type.
*
* @type {boolean} True if the type is the any type, false otherwise.
* @memberof TypeInfo
*/
get isAny() {
return this.fullName === TypeName.AnyTypeName;
}
static _getName(name, type) {
if (name) {
return name;
}
if (type) {
return type.name;
}
throw new ReflectionError('Either the name or the type name should be provided.');
}
static _getFullName(name, namespace, type) {
name = TypeInfo._getName(name, type);
namespace = TypeInfo._getNamespace(namespace, type);
return namespace ? `${namespace}.${name}` : name;
}
static _getNamespace(namespace, type) {
return namespace || (type ? Serializable.getTypeNamespace(type) : undefined);
}
}
TypeInfoRegistry.prototype.initialize = (registry) => {
registry.register(new TypeInfo({ name: TypeName.AnyTypeName, fullName: 'System.Object', defaultValue: null, registry }), new TypeInfo({ name: TypeName.BooleanTypeName, fullName: 'System.Boolean', defaultValue: false, registry }), new TypeInfo({ name: TypeName.NumberTypeName, fullName: 'System.Double', defaultValue: 0.0, registry }), new TypeInfo({ name: TypeName.StringTypeName, fullName: 'System.String', defaultValue: null, registry }), new TypeInfo({ name: TypeName.ArrayTypeName, fullName: 'System.Array', defaultValue: null, registry }), new TypeInfo({ name: TypeName.ArrayOfAnyTypeName, fullName: 'System.Array`1[[System.Object]]', defaultValue: null, registry }), new TypeInfo({ name: TypeName.ArrayOfByteTypeName, fullName: 'System.Array`1[[System.Byte]]', defaultValue: null, registry }), new TypeInfo({ name: TypeName.SymbolTypeName, fullName: 'System.Symbol', defaultValue: null, registry }), new TypeInfo({ name: TypeName.DateTypeName, fullName: 'System.DateTime', defaultValue: null, registry }));
};
/**
* Generated bundle index. Do not edit.
*/
export { DisplayInfo, ElementInfo, PropertyInfo, ReflectionError, TypeInfo, TypeInfoRegistry, TypeName, ValueElementInfo };
//# sourceMappingURL=kephas-reflection.mjs.map