import { AbstractMessageParser } from './abstract-message-parser' ; import { ParsedMessage } from './parsed-message' ; import { ParsedMessagePartStartTag } from './parsed-message-part-start-tag' ; import { ParsedMessagePartEndTag } from './parsed-message-part-end-tag' ; import { ParsedMessagePartPlaceholder } from './parsed-message-part-placeholder' ; import { ParsedMessagePartText } from './parsed-message-part-text' ; import { ParsedMessagePartType } from './parsed-message-part' ; import { TagMapping } from './tag-mapping' ; import { ParsedMessagePartEmptyTag } from './parsed-message-part-empty-tag' ; import { ParsedMessagePartICUMessageRef } from './parsed-message-part-icu-message-ref' ; /**
 * Created by roobm on 10.05.2017.
 * A message parser for XLIFF 2.0
 */ export class Xliff2MessageParser extends AbstractMessageParser { param elementNode param message return protected processStartElement ( elementNode , message ) { const tagName = elementNode . tagName ; if ( tagName === 'ph' ) { let isInterpolation = false ; let isICU = false ; let isEmptyTag = false ; const equiv = elementNode . getAttribute ( 'equiv' ) ; const disp = elementNode . getAttribute ( 'disp' ) ; let indexString = null ; let index = 0 ; let emptyTagName = null ; if ( ! equiv ) { isICU = true ; indexString = elementNode . getAttribute ( 'id' ) ; index = Number . parseInt ( indexString , 10 ) ; } else if ( equiv . startsWith ( 'ICU' ) ) { isICU = true ; if ( equiv === 'ICU' ) { indexString = '0' ; } else { indexString = equiv . substring ( 'ICU_' . length ) ; } index = Number . parseInt ( indexString , 10 ) ; } else if ( equiv . startsWith ( 'INTERPOLATION' ) ) { isInterpolation = true ; if ( equiv === 'INTERPOLATION' ) { indexString = '0' ; } else { indexString = equiv . substring ( 'INTERPOLATION_' . length ) ; } index = Number . parseInt ( indexString , 10 ) ; } else if ( new TagMapping ( ) . isEmptyTagPlaceholderName ( equiv ) ) { isEmptyTag = true ; emptyTagName = new TagMapping ( ) . getTagnameFromEmptyTagPlaceholderName ( equiv ) ; } else { return true ; } if ( isInterpolation ) { message . addPlaceholder ( index , disp ) ; } else if ( isICU ) { message . addICUMessageRef ( index , disp ) ; } else if ( isEmptyTag ) { message . addEmptyTag ( emptyTagName , this . parseIdCountFromName ( equiv ) ) ; } } else if ( tagName === 'pc' ) { const embeddedTagName = this . tagNameFromPCElement ( elementNode ) ; if ( embeddedTagName ) { message . addStartTag ( embeddedTagName , this . parseIdCountFromName ( elementNode . getAttribute ( 'equivStart' ) ) ) ; } } return true ; } param elementNode param message protected processEndElement ( elementNode , message ) { const tagName = elementNode . tagName ; if ( tagName === 'pc' ) { const embeddedTagName = this . tagNameFromPCElement ( elementNode ) ; if ( embeddedTagName ) { message . addEndTag ( embeddedTagName ) ; } return ; } } private tagNameFromPCElement ( pcNode ) { let dispStart = pcNode . getAttribute ( 'dispStart' ) ; if ( dispStart . startsWith ( '<' ) ) { dispStart = dispStart . substring ( 1 ) ; } if ( dispStart . endsWith ( '>' ) ) { dispStart = dispStart . substring ( 0 , dispStart . length - 1 ) ; } return dispStart ; } param message param rootElem protected addXmlRepresentationToRoot ( message , rootElem ) { const stack = [ { element : rootElem , tagName : 'root' } ] ; let id = 0 ; message . parts ( ) . forEach ( ( part ) => { switch ( part . type ) { case ParsedMessagePartType . TEXT : stack [ stack . length - 1 ] . element . appendChild ( this . createXmlRepresentationOfTextPart ( < ParsedMessagePartText > part , rootElem ) ) ; break ; case ParsedMessagePartType . PLACEHOLDER : stack [ stack . length - 1 ] . element . appendChild ( this . createXmlRepresentationOfPlaceholderPart ( < ParsedMessagePartPlaceholder > part , rootElem , id ++ ) ) ; break ; case ParsedMessagePartType . ICU_MESSAGE_REF : stack [ stack . length - 1 ] . element . appendChild ( this . createXmlRepresentationOfICUMessageRefPart ( < ParsedMessagePartICUMessageRef > part , rootElem ) ) ; break ; case ParsedMessagePartType . START_TAG : const newTagElem = this . createXmlRepresentationOfStartTagPart ( < ParsedMessagePartStartTag > part , rootElem , id ++ ) ; stack [ stack . length - 1 ] . element . appendChild ( newTagElem ) ; stack . push ( { element : < Element > newTagElem , tagName : ( < ParsedMessagePartStartTag > part ) . tagName ( ) } ) ; break ; case ParsedMessagePartType . END_TAG : const closeTagName = ( < ParsedMessagePartEndTag > part ) . tagName ( ) ; if ( stack . length <= 1 || stack [ stack . length - 1 ] . tagName !== closeTagName ) { throw new Error ( 'unexpected close tag ' + closeTagName ) ; } stack . pop ( ) ; break ; case ParsedMessagePartType . EMPTY_TAG : const emptyTagElem = this . createXmlRepresentationOfEmptyTagPart ( < ParsedMessagePartEmptyTag > part , rootElem , id ++ ) ; stack [ stack . length - 1 ] . element . appendChild ( emptyTagElem ) ; break ; } } ) ; if ( stack . length !== 1 ) { throw new Error ( 'missing close tag ' + stack [ stack . length - 1 ] . tagName ) ; } } param part param rootElem param id protected createXmlRepresentationOfStartTagPart ( part , rootElem , id ) { const tagMapping = new TagMapping ( ) ; const pcElem = rootElem . ownerDocument . createElement ( 'pc' ) ; const tagName = part . tagName ( ) ; const equivStart = tagMapping . getStartTagPlaceholderName ( tagName , part . idCounter ( ) ) ; const equivEnd = tagMapping . getCloseTagPlaceholderName ( tagName ) ; const dispStart = '<' + tagName + '>' ; const dispEnd = '</' + tagName + '>' ; pcElem . setAttribute ( 'id' , id . toString ( 10 ) ) ; pcElem . setAttribute ( 'equivStart' , equivStart ) ; pcElem . setAttribute ( 'equivEnd' , equivEnd ) ; pcElem . setAttribute ( 'type' , this . getTypeForTag ( tagName ) ) ; pcElem . setAttribute ( 'dispStart' , dispStart ) ; pcElem . setAttribute ( 'dispEnd' , dispEnd ) ; return pcElem ; } param part param rootElem protected createXmlRepresentationOfEndTagPart ( part , rootElem ) { return null ; } param part param rootElem param id protected createXmlRepresentationOfEmptyTagPart ( part , rootElem , id ) { const tagMapping = new TagMapping ( ) ; const phElem = rootElem . ownerDocument . createElement ( 'ph' ) ; const tagName = part . tagName ( ) ; const equiv = tagMapping . getEmptyTagPlaceholderName ( tagName , part . idCounter ( ) ) ; const disp = '<' + tagName + '/>' ; phElem . setAttribute ( 'id' , id . toString ( 10 ) ) ; phElem . setAttribute ( 'equiv' , equiv ) ; phElem . setAttribute ( 'type' , this . getTypeForTag ( tagName ) ) ; phElem . setAttribute ( 'disp' , disp ) ; return phElem ; } private getTypeForTag ( tag ) { switch ( tag . toLowerCase ( ) ) { case 'br' : case 'b' : case 'i' : case 'u' : return 'fmt' ; case 'img' : return 'image' ; case 'a' : return 'link' ; default : return 'other' ; } } param part param rootElem param id protected createXmlRepresentationOfPlaceholderPart ( part , rootElem , id ) { const phElem = rootElem . ownerDocument . createElement ( 'ph' ) ; let equivAttrib = 'INTERPOLATION' ; if ( part . index ( ) > 0 ) { equivAttrib = 'INTERPOLATION_' + part . index ( ) . toString ( 10 ) ; } phElem . setAttribute ( 'id' , id . toString ( 10 ) ) ; phElem . setAttribute ( 'equiv' , equivAttrib ) ; const disp = part . disp ( ) ; if ( disp ) { phElem . setAttribute ( 'disp' , disp ) ; } return phElem ; } param part param rootElem protected createXmlRepresentationOfICUMessageRefPart ( part , rootElem ) { const phElem = rootElem . ownerDocument . createElement ( 'ph' ) ; let equivAttrib = 'ICU' ; if ( part . index ( ) > 0 ) { equivAttrib = 'ICU_' + part . index ( ) . toString ( 10 ) ; } phElem . setAttribute ( 'id' , part . index ( ) . toString ( 10 ) ) ; phElem . setAttribute ( 'equiv' , equivAttrib ) ; const disp = part . disp ( ) ; if ( disp ) { phElem . setAttribute ( 'disp' , disp ) ; } return phElem ; } }