import {IValidator} from "./validation/validator"; const dcopy = require('deep-copy'); import {IMPARSABLE} from "./common"; import {ImparsableConstructor} from "./parsing/json/Imparsable"; import {Property, PropertyID} from "./property"; import { CollectionParsingDescription, ModelParsingSchema, ParsingSchema } from "./types"; import {createModelParsingDescription} from "./factories/createModelParsingDescription"; import {createParsingDescription} from "./factories/createParsingDescription"; import {DATE_DESCRIPTION} from "./descriptions/Date"; import {MAP_DESCRIPTION} from "./descriptions/Map"; import {SET_DESCRIPTION} from "./descriptions/Set"; //[key, constructor] export type DescriptionRecord = [ImparsableConstructor, ParsingSchema]; export class DescriptionManager { public static decoratePropertyWithParameters(target: T, key: K, parameters: Partial>) { if (parameters.type == null) parameters.type = this.getPropertyType(target, key); let {name = key, parsedName = name} = parameters; let propertyId = new PropertyID(name, parsedName, parameters.getOnly); let schema: ParsingSchema = this.autoCreateComplexDescription(parameters); let property: Property = Object.assign(dcopy(schema), propertyId); this.decorateProperty(target, key, property); } public static decorateProperty(target: T, key: K, property: Property) { let classDescription: ModelParsingSchema = this.getClassDescription( target.constructor as ImparsableConstructor, () => createModelParsingDescription(target.constructor as any) ); classDescription.properties[key as any] = property; // classDescription.classConstructor = target.constructor as any; this.setClassDescription(target.constructor as ImparsableConstructor, classDescription); } private static getPropertyType(instance: I, property: keyof I): any { return (Reflect as any).getMetadata('design:type', instance, property as any); } public static getClassDescription(constructor: ImparsableConstructor, defaultValueFactory: () => ModelParsingSchema = undefined): ModelParsingSchema { let description: ModelParsingSchema = (Reflect as any).getOwnMetadata(IMPARSABLE, constructor); if (!description) { let parentDescription: ModelParsingSchema = (Reflect as any).getMetadata(IMPARSABLE, constructor); if (parentDescription) { description = dcopy(parentDescription); description.type = constructor; } } if (!description) description = defaultValueFactory && defaultValueFactory(); return description; } public static getDescription(constructor: ImparsableConstructor): ParsingSchema { let description: ParsingSchema = this.getClassDescription(constructor); if (!description) description = dcopy(this.getDefaultParsingDescription(constructor)); return description; } public static setClassDescription(constructor: ImparsableConstructor, desc: ModelParsingSchema) { // constructor[IMPARSABLE + '_classDescription'] = desc; (Reflect as any).defineMetadata(IMPARSABLE, desc, constructor); } /** * This method takes schema from class and merge it with provided schema. * @param constructor * @param desc */ public static mergeDescription(constructor: ImparsableConstructor, desc: Partial>) { let description = this.getDescription(constructor); Object.assign(description, desc); this.setClassDescription(constructor, description as any); } private static defaultDescriptions: DescriptionRecord[] = [ [String, createParsingDescription(String)], [Number, createParsingDescription(Number)], [Boolean, createParsingDescription(Boolean)], [Map, MAP_DESCRIPTION], [Set, SET_DESCRIPTION], // [Array, ArrayComplexDescription], [Date, DATE_DESCRIPTION], // [Object, IDictionaryCollectionDescription] ]; public static registerDescription(description: ParsingSchema) { let record: DescriptionRecord = [description.type, description]; this.defaultDescriptions = [record].concat(this.defaultDescriptions); } public static getDefaultParsingDescription(classType: ImparsableConstructor): ParsingSchema { let found = this.defaultDescriptions.find(value => value[0] === classType); if (found) return found[1]; return createParsingDescription(classType); } public static autoCreateComplexDescription(parameters: Partial>): ParsingSchema { let complexDescription = this.getClassDescription(parameters.type); if (complexDescription) { return Object.assign(dcopy(complexDescription), parameters); } else { let description: ParsingSchema = this.getDefaultParsingDescription(parameters.type); return Object.assign(dcopy(description), parameters); } } public static createCollectionComplexDescription(collection: Partial>, collectionOf: Partial>): CollectionParsingDescription { collection.itemProperty = this.autoCreateComplexDescription(collectionOf); return this.autoCreateComplexDescription(collection) as CollectionParsingDescription; } static addValidators(target: T, key: K, validators: IValidator[]) { let schema = this.getClassDescription(target.constructor as any); if (!schema.validators) schema.validators = []; schema.validators.push(...validators); } }