import * as os from 'os' ; import { Class } from '../Components/Class' ; import { Enum } from '../Components/Enum' ; import { EnumValue } from '../Components/EnumValue' ; import { File } from '../Components/File' ; import { Interface } from '../Components/Interface' ; import { Method } from '../Components/Method' ; import { Namespace } from '../Components/Namespace' ; import { Parameter } from '../Components/Parameter' ; import { Property } from '../Components/Property' ; import { TypeParameter } from '../Components/TypeParameter' ; import { ComponentKind } from './ComponentKind' ; import { ICommandOptions } from './ICommandOptions' ; import { IComponentComposite } from './IComponentComposite' ; const REGEX_ONLY_TYPE_NAMES = /\w+/g ; const REGEX_TYPE_NAMES_WITH_ARRAY = /\w+(?:\[\])?/g ; /**
 * Define a format for class diagram
 */ export abstract class Formatter { /**
     * Options sent to the cli
     */ protected options : ICommandOptions ; constructor ( options ) { this . options = options ; } public header ( ) { return [ ] ; } public footer ( ) { return [ ] ; } public addAssociation ( type1 , cardinality , type2 ) { return [ ] ; } public serializeFile ( file ) { const result = [ ] ; file . parts . forEach ( ( part ) : void => { result . push ( this . serialize ( part ) ) ; } ) ; return result . join ( os . EOL ) ; } public abstract serializeClass ( component ) ; public abstract serializeEnum ( component ) ; public serializeEnumValue ( component ) { return component . name ; } public abstract serializeInterface ( component ) ; public abstract serializeMethod ( component ) ; public abstract serializeNamespace ( component ) ; public abstract serializeParameter ( component ) ; public abstract serializeProperty ( component ) ; public abstract serializeTypeParameter ( component ) ; public serialize ( component ) { if ( component . componentKind === ComponentKind . CLASS ) { return this . serializeClass ( < Class > component ) ; } else if ( component . componentKind === ComponentKind . FILE ) { return this . serializeFile ( < File > component ) ; } else if ( component . componentKind === ComponentKind . ENUM ) { return this . serializeEnum ( < Enum > component ) ; } else if ( component . componentKind === ComponentKind . ENUM_VALUE ) { return this . serializeEnumValue ( < EnumValue > component ) ; } else if ( component . componentKind === ComponentKind . INTERFACE ) { return this . serializeInterface ( < Interface > component ) ; } else if ( component . componentKind === ComponentKind . METHOD ) { return this . serializeMethod ( < Method > component ) ; } else if ( component . componentKind === ComponentKind . NAMESPACE ) { return this . serializeNamespace ( < Namespace > component ) ; } else if ( component . componentKind === ComponentKind . PARAMETER ) { return this . serializeParameter ( < Parameter > component ) ; } else if ( component . componentKind === ComponentKind . PROPERTY ) { return this . serializeProperty ( < Property > component ) ; } else if ( component . componentKind === ComponentKind . TYPE_PROPERTY ) { return this . serializeTypeParameter ( < TypeParameter > component ) ; } throw new Error ( 'Unknown Component' ) ; } public renderFiles ( files , associations ) { const lines = [ ] ; lines . push ( ... this . header ( ) ) ; files . forEach ( ( file ) : void => { const conversion = this . serialize ( file ) ; if ( conversion !== '' ) { lines . push ( conversion ) ; } } ) ; if ( associations ) { lines . push ( ... this . createAssociations ( files ) ) ; } lines . push ( ... this . footer ( ) ) ; return lines . join ( os . EOL ) ; } public createAssociations ( files ) { const associations = [ ] ; const mappedTypes = { } ; const outputConstraints = { } ; files . forEach ( ( file ) : void => { ( < File > file ) . parts . forEach ( ( part ) : void => { if ( part . componentKind === ComponentKind . CLASS || part . componentKind === ComponentKind . INTERFACE || part . componentKind === ComponentKind . ENUM ) { mappedTypes [ part . name ] = true ; } } ) ; } ) ; files . forEach ( ( file ) : void => { if ( file . componentKind !== ComponentKind . FILE ) { return ; } ( < File > file ) . parts . forEach ( ( part ) : void => { if ( ! ( part instanceof Class ) && ! ( part instanceof Interface ) ) { return ; } part . members . forEach ( ( member ) : void => { let checks = [ ] ; if ( member instanceof Method ) { member . parameters . forEach ( ( parameter ) : void => { const parameters = ( < Parameter > parameter ) . parameterType . match ( REGEX_ONLY_TYPE_NAMES ) ; if ( parameters !== null ) { checks = checks . concat ( parameters ) ; } } ) ; } const returnTypes = ( < Method | Property > member ) . returnType . match ( REGEX_TYPE_NAMES_WITH_ARRAY ) ; if ( returnTypes !== null ) { checks = checks . concat ( returnTypes ) ; } for ( const tempTypeName of checks ) { let typeName = tempTypeName ; let cardinality = '1' ; if ( tempTypeName . endsWith ( '[]' ) ) { cardinality = '*' ; typeName = typeName . substring ( 0 , typeName . indexOf ( '[]' ) ) ; } const key = `${ part . name } ${ cardinality } ${ typeName }` ; if ( typeName !== part . name && ! outputConstraints . hasOwnProperty ( key ) && mappedTypes . hasOwnProperty ( typeName ) ) { associations . push ( ... this . addAssociation ( part . name , cardinality , typeName ) ) ; outputConstraints [ key ] = true ; } } } ) ; } ) ; } ) ; return associations ; } }