import { ParsedMessage } from './parsed-message' ; import { EMPTY_TAG , END_TAG , ICU_MESSAGE , ICU_MESSAGE_REF , ParsedMesageTokenizer , PLACEHOLDER , START_TAG , TEXT , Token } from './parsed-message-tokenizer' ; import { ParsedMessagePartText } from './parsed-message-part-text' ; import { DOMParser } from 'xmldom' ; import { ParsedMessagePartStartTag } from './parsed-message-part-start-tag' ; import { ParsedMessagePartPlaceholder } from './parsed-message-part-placeholder' ; import { ParsedMessagePartEndTag } from './parsed-message-part-end-tag' ; import { IMessageParser } from './i-message-parser' ; import { format , isNullOrUndefined } from 'util' ; import { DOMUtilities } from './dom-utilities' ; import { ParsedMessagePartEmptyTag } from './parsed-message-part-empty-tag' ; import { ParsedMessagePartICUMessageRef } from './parsed-message-part-icu-message-ref' ; import { ParsedMessagePartICUMessage } from './parsed-message-part-icu-message' ; /**
 * Created by roobm on 10.05.2017.
 * A message parser can parse the xml content of a translatable message.
 * It generates a ParsedMessage from it.
 */ export abstract class AbstractMessageParser implements IMessageParser { param xmlElement param sourceMessage public createNormalizedMessageFromXML ( xmlElement , sourceMessage ) { const message = new ParsedMessage ( this , sourceMessage ) ; if ( xmlElement ) { message . setXmlRepresentation ( xmlElement ) ; this . addPartsOfNodeToMessage ( xmlElement , message , false ) ; } return message ; } param xmlString param sourceMessage createNormalizedMessageFromXMLString ( xmlString , sourceMessage ) { const doc = new DOMParser ( ) . parseFromString ( '<dummy>' + xmlString + '</dummy>' , 'text/xml' ) ; const xmlElement = < Element > doc . childNodes . item ( 0 ) ; return this . createNormalizedMessageFromXML ( xmlElement , sourceMessage ) ; } param node param message param includeSelf private addPartsOfNodeToMessage ( node , message , includeSelf ) { let processChildren = true ; if ( includeSelf ) { if ( node . nodeType === node . TEXT_NODE ) { message . addText ( node . textContent ) ; return ; } if ( node . nodeType === node . ELEMENT_NODE ) { processChildren = this . processStartElement ( < Element > node , message ) ; } } if ( processChildren ) { const icuMessageText = this . getICUMessageText ( node ) ; let isICU = ! isNullOrUndefined ( icuMessageText ) ; if ( isICU ) { try { message . addICUMessage ( icuMessageText ) ; } catch ( error ) { console . log ( 'non ICU message: ' , icuMessageText , error ) ; isICU = false ; } } if ( ! isICU ) { const children = node . childNodes ; for ( let i = 0 ; i < children . length ; i ++ ) { this . addPartsOfNodeToMessage ( children . item ( i ) , message , true ) ; } } } if ( node . nodeType === node . ELEMENT_NODE ) { this . processEndElement ( < Element > node , message ) ; } } param node return protected getICUMessageText ( node ) { const children = node . childNodes ; if ( children . length === 0 ) { return null ; } const firstChild = children . item ( 0 ) ; if ( firstChild . nodeType === firstChild . TEXT_NODE ) { if ( this . isICUMessageStart ( firstChild . textContent ) ) { return DOMUtilities . getXMLContent ( < Element > node ) ; } else { return null ; } } else { return null ; } } param text public isICUMessageStart ( text ) { return ParsedMessagePartICUMessage . looksLikeICUMessage ( text ) ; } param elementNode param message return protected abstract processStartElement ( elementNode , message ) ; param elementNode param message protected abstract processEndElement ( elementNode , message ) ; param normalizedString param sourceMessage return public parseNormalizedString ( normalizedString , sourceMessage ) { const message = new ParsedMessage ( this , sourceMessage ) ; const openTags = [ ] ; let tokens ; try { tokens = new ParsedMesageTokenizer ( ) . tokenize ( normalizedString ) ; } catch ( error ) { throw new Error ( format ( 'unexpected error while parsing message: "%s" (parsed "%")' , error . message , normalizedString ) ) ; } tokens . forEach ( ( token ) => { let disp = null ; switch ( token . type ) { case TEXT : message . addText ( token . value . text ) ; break ; case START_TAG : message . addStartTag ( token . value . name , token . value . idcounter ) ; openTags . push ( token . value . name ) ; break ; case END_TAG : message . addEndTag ( token . value . name ) ; if ( openTags . length === 0 || openTags [ openTags . length - 1 ] !== token . value . name ) { throw new Error ( format ( 'unexpected close tag "%s" (parsed "%s")' , token . value . name , normalizedString ) ) ; } openTags . pop ( ) ; break ; case EMPTY_TAG : message . addEmptyTag ( token . value . name , token . value . idcounter ) ; break ; case PLACEHOLDER : disp = ( sourceMessage ) ? sourceMessage . getPlaceholderDisp ( token . value . idcounter ) : null ; message . addPlaceholder ( token . value . idcounter , disp ) ; break ; case ICU_MESSAGE_REF : disp = ( sourceMessage ) ? sourceMessage . getICUMessageRefDisp ( token . value . idcounter ) : null ; message . addICUMessageRef ( token . value . idcounter , disp ) ; break ; case ICU_MESSAGE : throw new Error ( format ( '<ICUMessage/> not allowed here, use parseICUMessage instead (parsed "%")' , normalizedString ) ) ; default : break ; } } ) ; if ( openTags . length > 0 ) { throw new Error ( format ( 'missing close tag "%s" (parsed "%s")' , openTags [ openTags . length - 1 ] , normalizedString ) ) ; } message . setXmlRepresentation ( this . createXmlRepresentation ( message ) ) ; return message ; } param icuMessageString param sourceMessage return parseICUMessage ( icuMessageString , sourceMessage ) { const message = new ParsedMessage ( this , sourceMessage ) ; message . addICUMessage ( icuMessageString ) ; return message ; } param name return protected parseIdCountFromName ( name ) { const regex = /.*_([0-9]*)/ ; const match = regex . exec ( name ) ; if ( isNullOrUndefined ( match ) || match [ 1 ] === '' ) { return 0 ; } else { const num = match [ 1 ] ; return parseInt ( num , 10 ) ; } } param message protected createXmlRepresentation ( message ) { const root = new DOMParser ( ) . parseFromString ( '<dummy/>' , 'text/xml' ) ; const rootElem = root . getElementsByTagName ( 'dummy' ) . item ( 0 ) ; this . addXmlRepresentationToRoot ( message , rootElem ) ; return rootElem ; } protected abstract addXmlRepresentationToRoot ( message , rootElem ) ; protected createXmlRepresentationOfTextPart ( part , rootElem ) { return rootElem . ownerDocument . createTextNode ( part . asDisplayString ( ) ) ; } param part param rootElem param id protected abstract createXmlRepresentationOfStartTagPart ( part , rootElem , id ? ) ; param part param rootElem protected abstract createXmlRepresentationOfEndTagPart ( part , rootElem ) ; param part param rootElem param id protected abstract createXmlRepresentationOfEmptyTagPart ( part , rootElem , id ? ) ; param part param rootElem param id protected abstract createXmlRepresentationOfPlaceholderPart ( part , rootElem , id ? ) ; param part param rootElem protected abstract createXmlRepresentationOfICUMessageRefPart ( part , rootElem ) ; }