import Target from "./Target"; /** * Metadata key for all class annotations * * @type {Symbol} */ const METADATA_CLASS = Symbol('METADATA_CLASS'); /** * Metadata key for all method annotations * * @type {Symbol} */ const METADATA_METHOD = Symbol('METADATA_METHOD'); /** * Metadata key for all property annotations * * @type {Symbol} */ const METADATA_PROPERTY = Symbol('METADATA_PROPERTY'); /** * Default name for read polymorfic structures, like: * * * class_metadata { * DEFAULT_META_KEY: [ Annotation ] * } * property_metadata { * propertyA: [ Annotation ] * propertyB: [ Annotation, Annotation ] * } * method_metadata { * methodA: [ Annotation, Annotation ] * methodB: [ Annotation ] * } * * * @type {string} */ const DEFAULT_META_KEY = 'default'; /** * This is annotations reader class over Reflect Metadata API */ export default class Reader { /** * @type {Function} * @private */ _class: Function; /** * @param {Function} _class */ constructor(_class: Function) { this._class = _class; } /** * @param type * @param key * @private */ _boot(type, key = DEFAULT_META_KEY) { let data = Reflect.getMetadata(type, this._class) || {}; if (typeof data[key] === 'undefined') { data[key] = []; Reflect.defineMetadata(type, data, this._class); } } /** * @param {*} type * @param {string} key * @return {Array} */ getMetadata(type: any, key: string = DEFAULT_META_KEY): Array { this._boot(type, key); let items = Reflect.getMetadata(type, this._class)[key]; // Be sure for items array are immutable return items.slice(0); } /** * @param {Object} annotation * @param {*} type * @param {string} key * @return {void} */ addMetadata(annotation: Object, type: any, key: string = DEFAULT_META_KEY): void { this._boot(type, key); let data = Reflect.getMetadata(type, this._class); let items = data[key]; items.push(annotation); Reflect.defineMetadata(type, data, this._class); } /** * @return {Array} */ getClassAnnotations(): Array { return this.getMetadata(Target.TARGET_CLASS); } /** * @param {string} name * @return {Object|null} */ getClassAnnotation(name: string): ?Object { for (let annotation of this.getClassAnnotations()) { if (annotation.constructor.name === name) { return annotation; } } return null; } /** * @param {Object} annotation * @return {Reader} */ addClassAnnotation(annotation: Object): Reader { this.addMetadata(annotation, Target.TARGET_CLASS); return this; } /** * @param {string} methodName * @return {Array} */ getMethodAnnotations(methodName: string): Array { return this.getMetadata(Target.TARGET_METHOD, methodName); } /** * @param {string} methodName * @param {Object} annotation * @return {Reader} */ addMethodAnnotation(methodName: string, annotation: Object): Reader { this.addMetadata(annotation, Target.TARGET_METHOD, methodName); return this; } /** * @param {string} propertyName * @return {Generator} */ getPropertyAnnotations(propertyName: string): Array { return this.getMetadata(Target.TARGET_PROPERTY, propertyName); } /** * @param {string} propertyName * @param {Object} annotation * @return {Reader} */ addPropertyAnnotation(propertyName: string, annotation: Object): Reader { this.addMetadata(annotation, Target.TARGET_PROPERTY, propertyName); return this; } }