import { ResultCallback } from "../../core/callback" ; import { MappingModel } from "../mappingModel" ; import { MappingProvider , Configuration } from "../../config/configuration" ; import { Type , Property } from "reflect-helper" ; import { MappingBuilderAnnotation , TargetClassAnnotation } from "./annotations" ; import { Constructor } from "../../index" ; import { MappingBuilderContext } from "./mappingBuilderContext" ; import { MappingBuilder } from "./mappingBuilder" ; import { PersistenceError } from "../../persistenceError" ; export class AnnotationMappingProvider implements MappingProvider { hidden private _modules : Object [ ] = [ ] ; constructor ( modules ? ) { if ( modules ) { if ( ! Array . isArray ( modules ) ) { modules = [ < Object > modules ] ; } this . addModules ( < Object [ ] > modules ) ; } } addModule ( module ) { if ( module ) { this . _modules . push ( module ) ; } } addModules ( modules ) { if ( modules ) { for ( let module of modules ) { this . addModule ( module ) ; } } } getMapping ( config , callback ) { var builder = new Builder ( config ) ; var mappings = builder . build ( this . _modules ) ; if ( builder . hasErrors ) { callback ( new PersistenceError ( builder . getErrorMessage ( ) ) , null ) ; return ; } process . nextTick ( ( ) => callback ( null , mappings ) ) ; } } class Builder { private _context : MappingBuilderContext ; private _seenTypes = new Set < any > ( ) ; constructor ( config ) { this . _context = new MappingBuilderContext ( config ) ; } build ( modules ) { this . _addGlobalMapping ( "String" , MappingModel . createStringMapping ( ) ) ; this . _addGlobalMapping ( "Number" , MappingModel . createNumberMapping ( ) ) ; this . _addGlobalMapping ( "Boolean" , MappingModel . createBooleanMapping ( ) ) ; this . _addGlobalMapping ( "Date" , MappingModel . createDateMapping ( ) ) ; this . _addGlobalMapping ( "RegExp" , MappingModel . createRegExpMapping ( ) ) ; this . _addGlobalMapping ( "Buffer" , MappingModel . createBufferMapping ( ) ) ; for ( let module of modules ) { this . _processModule ( module ) ; } return this . _context . populateMappings ( ) ; } get hasErrors ( ) : boolean { return this . _context . errors . length > 0 ; } getErrorMessage ( ) { return "Unable to build type mappings:\n" + this . _context . errors . join ( "\n" ) ; } private _addGlobalMapping ( name , mapping ) { var ctr = ( < any > global ) [ name ] ; if ( ! ctr ) { this . _context . addError ( "Could not find global type '" + name + '.' ) ; return ; } var type = this . _context . getType ( ctr ) ; this . _context . addBuilder ( new MappingBuilder ( this . _context , type , mapping ) ) ; } private _processModule ( obj ) { if ( typeof obj === "function" ) { this . _findTypes ( this . _context . getType ( obj ) ) ; } else if ( typeof obj !== "object" ) { return ; } for ( let p in obj ) { if ( obj . hasOwnProperty ( p ) ) { this . _processModule ( obj [ p ] ) ; } } } private _findTypes ( type ) { if ( ! type || this . _seenTypes . has ( type ) ) { return ; } this . _seenTypes . add ( type ) ; if ( type . baseType ) { this . _findTypes ( type . baseType ) ; } for ( let annotation of < MappingBuilderAnnotation [ ] > type . getAnnotations ( ) ) { if ( annotation . createBuilder ) { this . _context . addBuilder ( annotation . createBuilder ( this . _context , type ) ) ; break ; } } this . _scanPropertiesForTypes ( type ) ; } private _scanPropertiesForTypes ( type ) { for ( var property of type . properties ) { this . _findTypes ( this . _getPropertyTypeToScan ( property ) ) ; } } private _getPropertyTypeToScan ( property ) { for ( let annotation of < TargetClassAnnotation [ ] > property . getAnnotations ( ) ) { if ( annotation . target ) { if ( typeof annotation . target === "string" ) { return null ; } return this . _context . getType ( < Constructor < any > > annotation . target ) ; } } return property . type ; } }