license import { Element , Expansion , ExpansionCase , Node , Text , visitAll } from '@angular/compiler' ; import { BaseVisitor } from '@angular/localize/src/tools/src/translate/translation_files/base_visitor' ; import { TranslationParseError } from '../translation_parsers/translation_parse_error' ; import { getAttrOrThrow , getAttribute } from '../translation_parsers/translation_utils' ; import { MessageRenderer } from './message_renderer' ; interface MessageSerializerConfig { inlineElements : string [ ] ; placeholder ? : { elementName : string ; nameAttribute : string ; bodyAttribute ? : string ; } ; placeholderContainer ? : { elementName : string ; startAttribute : string ; endAttribute : string ; } ; } /**
 * This visitor will walk over a set of XML nodes, which represent an i18n message, and serialize
 * them into a message object of type `T`.
 * The type of the serialized message is controlled by the
 */ export class MessageSerializer < T > extends BaseVisitor { constructor ( private renderer , private config ) { super ( ) ; } serialize ( nodes ) { this . renderer . startRender ( ) ; visitAll ( this , nodes ) ; this . renderer . endRender ( ) ; return this . renderer . message ; } visitElement ( element ) { if ( this . config . placeholder && element . name === this . config . placeholder . elementName ) { const name = getAttrOrThrow ( element , this . config . placeholder . nameAttribute ) ; const body = this . config . placeholder . bodyAttribute && getAttribute ( element , this . config . placeholder . bodyAttribute ) ; this . visitPlaceholder ( name , body ) ; } else if ( this . config . placeholderContainer && element . name === this . config . placeholderContainer . elementName ) { const start = getAttrOrThrow ( element , this . config . placeholderContainer . startAttribute ) ; const end = getAttrOrThrow ( element , this . config . placeholderContainer . endAttribute ) ; this . visitPlaceholderContainer ( start , element . children , end ) ; } else if ( this . config . inlineElements . indexOf ( element . name ) !== - 1 ) { visitAll ( this , element . children ) ; } else { throw new TranslationParseError ( element . sourceSpan , `Invalid element found in message.` ) ; } } visitText ( text ) { this . renderer . text ( text . value ) ; } visitExpansion ( expansion ) { this . renderer . startIcu ( ) ; this . renderer . text ( `${ expansion . switchValue }, ${ expansion . type },` ) ; visitAll ( this , expansion . cases ) ; this . renderer . endIcu ( ) ; } visitExpansionCase ( expansionCase ) { this . renderer . text ( ` ${ expansionCase . value } {` ) ; this . renderer . startContainer ( ) ; visitAll ( this , expansionCase . expression ) ; this . renderer . closeContainer ( ) ; this . renderer . text ( `}` ) ; } visitContainedNodes ( nodes ) { const length = nodes . length ; let index = 0 ; while ( index < length ) { if ( ! this . isPlaceholderContainer ( nodes [ index ] ) ) { const startOfContainedNodes = index ; while ( index < length - 1 ) { index ++ ; if ( this . isPlaceholderContainer ( nodes [ index ] ) ) { break ; } } if ( index - startOfContainedNodes > 1 ) { this . renderer . startContainer ( ) ; visitAll ( this , nodes . slice ( startOfContainedNodes , index - 1 ) ) ; this . renderer . closeContainer ( ) ; } } if ( index < length ) { nodes [ index ] . visit ( this , undefined ) ; } index ++ ; } } visitPlaceholder ( name , body ) { this . renderer . placeholder ( name , body ) ; } visitPlaceholderContainer ( startName , children , closeName ) { this . renderer . startPlaceholder ( startName ) ; this . visitContainedNodes ( children ) ; this . renderer . closePlaceholder ( closeName ) ; } private isPlaceholderContainer ( node ) { return ( node instanceof Element && node . name === this . config . placeholderContainer ! . elementName ) ; } }