package cz.tomaskypta.tools.langtool.importing; import java.io.*; import java.util.HashMap; import java.util.Iterator; 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.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import cz.tomaskypta.tools.langtool.util.EscapingUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class ToolImport { private DocumentBuilder builder; private File outResDir; private PrintStream out; private HashMap<String, String> mMapping; private ImportConfig mConfig; public ToolImport(PrintStream out) throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); builder = dbf.newDocumentBuilder(); this.out = out == null ? System.out : out; } public static void run(ImportConfig config) throws IOException, ParserConfigurationException, TransformerException { if (config == null) { System.err.println("Cannot import, missing config"); return; } if (StringUtils.isEmpty(config.inputFile)) { System.err.println("Cannot import, missing input file name"); return; } HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(new File(config.inputFile))); HSSFSheet sheet = wb.getSheetAt(0); HSSFSheet sheetMapping = null; if (!StringUtils.isEmpty(config.mappingFile)) { HSSFWorkbook wbMapping = new HSSFWorkbook(new FileInputStream(new File(config.mappingFile))); sheetMapping = wbMapping.getSheetAt(0); } String outputDirName = config.outputDirName; if (StringUtils.isEmpty(outputDirName)) { outputDirName = sheet.getSheetName(); } if (config.outputFileName == null) { config.outputFileName = "strings.xml"; } ToolImport tool = new ToolImport(null); tool.mConfig = config; tool.outResDir = new File("out/" + outputDirName + "/res"); tool.outResDir.mkdirs(); tool.prepareMapping(sheetMapping); tool.parse(sheet); } public static void run(PrintStream out, String projectDir, String input) throws IOException, ParserConfigurationException, TransformerException { ToolImport tool = new ToolImport(out); if (input == null || "".equals(input)) { tool.out.println("File name is missed"); return; } HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(new File(input))); HSSFSheet sheet = wb.getSheetAt(0); tool.outResDir = new File(projectDir, "/res"); //tool.outResDir.mkdirs(); tool.parse(sheet); } private void prepareMapping(HSSFSheet sheetMapping) { if (sheetMapping == null) { return; } mMapping = new HashMap<String, String>(); Iterator<Row> it = sheetMapping.rowIterator(); while (it.hasNext()) { Row row = it.next(); mMapping.put(row.getCell(0).getStringCellValue(), row.getCell(1).getStringCellValue()); } } private void parse(HSSFSheet sheet) throws IOException, TransformerException { Row row = sheet.getRow(0); Iterator<Cell> cells = row.cellIterator(); cells.next();// ignore key int i = 1; while (cells.hasNext()) { String lang = cells.next().toString(); if (mMapping != null && mMapping.containsKey(lang)) { lang = mMapping.get(lang); } generateLang(sheet, lang, i); i++; } } private void generateLang(HSSFSheet sheet, String lang, int column) throws IOException, TransformerException { Document dom = builder.newDocument(); Element root = dom.createElement("resources"); dom.appendChild(root); Iterator<Row> iterator = sheet.rowIterator(); iterator.next();//ignore first row; Element pluralsNode = null; Element stringArrayNode = null; String plurarName = null; String arrayName = null; while (iterator.hasNext()) { HSSFRow row = (HSSFRow)iterator.next(); Cell cell = row.getCell(0);// android key if (cell == null) { continue; } String key = cell.toString(); if (key == null || "".equals(key)) { root.appendChild(dom.createTextNode("")); continue; } if (key.startsWith("/**")) { root.appendChild(dom.createComment(key.substring(3, key.length() - 3))); continue; } if (key.startsWith("//")) { root.appendChild(dom.createComment(key.substring(2))); continue; } if (mConfig.isIgnoredKey(key)) { root.appendChild(dom.createTextNode("")); continue; } int plurarIndex = key.indexOf("#"); int arrayIndex = key.indexOf("["); if (plurarIndex >= 0) { // plurals Cell valueCell = row.getCell(column); String value = ""; if (valueCell != null) { value = valueCell.toString();// value } String plurarNameNew = key.substring(0, plurarIndex); String quantity = key.substring(plurarIndex + 1); if (!plurarNameNew.equals(plurarName)) { plurarName = plurarNameNew; pluralsNode = dom.createElement("plurals"); pluralsNode.setAttribute("name", plurarName); } value = prepareOutputValue(lang, key, value); addContent(dom, pluralsNode, value, "item", null, quantity); root.appendChild(pluralsNode); } else if (arrayIndex >= 0) { // string-array Cell valueCell = row.getCell(column); String value = ""; if (valueCell != null) { value = valueCell.toString();// value } String arrayNameNew = key.substring(0, arrayIndex); // we don't really need the index // String tmp = key.substring(arrayIndex+1); // int index = Integer.parseInt(tmp.substring(0, tmp.indexOf("]"))); // it's not bullet-proof, but for the time being good enough if (!arrayNameNew.equals(arrayName)) { arrayName = arrayNameNew; stringArrayNode = dom.createElement("string-array"); stringArrayNode.setAttribute("name", arrayName); } value = prepareOutputValue(lang, key, value); addContent(dom, stringArrayNode, value, "item", null, null); root.appendChild(stringArrayNode); } else { //string Cell valueCell = row.getCell(column); if (valueCell == null) { addEmptyKeyValue(dom, root, key); continue; } String value = valueCell.toString();// value if (value.isEmpty()) { addEmptyKeyValue(dom, root, key); } else { value = prepareOutputValue(lang, key, value); addContent(dom, root, value, "string", key, null); } } } save(dom, lang); } private void addContent(Document dom, Element root, String value, String nodeName, String key, String quantity) { if (!mConfig.isMixedContent(key)) { addContentAsString(dom, root, value, nodeName, key, quantity); return; } try { DocumentBuilder db = DocumentBuilderFactory .newInstance() .newDocumentBuilder(); // TODO improve // currently ignoring errors - there were irrelevant messages about '&' db.setErrorHandler(new ErrorHandler() { @Override public void warning(SAXParseException exception) throws SAXException { } @Override public void error(SAXParseException exception) throws SAXException { } @Override public void fatalError(SAXParseException exception) throws SAXException { } }); Element content = db .parse(new ByteArrayInputStream(("<" + nodeName + ">" + value + "</" + nodeName + ">").getBytes())) .getDocumentElement(); if (key != null) { content.setAttribute("name", key); } if (quantity != null) { content.setAttribute("quantity", quantity); } Node tmp = dom.importNode(content, true); root.appendChild(tmp); } catch (Exception e) { addContentAsString(dom, root, value, nodeName, key, quantity); } } private void addContentAsString(Document dom, Element root, String value, String nodeName, String key, String quantity) { Element node = dom.createElement(nodeName); if (key != null) { node.setAttribute("name", key); } if (quantity != null) { node.setAttribute("quantity", quantity); } node.setTextContent(value); root.appendChild(node); } private String prepareOutputValue(String lang, String key, String value) { ImportConfig.Transformation tranformation = mConfig.getKeyTransformation(key); if (tranformation != null) { value = tranformation.apply(value, lang); } if (mConfig.unescapeFirst) { value = EscapingUtils.unescapeQuotes(value); } if (mConfig.isEscapedKey(key)) { value = EscapingUtils.escapeWithQuotes(value); } else { value = EscapingUtils.escapeWithBackslash(value); } return value; } private static void addEmptyKeyValue(Document dom, Element root, String key) { root.appendChild(dom.createComment(String.format(" TODO: string name=\"%s\" ", key))); } private void save(Document doc, String lang) throws TransformerException { File dir; if ("default".equals(lang) || lang == null || "".equals(lang)) { dir = new File(outResDir, "values"); } else { dir = new File(outResDir, "values-" + lang); } dir.mkdir(); //DOMUtils.prettyPrint(doc); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(new File(dir, mConfig.outputFileName)); transformer.transform(source, result); } }