import * as assert from 'assert' ; import * as path from 'path' ; import * as utils from './utils' ; import { Application } from './application' ; import { ScopedObjectContextDef , ScopedObjectContext , TypeDef , ProviderDef , NamedObjectDef } from './object-model' ; import * as fs from 'fs' ; export interface RequestTemplateDef { base ? : string ; application ? : string ; overrideTypes ? : TypeDef [ ] ; overrideProviders ? : ProviderDef [ ] ; overrideObjects ? : NamedObjectDef [ ] ; } export class RequestTemplate { private _uri : string ; private _objectContext : ScopedObjectContext ; private _app : Application ; private _base : RequestTemplate ; public constructor ( uri , app , base , def ) { assert ( uri != null ) ; assert ( app != null ) ; this . _uri = uri ; this . _app = app ; this . _base = base ; let parentContext = base != null ? base . objectContext : app . objectContext ; let contextDef = new ScopedObjectContextDef ( parentContext . def , def . overrideTypes , def . overrideProviders , def . overrideObjects , true ) ; this . _objectContext = new ScopedObjectContext ( `template:${ uri }` , app . objectContext . baseDir , parentContext , contextDef ) ; } public get uri ( ) : string { return this . _uri ; } public get application ( ) : Application { return this . _app ; } public get base ( ) : RequestTemplate { return this . _base ; } public get objectContext ( ) : ScopedObjectContext { return this . _objectContext ; } } export interface RequestTemplateLoader { load ( uri , applicationGetter , baseTemplateGetter ) : RequestTemplate ; getApplicationName ( uri ) : string ; } export class RequestTemplateFileLoader { private static readonly REQUEST_TEMPLATE_SCHEMA : utils . JsonSchema = new utils . JsonSchema ( path . resolve ( __dirname , "../schema/request-template.schema.json" ) ) ; public load ( uri , appGetter , baseTemplateGetter ) { let seenUris = { } ; return this . loadInternal ( uri , appGetter , baseTemplateGetter , seenUris ) ; } private loadInternal ( uri , appGetter , baseTemplateGetter , seenUris ) { let lcUri = uri . toLowerCase ( ) ; if ( seenUris [ lcUri ] != null ) { throw new Error ( `Circle found in template inheritance. Uri: "${ uri }"` ) ; } seenUris [ lcUri ] = true ; let def = < RequestTemplateDef > utils . readConfig ( uri , RequestTemplateFileLoader . REQUEST_TEMPLATE_SCHEMA ) ; let baseDir = path . dirname ( uri ) ; let base = undefined ; let app = undefined ; if ( def . base != null ) { let basePath = def . base ; if ( def . base . startsWith ( '.' ) ) { basePath = path . resolve ( baseDir , def . base ) ; } else { basePath = require . resolve ( def . base ) ; } base = this . loadInternal ( basePath , appGetter , baseTemplateGetter , seenUris ) ; app = base . application ; } else { if ( def . application == null ) { throw new Error ( `Property "application" or "base" must be present in Request Template definition.` ) ; } app = appGetter ( def . application ) ; if ( app == null ) { throw new Error ( `Application "${ def . application }" is not registered in current host.` ) ; } } return new RequestTemplate ( uri , app , base , def ) ; } public getApplicationName ( uri ) { let def = < RequestTemplateDef > utils . readConfig ( uri , RequestTemplateFileLoader . REQUEST_TEMPLATE_SCHEMA ) ; if ( def [ "base" ] != null ) { return this . getApplicationName ( path . resolve ( path . dirname ( uri ) , def . base ) ) ; } else { assert ( def . application != null ) ; return def . application ; } } } class RequestTemplateReference { public template : RequestTemplate ; public refCount : number ; constructor ( template , refCount ) { this . template = template ; this . refCount = refCount ; } } export class RequestTemplateManager { private _loader : RequestTemplateLoader ; private _appGetter : ( name : string ) => Application ; private _cache : Map < string , RequestTemplateReference > = new Map < string , RequestTemplateReference > ( ) ; public constructor ( loader , appGetter ) { this . _loader = loader ; this . _appGetter = appGetter ; } public get ( uri ) { if ( this . _cache . has ( uri ) ) { return this . _cache . get ( uri ) . template ; } return undefined ; } private load ( uri ) { let thisTemplate = this . _loader . load ( uri , this . _appGetter , ( baseUri ) : RequestTemplate => { return this . get ( baseUri ) ; } ) ; let t = thisTemplate ; while ( t != null ) { let uri = t . uri ; if ( this . _cache . has ( uri ) ) { let ref = this . _cache . get ( uri ) ; ref . refCount ++ ; } else { this . _cache . set ( uri , new RequestTemplateReference ( t , 1 ) ) ; } t = t . base ; } return thisTemplate ; } public getOrLoad ( uri ) { let t = this . get ( uri ) ; if ( t == null ) { return this . load ( uri ) ; } return t ; } public unload ( uri ) { if ( ! this . _cache . has ( uri ) ) { return ; } while ( true ) { assert ( this . _cache . has ( uri ) ) ; let r = this . _cache . get ( uri ) ; if ( -- r . refCount == 0 ) { this . _cache . delete ( uri ) ; } if ( r . template . base == null ) { break ; } uri = r . template . base . uri ; } } public get loadedTemplates ( ) : string [ ] { let uris = [ ] ; this . _cache . forEach ( ( r , uri ) => { uris . push ( uri ) ; } ) ; return uris ; } }