/**
* (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jabylon.properties.xliff;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jabylon.properties.Property;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import static org.jabylon.properties.xliff.XliffXMLConstants.*;
/**
* @author c.samulski (2016-01-30)
*/
public final class XliffWriter {
private XliffWriter() {} // no instantiation necessary
/**
* Basically Main() for this class. Writes an XLIFF 1.2 xml document filled with source/target
* properties passed via the corresponding {@link PropertyWrapper}s to the {@link OutputStream}.<br>
*
*/
public static final void write(OutputStream out, PropertyWrapper filteredSource, PropertyWrapper filteredTarget,
String encoding) throws IOException {
Element rootElement = writeDocument(filteredSource, filteredTarget);
Transformer transformer = getTransformer();
StreamResult result = new StreamResult(out);
DOMSource domSource = new DOMSource(rootElement);
try {
transformer.transform(domSource, result);
} catch (TransformerException e) {
throw new IOException(e);
}
}
/**
* Creates a {@link Document}, writes source/target {@link Property}s to document, adhering to
* XLIFF 1.2 schemas.<br>
*
* @return a populated "xliff" root {@link Element}.<br>
*/
private static Element writeDocument(PropertyWrapper filteredSource, PropertyWrapper filteredTarget) throws IOException {
Document doc = createDocument();
Element rootElement = createRootElement(doc);
Element fileElement = createFileElement(doc, rootElement, filteredSource.getLocale(), filteredTarget.getLocale());
Element bodyElement = doc.createElement(TAG_BODY);
Set<Map.Entry<String, Property>> targetEntries = filteredTarget.getProperties().entrySet();
Map<String, Property> sourceEntries = filteredSource.getProperties();
/* Retrieve source and target translation values. */
for (Map.Entry<String, Property> targetEntry : targetEntries) {
String key = targetEntry.getKey();
String targetValue = getPropertyValueNullAsEmpty(targetEntry.getValue());
String sourceValue = getPropertyValueNullAsEmpty(sourceEntries.get(key));
/* Create populated unit element with the given input */
bodyElement.appendChild(createTransUnitElement(doc, key, sourceValue, targetValue));
}
fileElement.appendChild(bodyElement);
rootElement.appendChild(fileElement);
return rootElement;
}
/**
* @return a "file" {@link Element}, with sourceLanguage and targetLanguage attributes set.<br>
*/
private static Element createFileElement(Document doc, Element rootElement, Locale source, Locale target) {
Element fileElement = doc.createElement(TAG_FILE);
fileElement.setAttribute(ATT_DATATYPE, "plaintext");
fileElement.setAttribute(ATT_SOURCE_LANGUAGE, getLanguageCode(source));
fileElement.setAttribute(ATT_TARGET_LANGUAGE, getLanguageCode(target));
fileElement.setAttribute(ATT_ORIGINAL, "properties");
return fileElement;
}
private static Element createRootElement(Document doc) {
Element rootElement = doc.createElement(TAG_XLIFF);
rootElement.setAttribute(ATT_XMLNS, "urn:oasis:names:tc:xliff:document:1.2");
rootElement.setAttribute(ATT_VERSION, "1.2");
return rootElement;
}
/**
* @return a populated "unit" {@link Element}, with children and text content.<br>
*/
protected static final Element createTransUnitElement(Document doc, String key, String sourceValue, String targetValue) {
Element transUnitElement = doc.createElement(TAG_TRANS_UNIT);
Element sourceElement = doc.createElement(TAG_SOURCE);
Element targetElement = doc.createElement(TAG_TARGET);
sourceElement.setTextContent(sourceValue);
targetElement.setTextContent(targetValue);
transUnitElement.appendChild(sourceElement);
transUnitElement.appendChild(targetElement);
transUnitElement.setAttribute(ATT_ID, key);
return transUnitElement;
}
private static Document createDocument() throws IOException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
return db.newDocument();
} catch (ParserConfigurationException e) {
throw new IOException(e);
}
}
private static Transformer getTransformer() throws IOException {
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
return transformer;
} catch (TransformerConfigurationException e) {
throw new IOException(e);
}
}
private static String getPropertyValueNullAsEmpty(Property value) {
return value == null ? "" : value.getValue() != null ? value.getValue() : "";
}
private static String getLanguageCode(Locale locale) {
if (locale == null) {
return "en";
}
return locale.toLanguageTag();
}
}