import { SimpleState , SimpleStateScope } from "../Monitor" ; import { State , StateCreationScope } from "../State" ; import { StateAccessingScope } from "../Types" ; import { StateInstance } from "./StateInstance" ; import { StateManagerImpl } from "./StateManagerImpl" ; import { compare } from "./util" ; export class ScopedStateManager { private _stateManager : StateManagerImpl < any > ; private _parent ? : ScopedStateManager ; private _childMap ? : Map < string , ScopedStateManager > ; private _instanceMap = new Map < string , StateInstance > ( ) ; private _path : string ; constructor ( parent , private name ? ) { if ( parent instanceof ScopedStateManager ) { this . _parent = parent ; this . _stateManager = parent . _stateManager ; this . _path = parent . name === undefined ? `/${ name }` : `${ parent . _path }/${ name }` ; } else { this . _stateManager = parent ; this . _path = `/` ; } } get parent ( ) : ScopedStateManager | undefined { return this . _parent ; } get stateManager ( ) : StateManagerImpl < any > { return this . _stateManager ; } get path ( ) : string { return this . _path ; } subScope ( path ) { if ( this . parent !== undefined ) { throw new Error ( 'The function "subScope" can only be supported by root scope' ) ; } if ( path === "/" ) { return this ; } let scope = this ; for ( const name of path . split ( "/" ) ) { scope = scope . childScope ( name ) ; } return scope ; } private childScope ( name ) { if ( name === "" ) { return this ; } let child = this . _childMap ?. get ( name ) ; if ( child === undefined ) { let childMap = this . _childMap ; if ( childMap === undefined ) { this . _childMap = childMap = new Map < string , ScopedStateManager > ( ) ; } childMap . set ( name , child = new ScopedStateManager ( this , name ) ) ; } return child ; } instance ( state , scope ) { const instance = this . getInstance ( state , scope ) ; if ( instance !== undefined ) { return instance ; } return this . createInstance ( state , scope ) ; } private getInstance ( state , scope ) { const instance = this . _instanceMap . get ( state [ " $name" ] ) ; if ( instance !== undefined ) { return instance ; } if ( scope !== "local" && this . _parent ) { return this . _parent . getInstance ( state , scope ) ; } return undefined ; } private createInstance ( state , scope ) { if ( scope !== "local" && this . _parent ) { return this . _parent . createInstance ( state , scope ) ; } const creatingScope = state [ " $options" ] ?. scope ?? "global-scope-only" ; if ( creatingScope === "global-scope-only" && this . _parent ) { throw new Error ( `The state using scope is "local" and current scope is not global scope, but the state is "global-scope-only"` ) ; } const instance = new StateInstance ( this , state ) ; this . _instanceMap . set ( state [ " $name" ] , instance ) ; return instance ; } dispose ( ) { let exception = undefined ; if ( this . _childMap !== undefined ) { for ( const child of this . _childMap ?. values ( ) ) { try { child . dispose ( ) ; } catch ( ex ) { if ( exception === undefined ) { exception = ex ; } } } this . _childMap = undefined ; } for ( const instance of this . _instanceMap . values ( ) ) { try { instance . dispose ( ) ; } catch ( ex ) { if ( exception === undefined ) { exception = ex ; } } } this . _instanceMap . clear ( ) ; if ( exception !== undefined ) { throw exception ; } } monitor ( ) { const states = Array . from ( this . _instanceMap . values ( ) ) . map ( value => value . mintor ( ) ) ; states . sort ( ( a , b ) => compare ( a , b , "name" ) ) ; const scopes = [ ] ; if ( this . _childMap !== undefined ) { for ( const child of this . _childMap . values ( ) ) { scopes . push ( child . monitor ( ) ) ; } } scopes . sort ( ( a , b ) => compare ( a , b , "name" ) ) ; return { name : this . name ?? "" , states , scopes } ; } }