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 {TagMapping} from './tag-mapping';
import {ParsedMessagePartEmptyTag} from './parsed-message-part-empty-tag';
import {ParsedMessagePartICUMessageRef} from './parsed-message-part-icu-message-ref';
import {ParsedMessagePartType} from './parsed-message-part';
import {ParsedMessagePartText} from './parsed-message-part-text';
/**
* Created by roobm on 10.05.2017.
* A message parser for XLIFF 1.2
*/
export class XliffMessageParser extends AbstractMessageParser {
/**
* Handle this element node.
* This is called before the children are done.
* @param elementNode elementNode
* @param message message to be altered
* @return true, if children should be processed too, false otherwise (children ignored then)
*/
protected processStartElement(elementNode: Element, message: ParsedMessage): boolean {
const tagName = elementNode.tagName;
const tagMapping = new TagMapping();
if (tagName === 'x') {
// placeholder are like or
const id = elementNode.getAttribute('id');
if (!id) {
return; // should not happen
}
if (id.startsWith('INTERPOLATION')) {
const index = this.parsePlaceholderIndexFromId(id);
message.addPlaceholder(index, null);
} else if (id.startsWith('ICU')) {
const index = this.parseICUMessageRefIndexFromId(id);
message.addICUMessageRef(index, null);
} else if (id.startsWith('START_')) {
const normalizedTagName = tagMapping.getTagnameFromStartTagPlaceholderName(id);
if (normalizedTagName) {
const idcount = this.parseIdCountFromName(id);
message.addStartTag(normalizedTagName, idcount);
}
} else if (id.startsWith('CLOSE_')) {
const normalizedTagName = tagMapping.getTagnameFromCloseTagPlaceholderName(id);
if (normalizedTagName) {
message.addEndTag(normalizedTagName);
}
} else if (tagMapping.isEmptyTagPlaceholderName(id)) {
const normalizedTagName = tagMapping.getTagnameFromEmptyTagPlaceholderName(id);
if (normalizedTagName) {
const idcount = this.parseIdCountFromName(id);
message.addEmptyTag(normalizedTagName, idcount);
}
}
}
return true;
}
/**
* Handle end of this element node.
* This is called after all children are processed.
* @param elementNode elementNode
* @param message message to be altered
*/
protected processEndElement(elementNode: Element, message: ParsedMessage) {
}
/**
* Parse id attribute of x element as placeholder index.
* id can be "INTERPOLATION" or "INTERPOLATION_n"
* @param id id
* @return index
*/
private parsePlaceholderIndexFromId(id: string): number {
let indexString = '';
if (id === 'INTERPOLATION') {
indexString = '0';
} else {
indexString = id.substring('INTERPOLATION_'.length);
}
return Number.parseInt(indexString, 10);
}
/**
* Parse id attribute of x element as placeholder index.
* id can be "INTERPOLATION" or "INTERPOLATION_n"
* @param id id
* @return id as number
*/
private parseICUMessageRefIndexFromId(id: string): number {
let indexString = '';
if (id === 'ICU') {
indexString = '0';
} else {
indexString = id.substring('ICU_'.length);
}
return Number.parseInt(indexString, 10);
}
protected addXmlRepresentationToRoot(message: ParsedMessage, rootElem: Element) {
message.parts().forEach((part) => {
let child: Node;
switch (part.type) {
case ParsedMessagePartType.TEXT:
child = this.createXmlRepresentationOfTextPart( part, rootElem);
break;
case ParsedMessagePartType.START_TAG:
child = this.createXmlRepresentationOfStartTagPart((part), rootElem);
break;
case ParsedMessagePartType.END_TAG:
child = this.createXmlRepresentationOfEndTagPart((part), rootElem);
break;
case ParsedMessagePartType.EMPTY_TAG:
child = this.createXmlRepresentationOfEmptyTagPart((part), rootElem);
break;
case ParsedMessagePartType.PLACEHOLDER:
child = this.createXmlRepresentationOfPlaceholderPart((part), rootElem);
break;
case ParsedMessagePartType.ICU_MESSAGE_REF:
child = this.createXmlRepresentationOfICUMessageRefPart((part), rootElem);
break;
}
if (child) {
rootElem.appendChild(child);
}
});
}
/**
* the xml used for start tag in the message.
* Returns an empty -Element with attributes id and ctype
* @param part part
* @param rootElem rootElem
*/
protected createXmlRepresentationOfStartTagPart(part: ParsedMessagePartStartTag, rootElem: Element): Node {
const xElem = rootElem.ownerDocument.createElement('x');
const tagMapping = new TagMapping();
const idAttrib = tagMapping.getStartTagPlaceholderName(part.tagName(), part.idCounter());
const ctypeAttrib = tagMapping.getCtypeForTag(part.tagName());
const equivTextAttr = '<' + part.tagName() + '>';
xElem.setAttribute('id', idAttrib);
xElem.setAttribute('ctype', ctypeAttrib);
xElem.setAttribute('equiv-text', equivTextAttr);
return xElem;
}
/**
* the xml used for end tag in the message.
* Returns an empty -Element with attributes id and ctype
* @param part part
* @param rootElem rootElem
*/
protected createXmlRepresentationOfEndTagPart(part: ParsedMessagePartEndTag, rootElem: Element): Node {
const xElem = rootElem.ownerDocument.createElement('x');
const tagMapping = new TagMapping();
const idAttrib = tagMapping.getCloseTagPlaceholderName(part.tagName());
const ctypeAttrib = 'x-' + part.tagName();
xElem.setAttribute('id', idAttrib);
xElem.setAttribute('ctype', ctypeAttrib);
return xElem;
}
/**
* the xml used for empty tag in the message.
* Returns an empty -Element with attributes id and ctype
* @param part part
* @param rootElem rootElem
*/
protected createXmlRepresentationOfEmptyTagPart(part: ParsedMessagePartEmptyTag, rootElem: Element): Node {
const xElem = rootElem.ownerDocument.createElement('x');
const tagMapping = new TagMapping();
const idAttrib = tagMapping.getEmptyTagPlaceholderName(part.tagName(), part.idCounter());
const ctypeAttrib = tagMapping.getCtypeForTag(part.tagName());
const equivTextAttr = '<' + part.tagName() + '/>';
xElem.setAttribute('id', idAttrib);
xElem.setAttribute('ctype', ctypeAttrib);
xElem.setAttribute('equiv-text', equivTextAttr);
return xElem;
}
/**
* the xml used for placeholder in the message.
* Returns an empty -Element with attribute id="INTERPOLATION" or id="INTERPOLATION_n"
* @param part part
* @param rootElem rootElem
*/
protected createXmlRepresentationOfPlaceholderPart(part: ParsedMessagePartPlaceholder, rootElem: Element): Node {
const xElem = rootElem.ownerDocument.createElement('x');
let idAttrib = 'INTERPOLATION';
if (part.index() > 0) {
idAttrib = 'INTERPOLATION_' + part.index().toString(10);
}
const equivTextAttr = part.disp();
xElem.setAttribute('id', idAttrib);
if (equivTextAttr) {
xElem.setAttribute('equiv-text', equivTextAttr);
}
return xElem;
}
/**
* the xml used for icu message refs in the message.
* @param part part
* @param rootElem rootElem
*/
protected createXmlRepresentationOfICUMessageRefPart(part: ParsedMessagePartICUMessageRef, rootElem: Element): Node {
const xElem = rootElem.ownerDocument.createElement('x');
let idAttrib = 'ICU';
if (part.index() > 0) {
idAttrib = 'ICU_' + part.index().toString(10);
}
xElem.setAttribute('id', idAttrib);
return xElem;
}
}