| /* |
| ****************************************************************************** |
| * Copyright (C) 2003-2013, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ****************************************************************************** |
| */ |
| |
| package com.ibm.icu.dev.tool.localeconverter; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.text.MessageFormat; |
| import java.util.Date; |
| |
| import javax.xml.XMLConstants; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.validation.Schema; |
| import javax.xml.validation.SchemaFactory; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| import com.ibm.icu.dev.tool.UOption; |
| |
| public final class XLIFF2ICUConverter { |
| |
| /** |
| * These must be kept in sync with getOptions(). |
| */ |
| private static final int HELP1 = 0; |
| private static final int HELP2 = 1; |
| private static final int SOURCEDIR = 2; |
| private static final int DESTDIR = 3; |
| private static final int TARGETONLY = 4; |
| private static final int SOURCEONLY = 5; |
| private static final int MAKE_SOURCE_ROOT = 6; |
| private static final int XLIFF_1_0 = 7; |
| |
| private static final UOption[] options = new UOption[] { |
| UOption.HELP_H(), |
| UOption.HELP_QUESTION_MARK(), |
| UOption.SOURCEDIR(), |
| UOption.DESTDIR(), |
| UOption.create("target-only", 't', UOption.OPTIONAL_ARG), |
| UOption.create("source-only", 'c', UOption.OPTIONAL_ARG), |
| UOption.create("make-source-root", 'r', UOption.NO_ARG), |
| UOption.create("xliff-1.0", 'x', UOption.NO_ARG) |
| }; |
| |
| private static final int ARRAY_RESOURCE = 0; |
| private static final int ALIAS_RESOURCE = 1; |
| private static final int BINARY_RESOURCE = 2; |
| private static final int INTEGER_RESOURCE = 3; |
| private static final int INTVECTOR_RESOURCE = 4; |
| private static final int TABLE_RESOURCE = 5; |
| |
| private static final String NEW_RESOURCES[] = { |
| "x-icu-array", |
| "x-icu-alias", |
| "x-icu-binary", |
| "x-icu-integer", |
| "x-icu-intvector", |
| "x-icu-table" |
| }; |
| |
| private static final String OLD_RESOURCES[] = { |
| "array", |
| "alias", |
| "bin", |
| "int", |
| "intvector", |
| "table" |
| }; |
| |
| private String resources[]; |
| |
| private static final String ROOT = "root"; |
| private static final String RESTYPE = "restype"; |
| private static final String RESNAME = "resname"; |
| //private static final String YES = "yes"; |
| //private static final String NO = "no"; |
| private static final String TRANSLATE = "translate"; |
| //private static final String BODY = "body"; |
| private static final String GROUPS = "group"; |
| private static final String FILES = "file"; |
| private static final String TRANSUNIT = "trans-unit"; |
| private static final String BINUNIT = "bin-unit"; |
| private static final String BINSOURCE = "bin-source"; |
| //private static final String TS = "ts"; |
| //private static final String ORIGINAL = "original"; |
| private static final String SOURCELANGUAGE = "source-language"; |
| private static final String TARGETLANGUAGE = "target-language"; |
| private static final String TARGET = "target"; |
| private static final String SOURCE = "source"; |
| private static final String NOTE = "note"; |
| private static final String XMLLANG = "xml:lang"; |
| private static final String FILE = "file"; |
| private static final String INTVECTOR = "intvector"; |
| private static final String ARRAYS = "array"; |
| private static final String STRINGS = "string"; |
| private static final String BIN = "bin"; |
| private static final String INTS = "int"; |
| private static final String TABLE = "table"; |
| private static final String IMPORT = "import"; |
| private static final String HREF = "href"; |
| private static final String EXTERNALFILE = "external-file"; |
| private static final String INTERNALFILE = "internal-file"; |
| private static final String ALTTRANS = "alt-trans"; |
| private static final String CRC = "crc"; |
| private static final String ALIAS = "alias"; |
| private static final String LINESEP = System.getProperty("line.separator"); |
| private static final String BOM = "\uFEFF"; |
| private static final String CHARSET = "UTF-8"; |
| private static final String OPENBRACE = "{"; |
| private static final String CLOSEBRACE = "}"; |
| private static final String COLON = ":"; |
| private static final String COMMA = ","; |
| private static final String QUOTE = "\""; |
| private static final String COMMENTSTART = "/**"; |
| private static final String COMMENTEND = " */"; |
| private static final String TAG = " * @"; |
| private static final String COMMENTMIDDLE = " * "; |
| private static final String SPACE = " "; |
| private static final String INDENT = " "; |
| private static final String EMPTY = ""; |
| private static final String ID = "id"; |
| |
| public static void main(String[] args) { |
| XLIFF2ICUConverter cnv = new XLIFF2ICUConverter(); |
| cnv.processArgs(args); |
| } |
| private String sourceDir = null; |
| //private String fileName = null; |
| private String destDir = null; |
| private boolean targetOnly = false; |
| private String targetFileName = null; |
| private boolean makeSourceRoot = false; |
| private String sourceFileName = null; |
| private boolean sourceOnly = false; |
| private boolean xliff10 = false; |
| |
| private void processArgs(String[] args) { |
| int remainingArgc = 0; |
| try{ |
| remainingArgc = UOption.parseArgs(args, options); |
| }catch (Exception e){ |
| System.err.println("ERROR: "+ e.toString()); |
| usage(); |
| } |
| if(args.length==0 || options[HELP1].doesOccur || options[HELP2].doesOccur) { |
| usage(); |
| } |
| if(remainingArgc==0){ |
| System.err.println("ERROR: Either the file name to be processed is not "+ |
| "specified or the it is specified after the -t/-c \n"+ |
| "option which has an optional argument. Try rearranging "+ |
| "the options."); |
| usage(); |
| } |
| if(options[SOURCEDIR].doesOccur) { |
| sourceDir = options[SOURCEDIR].value; |
| } |
| |
| if(options[DESTDIR].doesOccur) { |
| destDir = options[DESTDIR].value; |
| } |
| |
| if(options[TARGETONLY].doesOccur){ |
| targetOnly = true; |
| targetFileName = options[TARGETONLY].value; |
| } |
| |
| if(options[SOURCEONLY].doesOccur){ |
| sourceOnly = true; |
| sourceFileName = options[SOURCEONLY].value; |
| } |
| |
| if(options[MAKE_SOURCE_ROOT].doesOccur){ |
| makeSourceRoot = true; |
| } |
| |
| if(options[XLIFF_1_0].doesOccur) { |
| xliff10 = true; |
| } |
| |
| if(destDir==null){ |
| destDir = "."; |
| } |
| |
| if(sourceOnly == true && targetOnly == true){ |
| System.err.println("--source-only and --target-only are specified. Please check the arguments and try again."); |
| usage(); |
| } |
| |
| for (int i = 0; i < remainingArgc; i++) { |
| //int lastIndex = args[i].lastIndexOf(File.separator, args[i].length()) + 1; /* add 1 to skip past the separator */ |
| //fileName = args[i].substring(lastIndex, args[i].length()); |
| String xmlfileName = getFullPath(false,args[i]); |
| System.out.println("Processing file: "+xmlfileName); |
| createRB(xmlfileName); |
| } |
| } |
| |
| private void usage() { |
| System.out.println("\nUsage: XLIFF2ICUConverter [OPTIONS] [FILES]\n\n"+ |
| "This program is used to convert XLIFF files to ICU ResourceBundle TXT files.\n"+ |
| "Please refer to the following options. Options are not case sensitive.\n"+ |
| "Options:\n"+ |
| "-s or --sourcedir source directory for files followed by path, default is current directory.\n" + |
| "-d or --destdir destination directory, followed by the path, default is current directory.\n" + |
| "-h or -? or --help this usage text.\n"+ |
| "-t or --target-only only generate the target language txt file, followed by optional output file name.\n" + |
| " Cannot be used in conjunction with --source-only.\n"+ |
| "-c or --source-only only generate the source language bundle followed by optional output file name.\n"+ |
| " Cannot be used in conjunction with --target-only.\n"+ |
| "-r or --make-source-root produce root bundle from source elements.\n" + |
| "-x or --xliff-1.0 source file is XLIFF 1.0" + |
| "example: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter -t <optional argument> -s xxx -d yyy myResources.xlf"); |
| System.exit(-1); |
| } |
| |
| private String getFullPath(boolean fileType, String fName){ |
| String str; |
| int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1; /*add 1 to skip past the separator*/ |
| int lastIndex2 = fName.lastIndexOf('.', fName.length()); |
| if (fileType == true) { |
| if(lastIndex2 == -1){ |
| fName = fName.trim() + ".txt"; |
| }else{ |
| if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){ |
| fName = fName.substring(lastIndex1,lastIndex2) + ".txt"; |
| } |
| } |
| if (destDir != null && fName != null) { |
| str = destDir + File.separator + fName.trim(); |
| } else { |
| str = System.getProperty("user.dir") + File.separator + fName.trim(); |
| } |
| } else { |
| if(lastIndex2 == -1){ |
| fName = fName.trim() + ".xlf"; |
| }else{ |
| if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){ |
| fName = fName.substring(lastIndex1,lastIndex2) + ".xlf"; |
| } |
| } |
| if(sourceDir != null && fName != null) { |
| str = sourceDir + File.separator + fName; |
| } else if (lastIndex1 > 0) { |
| str = fName; |
| } else { |
| str = System.getProperty("user.dir") + File.separator + fName; |
| } |
| } |
| return str; |
| } |
| |
| /* |
| * Utility method to translate a String filename to URL. |
| * |
| * Note: This method is not necessarily proven to get the |
| * correct URL for every possible kind of filename; it should |
| * be improved. It handles the most common cases that we've |
| * encountered when running Conformance tests on Xalan. |
| * Also note, this method does not handle other non-file: |
| * flavors of URLs at all. |
| * |
| * If the name is null, return null. |
| * If the name starts with a common URI scheme (namely the ones |
| * found in the examples of RFC2396), then simply return the |
| * name as-is (the assumption is that it's already a URL) |
| * Otherwise we attempt (cheaply) to convert to a file:/// URL. |
| */ |
| private static String filenameToURL(String filename){ |
| // null begets null - something like the commutative property |
| if (null == filename){ |
| return null; |
| } |
| |
| // Don't translate a string that already looks like a URL |
| if (filename.startsWith("file:") |
| || filename.startsWith("http:") |
| || filename.startsWith("ftp:") |
| || filename.startsWith("gopher:") |
| || filename.startsWith("mailto:") |
| || filename.startsWith("news:") |
| || filename.startsWith("telnet:") |
| ){ |
| return filename; |
| } |
| |
| |
| File f = new File(filename); |
| String tmp = null; |
| try{ |
| // This normally gives a better path |
| tmp = f.getCanonicalPath(); |
| }catch (IOException ioe){ |
| // But this can be used as a backup, for cases |
| // where the file does not exist, etc. |
| tmp = f.getAbsolutePath(); |
| } |
| |
| // URLs must explicitly use only forward slashes |
| if (File.separatorChar == '\\') { |
| tmp = tmp.replace('\\', '/'); |
| } |
| // Note the presumption that it's a file reference |
| // Ensure we have the correct number of slashes at the |
| // start: we always want 3 /// if it's absolute |
| // (which we should have forced above) |
| if (tmp.startsWith("/")){ |
| return "file://" + tmp; |
| } |
| else{ |
| return "file:///" + tmp; |
| } |
| } |
| private boolean isXmlLang (String lang){ |
| |
| int suffix; |
| char c; |
| |
| if (lang.length () < 2){ |
| return false; |
| } |
| |
| c = lang.charAt(1); |
| if (c == '-') { |
| c = lang.charAt(0); |
| if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')){ |
| return false; |
| } |
| suffix = 1; |
| } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { |
| c = lang.charAt(0); |
| if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){ |
| return false; |
| } |
| suffix = 2; |
| } else{ |
| return false; |
| } |
| while (suffix < lang.length ()) { |
| c = lang.charAt(suffix); |
| if (c != '-'){ |
| break; |
| } |
| while (++suffix < lang.length ()) { |
| c = lang.charAt(suffix); |
| if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))){ |
| break; |
| } |
| } |
| } |
| return ((lang.length() == suffix) && (c != '-')); |
| } |
| |
| private void createRB(String xmlfileName) { |
| |
| String urls = filenameToURL(xmlfileName); |
| DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); |
| dfactory.setNamespaceAware(true); |
| Document doc = null; |
| |
| if (xliff10) { |
| dfactory.setValidating(true); |
| resources = OLD_RESOURCES; |
| } else { |
| try { |
| SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| Schema schema = schemaFactory.newSchema(); |
| |
| dfactory.setSchema(schema); |
| } catch (SAXException e) { |
| System.err.println("Can't create the schema..."); |
| System.exit(-1); |
| } catch (UnsupportedOperationException e) { |
| System.err.println("ERROR:\tOne of the schema operations is not supported with this JVM."); |
| System.err.println("\tIf you are using GNU Java, you should try using the latest Sun JVM."); |
| System.err.println("\n*Here is the stack trace:"); |
| e.printStackTrace(); |
| System.exit(-1); |
| } |
| |
| resources = NEW_RESOURCES; |
| } |
| |
| ErrorHandler nullHandler = new ErrorHandler() { |
| public void warning(SAXParseException e) throws SAXException { |
| |
| } |
| public void error(SAXParseException e) throws SAXException { |
| System.err.println("The XLIFF document is invalid, please check it first: "); |
| System.err.println("Line "+e.getLineNumber()+", Column "+e.getColumnNumber()); |
| System.err.println("Error: " + e.getMessage()); |
| System.exit(-1); |
| } |
| public void fatalError(SAXParseException e) throws SAXException { |
| throw e; |
| } |
| }; |
| |
| try { |
| DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); |
| docBuilder.setErrorHandler(nullHandler); |
| doc = docBuilder.parse(new InputSource(urls)); |
| |
| NodeList nlist = doc.getElementsByTagName(FILES); |
| if(nlist.getLength()>1){ |
| throw new RuntimeException("Multiple <file> elements in the XLIFF file not supported."); |
| } |
| |
| // get the value of source-language attribute |
| String sourceLang = getLanguageName(doc, SOURCELANGUAGE); |
| // get the value of target-language attribute |
| String targetLang = getLanguageName(doc, TARGETLANGUAGE); |
| |
| // get the list of <source> elements |
| NodeList sourceList = doc.getElementsByTagName(SOURCE); |
| // get the list of target elements |
| NodeList targetList = doc.getElementsByTagName(TARGET); |
| |
| // check if the xliff file has source elements in multiple languages |
| // the source-language value should be the same as xml:lang values |
| // of all the source elements. |
| String xmlSrcLang = checkLangAttribute(sourceList, sourceLang); |
| |
| // check if the xliff file has target elements in multiple languages |
| // the target-language value should be the same as xml:lang values |
| // of all the target elements. |
| String xmlTargetLang = checkLangAttribute(targetList, targetLang); |
| |
| // Create the Resource linked list which will hold the |
| // source and target bundles after parsing |
| Resource[] set = new Resource[2]; |
| set[0] = new ResourceTable(); |
| set[1] = new ResourceTable(); |
| |
| // lenient extraction of source language |
| if(makeSourceRoot == true){ |
| set[0].name = ROOT; |
| }else if(sourceLang!=null){ |
| set[0].name = sourceLang.replace('-','_'); |
| }else{ |
| if(xmlSrcLang != null){ |
| set[0].name = xmlSrcLang.replace('-','_'); |
| }else{ |
| System.err.println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file."); |
| System.exit(-1); |
| } |
| } |
| |
| // lenient extraction of the target language |
| if(targetLang!=null){ |
| set[1].name = targetLang.replace('-','_'); |
| }else{ |
| if(xmlTargetLang!=null){ |
| set[1].name = xmlTargetLang.replace('-','_'); |
| }else{ |
| System.err.println("WARNING: Could not figure out the target language of the file. Producing source bundle only."); |
| } |
| } |
| |
| |
| // check if any <alt-trans> elements are present |
| NodeList altTrans = doc.getElementsByTagName(ALTTRANS); |
| if(altTrans.getLength()>0){ |
| System.err.println("WARNING: <alt-trans> elements in found. Ignoring all <alt-trans> elements."); |
| } |
| |
| // get all the group elements |
| NodeList list = doc.getElementsByTagName(GROUPS); |
| |
| // process the first group element. The first group element is |
| // the base table that must be parsed recursively |
| parseTable(list.item(0), set); |
| |
| // write out the bundle |
| writeResource(set, xmlfileName); |
| } |
| catch (Throwable se) { |
| System.err.println("ERROR: " + se.toString()); |
| System.exit(1); |
| } |
| } |
| |
| private void writeResource(Resource[] set, String xmlfileName){ |
| if(targetOnly==false){ |
| writeResource(set[0], xmlfileName, sourceFileName); |
| } |
| if(sourceOnly == false){ |
| if(targetOnly==true && set[1].name == null){ |
| throw new RuntimeException("The "+ xmlfileName +" does not contain translation\n"); |
| } |
| if(set[1].name != null){ |
| writeResource(set[1], xmlfileName, targetFileName); |
| } |
| } |
| } |
| |
| private void writeResource(Resource set, String sourceFilename, String targetFilename){ |
| try { |
| String outputFileName = null; |
| if(targetFilename != null){ |
| outputFileName = destDir+File.separator+targetFilename+".txt"; |
| }else{ |
| outputFileName = destDir+File.separator+set.name+".txt"; |
| } |
| FileOutputStream file = new FileOutputStream(outputFileName); |
| BufferedOutputStream writer = new BufferedOutputStream(file); |
| |
| writeHeader(writer,sourceFilename); |
| |
| //Now start writing the resource; |
| Resource current = set; |
| while(current!=null){ |
| current.write(writer, 0, false); |
| current = current.next; |
| } |
| writer.flush(); |
| writer.close(); |
| } catch (Exception ie) { |
| System.err.println("ERROR :" + ie.toString()); |
| return; |
| } |
| } |
| |
| private String getLanguageName(Document doc, String lang){ |
| if(doc!=null){ |
| NodeList list = doc.getElementsByTagName(FILE); |
| Node node = list.item(0); |
| NamedNodeMap attr = node.getAttributes(); |
| Node orig = attr.getNamedItem(lang); |
| |
| if(orig != null){ |
| String name = orig.getNodeValue(); |
| NodeList groupList = doc.getElementsByTagName(GROUPS); |
| Node group = groupList.item(0); |
| NamedNodeMap groupAtt = group.getAttributes(); |
| Node id = groupAtt.getNamedItem(ID); |
| if(id!=null){ |
| String idVal = id.getNodeValue(); |
| |
| if(!name.equals(idVal)){ |
| System.out.println("WARNING: The id value != language name. " + |
| "Please compare the output with the orignal " + |
| "ICU ResourceBundle before proceeding."); |
| } |
| } |
| if(!isXmlLang(name)){ |
| System.err.println("The attribute "+ lang + "=\""+ name + |
| "\" of <file> element is invalid."); |
| System.exit(-1); |
| } |
| return name; |
| } |
| } |
| return null; |
| } |
| |
| // check if the xliff file is translated into multiple languages |
| // The XLIFF specification allows for single <target> element |
| // as the child of <trans-unit> but the attributes of the |
| // <target> element may different across <trans-unit> elements |
| // check for it. Similar is the case with <source> elements |
| private String checkLangAttribute(NodeList list, String origName){ |
| String oldLangName=origName; |
| for(int i = 0 ;i<list.getLength(); i++){ |
| Node node = list.item(i); |
| NamedNodeMap attr = node.getAttributes(); |
| Node lang = attr.getNamedItem(XMLLANG); |
| String langName = null; |
| // the target element should always contain xml:lang attribute |
| if(lang==null ){ |
| if(origName==null){ |
| System.err.println("Encountered <target> element without xml:lang attribute. Please fix the below element in the XLIFF file.\n"+ node.toString()); |
| System.exit(-1); |
| }else{ |
| langName = origName; |
| } |
| }else{ |
| langName = lang.getNodeValue(); |
| } |
| |
| if(oldLangName!=null && langName!=null && !langName.equals(oldLangName)){ |
| throw new RuntimeException("The <trans-unit> elements must be bilingual, multilingual tranlations not supported. xml:lang = " + oldLangName + |
| " and xml:lang = " + langName); |
| } |
| oldLangName = langName; |
| } |
| return oldLangName; |
| } |
| |
| private class Resource{ |
| String[] note = new String[20]; |
| int noteLen = 0; |
| String translate; |
| String comment; |
| String name; |
| Resource next; |
| public String escapeSyntaxChars(String val){ |
| // escape the embedded quotes |
| char[] str = val.toCharArray(); |
| StringBuffer result = new StringBuffer(); |
| for(int i=0; i<str.length; i++){ |
| switch (str[i]){ |
| case '\u0022': |
| result.append('\\'); //append backslash |
| default: |
| result.append(str[i]); |
| } |
| } |
| return result.toString(); |
| } |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| while(next!=null){ |
| next.write(writer, numIndent+1, false); |
| } |
| } |
| public void writeIndent(OutputStream writer, int numIndent){ |
| for(int i=0; i< numIndent; i++){ |
| write(writer,INDENT); |
| } |
| } |
| public void write(OutputStream writer, String value){ |
| try { |
| byte[] bytes = value.getBytes(CHARSET); |
| writer.write(bytes, 0, bytes.length); |
| } catch(Exception e) { |
| System.err.println(e); |
| System.exit(1); |
| } |
| } |
| public void writeComments(OutputStream writer, int numIndent){ |
| boolean translateIsDefault = translate == null || translate.equals("yes"); |
| |
| if(comment!=null || ! translateIsDefault || noteLen > 0){ |
| // print the start of the comment |
| writeIndent(writer, numIndent); |
| write(writer, COMMENTSTART+LINESEP); |
| |
| // print comment if any |
| if(comment!=null){ |
| writeIndent(writer, numIndent); |
| write(writer, COMMENTMIDDLE); |
| write(writer, comment); |
| write(writer, LINESEP); |
| } |
| |
| // print the translate attribute if any |
| if(! translateIsDefault){ |
| writeIndent(writer, numIndent); |
| write(writer, TAG+TRANSLATE+SPACE); |
| write(writer, translate); |
| write(writer, LINESEP); |
| } |
| |
| // print note elements if any |
| for(int i=0; i<noteLen; i++){ |
| if(note[i]!=null){ |
| writeIndent(writer, numIndent); |
| write(writer, TAG+NOTE+SPACE+note[i]); |
| write(writer, LINESEP); |
| } |
| } |
| |
| // terminate the comment |
| writeIndent(writer, numIndent); |
| write(writer, COMMENTEND+LINESEP); |
| } |
| } |
| } |
| |
| private class ResourceString extends Resource{ |
| String val; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| if(bare==true){ |
| if(name!=null){ |
| throw new RuntimeException("Bare option is set to true but the resource has a name!"); |
| } |
| |
| write(writer,QUOTE+escapeSyntaxChars(val)+QUOTE); |
| }else{ |
| write(writer, name+COLON+STRINGS+ OPENBRACE + QUOTE + escapeSyntaxChars(val) + QUOTE+ CLOSEBRACE + LINESEP); |
| } |
| } |
| } |
| private class ResourceAlias extends Resource{ |
| String val; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| String line = ((name==null)? EMPTY: name)+COLON+ALIAS+ OPENBRACE+QUOTE+escapeSyntaxChars(val)+QUOTE+CLOSEBRACE; |
| if(bare==true){ |
| if(name!=null){ |
| throw new RuntimeException("Bare option is set to true but the resource has a name!"); |
| } |
| write(writer,line); |
| }else{ |
| write(writer, line+LINESEP); |
| } |
| } |
| } |
| private class ResourceInt extends Resource{ |
| String val; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| String line = ((name==null)? EMPTY: name)+COLON+INTS+ OPENBRACE + val +CLOSEBRACE; |
| if(bare==true){ |
| if(name!=null){ |
| throw new RuntimeException("Bare option is set to true but the resource has a name!"); |
| } |
| write(writer,line); |
| }else{ |
| write(writer, line+LINESEP); |
| } |
| } |
| } |
| private class ResourceBinary extends Resource{ |
| String internal; |
| String external; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| if(internal==null){ |
| String line = ((name==null) ? EMPTY : name)+COLON+IMPORT+ OPENBRACE+QUOTE+external+QUOTE+CLOSEBRACE + ((bare==true) ? EMPTY : LINESEP); |
| write(writer, line); |
| }else{ |
| String line = ((name==null) ? EMPTY : name)+COLON+BIN+ OPENBRACE+internal+CLOSEBRACE+ ((bare==true) ? EMPTY : LINESEP); |
| write(writer,line); |
| } |
| |
| } |
| } |
| private class ResourceIntVector extends Resource{ |
| ResourceInt first; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| write(writer, name+COLON+INTVECTOR+OPENBRACE+LINESEP); |
| numIndent++; |
| ResourceInt current = (ResourceInt) first; |
| while(current != null){ |
| //current.write(writer, numIndent, true); |
| writeIndent(writer, numIndent); |
| write(writer, current.val); |
| write(writer, COMMA+LINESEP); |
| current = (ResourceInt) current.next; |
| } |
| numIndent--; |
| writeIndent(writer, numIndent); |
| write(writer, CLOSEBRACE+LINESEP); |
| } |
| } |
| private class ResourceTable extends Resource{ |
| Resource first; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| write(writer, name+COLON+TABLE+OPENBRACE+LINESEP); |
| numIndent++; |
| Resource current = first; |
| while(current != null){ |
| current.write(writer, numIndent, false); |
| current = current.next; |
| } |
| numIndent--; |
| writeIndent(writer, numIndent); |
| write(writer, CLOSEBRACE+LINESEP); |
| } |
| } |
| private class ResourceArray extends Resource{ |
| Resource first; |
| public void write(OutputStream writer, int numIndent, boolean bare){ |
| writeComments(writer, numIndent); |
| writeIndent(writer, numIndent); |
| write(writer, name+COLON+ARRAYS+OPENBRACE+LINESEP); |
| numIndent++; |
| Resource current = first; |
| while(current != null){ |
| current.write(writer, numIndent, true); |
| write(writer, COMMA+LINESEP); |
| current = current.next; |
| } |
| numIndent--; |
| writeIndent(writer, numIndent); |
| write(writer, CLOSEBRACE+LINESEP); |
| } |
| } |
| |
| private String getAttributeValue(Node sNode, String attribName){ |
| String value=null; |
| Node node = sNode; |
| |
| NamedNodeMap attributes = node.getAttributes(); |
| Node attr = attributes.getNamedItem(attribName); |
| if(attr!=null){ |
| value = attr.getNodeValue(); |
| } |
| |
| return value; |
| } |
| |
| private void parseResourceString(Node node, ResourceString[] set){ |
| ResourceString currentSource; |
| ResourceString currentTarget; |
| currentSource = set[0]; |
| currentTarget = set[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node, TRANSLATE); |
| |
| // loop to pickup <source>, <note> and <target> elements |
| for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ |
| short type = transUnit.getNodeType(); |
| String name = transUnit.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| // get the comment |
| currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); |
| }else if( type == Node.ELEMENT_NODE){ |
| if(name.equals(SOURCE)){ |
| // save the source and target values |
| currentSource.name = currentTarget.name = resName; |
| currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| currentSource.translate = currentTarget.translate = translate; |
| }else if(name.equals(NOTE)){ |
| // save the note values |
| currentSource.note[currentSource.noteLen++] = |
| currentTarget.note[currentTarget.noteLen++] = |
| transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(TARGET)){ |
| // if there is a target element replace it |
| currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| } |
| } |
| |
| } |
| } |
| |
| private void parseResourceInt(Node node, ResourceInt[] set){ |
| ResourceInt currentSource; |
| ResourceInt currentTarget; |
| currentSource = set[0]; |
| currentTarget = set[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node, TRANSLATE); |
| // loop to pickup <source>, <note> and <target> elements |
| for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ |
| short type = transUnit.getNodeType(); |
| String name = transUnit.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| // get the comment |
| currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); |
| }else if( type == Node.ELEMENT_NODE){ |
| if(name.equals(SOURCE)){ |
| // save the source and target values |
| currentSource.name = currentTarget.name = resName; |
| currentSource.translate = currentTarget.translate = translate; |
| currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(NOTE)){ |
| // save the note values |
| currentSource.note[currentSource.noteLen++] = |
| currentTarget.note[currentTarget.noteLen++] = |
| transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(TARGET)){ |
| // if there is a target element replace it |
| currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| } |
| } |
| |
| } |
| } |
| |
| private void parseResourceAlias(Node node, ResourceAlias[] set){ |
| ResourceAlias currentSource; |
| ResourceAlias currentTarget; |
| currentSource = set[0]; |
| currentTarget = set[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node, TRANSLATE); |
| // loop to pickup <source>, <note> and <target> elements |
| for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ |
| short type = transUnit.getNodeType(); |
| String name = transUnit.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| // get the comment |
| currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); |
| }else if( type == Node.ELEMENT_NODE){ |
| if(name.equals(SOURCE)){ |
| // save the source and target values |
| currentSource.name = currentTarget.name = resName; |
| currentSource.translate = currentTarget.translate = translate; |
| currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(NOTE)){ |
| // save the note values |
| currentSource.note[currentSource.noteLen++] = |
| currentTarget.note[currentTarget.noteLen++] = |
| transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(TARGET)){ |
| // if there is a target element replace it |
| currentTarget.val = transUnit.getFirstChild().getNodeValue(); |
| } |
| } |
| |
| } |
| } |
| private void parseResourceBinary(Node node, ResourceBinary[] set){ |
| ResourceBinary currentSource; |
| ResourceBinary currentTarget; |
| currentSource = set[0]; |
| currentTarget = set[1]; |
| |
| // loop to pickup <source>, <note> and <target> elements |
| for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ |
| short type = transUnit.getNodeType(); |
| String name = transUnit.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| // get the comment |
| currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); |
| }else if( type == Node.ELEMENT_NODE){ |
| if(name.equals(BINSOURCE)){ |
| // loop to pickup internal-file/extenal-file element |
| continue; |
| }else if(name.equals(NOTE)){ |
| // save the note values |
| currentSource.note[currentSource.noteLen++] = |
| currentTarget.note[currentTarget.noteLen++] = |
| transUnit.getFirstChild().getNodeValue(); |
| }else if(name.equals(INTERNALFILE)){ |
| // if there is a target element replace it |
| String crc = getAttributeValue(transUnit, CRC); |
| String value = transUnit.getFirstChild().getNodeValue(); |
| |
| //verify that the binary value conforms to the CRC |
| if(Integer.parseInt(crc, 10) != CalculateCRC32.computeCRC32(value)) { |
| System.err.println("ERROR: CRC value incorrect! Please check."); |
| System.exit(1); |
| } |
| |
| currentTarget.internal = currentSource.internal= value; |
| |
| }else if(name.equals(EXTERNALFILE)){ |
| currentSource.external = getAttributeValue(transUnit, HREF); |
| currentTarget.external = currentSource.external; |
| } |
| } |
| |
| } |
| } |
| private void parseTransUnit(Node node, Resource[] set){ |
| |
| String attrType = getAttributeValue(node, RESTYPE); |
| String translate = getAttributeValue(node, TRANSLATE); |
| if(attrType==null || attrType.equals(STRINGS)){ |
| ResourceString[] strings = new ResourceString[2]; |
| strings[0] = new ResourceString(); |
| strings[1] = new ResourceString(); |
| parseResourceString(node, strings); |
| strings[0].translate = strings[1].translate = translate; |
| set[0] = strings[0]; |
| set[1] = strings[1]; |
| }else if(attrType.equals(resources[INTEGER_RESOURCE])){ |
| ResourceInt[] ints = new ResourceInt[2]; |
| ints[0] = new ResourceInt(); |
| ints[1] = new ResourceInt(); |
| parseResourceInt(node, ints); |
| ints[0].translate = ints[1].translate = translate; |
| set[0] = ints[0]; |
| set[1] = ints[1]; |
| }else if(attrType.equals(resources[ALIAS_RESOURCE])){ |
| ResourceAlias[] ints = new ResourceAlias[2]; |
| ints[0] = new ResourceAlias(); |
| ints[1] = new ResourceAlias(); |
| parseResourceAlias(node, ints); |
| ints[0].translate = ints[1].translate = translate; |
| set[0] = ints[0]; |
| set[1] = ints[1]; |
| } |
| } |
| |
| private void parseBinUnit(Node node, Resource[] set){ |
| if (getAttributeValue(node, RESTYPE).equals(resources[BINARY_RESOURCE])) { |
| ResourceBinary[] bins = new ResourceBinary[2]; |
| |
| bins[0] = new ResourceBinary(); |
| bins[1] = new ResourceBinary(); |
| |
| Resource currentSource = bins[0]; |
| Resource currentTarget = bins[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node, TRANSLATE); |
| |
| currentTarget.name = currentSource.name = resName; |
| currentSource.translate = currentTarget.translate = translate; |
| |
| for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ |
| short type = child.getNodeType(); |
| String name = child.getNodeName(); |
| |
| if(type == Node.COMMENT_NODE){ |
| currentSource.comment = currentTarget.comment = child.getNodeValue(); |
| }else if(type == Node.ELEMENT_NODE){ |
| if(name.equals(BINSOURCE)){ |
| parseResourceBinary(child, bins); |
| }else if(name.equals(NOTE)){ |
| String note = child.getFirstChild().getNodeValue(); |
| |
| currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; |
| } |
| } |
| } |
| |
| set[0] = bins[0]; |
| set[1] = bins[1]; |
| } |
| } |
| |
| private void parseArray(Node node, Resource[] set){ |
| if(set[0]==null){ |
| set[0] = new ResourceArray(); |
| set[1] = new ResourceArray(); |
| } |
| Resource currentSource = set[0]; |
| Resource currentTarget = set[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| currentSource.name = currentTarget.name = resName; |
| boolean isFirst = true; |
| |
| for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ |
| short type = child.getNodeType(); |
| String name = child.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| currentSource.comment = currentTarget.comment = child.getNodeValue(); |
| }else if(type == Node.ELEMENT_NODE){ |
| if(name.equals(TRANSUNIT)){ |
| Resource[] next = new Resource[2]; |
| parseTransUnit(child, next); |
| if(isFirst==true){ |
| ((ResourceArray) currentSource).first = next[0]; |
| ((ResourceArray) currentTarget).first = next[1]; |
| currentSource = ((ResourceArray) currentSource).first; |
| currentTarget = ((ResourceArray) currentTarget).first; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| }else if(name.equals(NOTE)){ |
| String note = child.getFirstChild().getNodeValue(); |
| currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; |
| }else if(name.equals(BINUNIT)){ |
| Resource[] next = new Resource[2]; |
| parseBinUnit(child, next); |
| if(isFirst==true){ |
| ((ResourceArray) currentSource).first = next[0]; |
| ((ResourceArray) currentTarget).first = next[1]; |
| currentSource = ((ResourceArray) currentSource).first.next; |
| currentTarget = ((ResourceArray) currentTarget).first.next; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| } |
| } |
| } |
| } |
| private void parseIntVector(Node node, Resource[] set){ |
| if(set[0]==null){ |
| set[0] = new ResourceIntVector(); |
| set[1] = new ResourceIntVector(); |
| } |
| Resource currentSource = set[0]; |
| Resource currentTarget = set[1]; |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node,TRANSLATE); |
| currentSource.name = currentTarget.name = resName; |
| currentSource.translate = translate; |
| boolean isFirst = true; |
| for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ |
| short type = child.getNodeType(); |
| String name = child.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| currentSource.comment = currentTarget.comment = child.getNodeValue(); |
| }else if(type == Node.ELEMENT_NODE){ |
| if(name.equals(TRANSUNIT)){ |
| Resource[] next = new Resource[2]; |
| parseTransUnit(child, next); |
| if(isFirst==true){ |
| // the down cast should be safe .. if not something is terribly wrong!! |
| ((ResourceIntVector) currentSource).first = (ResourceInt)next[0]; |
| ((ResourceIntVector) currentTarget).first = (ResourceInt) next[1]; |
| currentSource = ((ResourceIntVector) currentSource).first; |
| currentTarget = ((ResourceIntVector) currentTarget).first; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| }else if(name.equals(NOTE)){ |
| String note = child.getFirstChild().getNodeValue(); |
| currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; |
| } |
| } |
| } |
| } |
| private void parseTable(Node node, Resource[] set){ |
| if(set[0]==null){ |
| set[0] = new ResourceTable(); |
| set[1] = new ResourceTable(); |
| } |
| Resource currentSource = set[0]; |
| Resource currentTarget = set[1]; |
| |
| String resName = getAttributeValue(node, RESNAME); |
| String translate = getAttributeValue(node,TRANSLATE); |
| if(resName!=null && currentSource.name==null && currentTarget.name==null){ |
| currentSource.name = currentTarget.name = resName; |
| } |
| currentTarget.translate = currentSource.translate = translate; |
| |
| boolean isFirst = true; |
| for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ |
| short type = child.getNodeType(); |
| String name = child.getNodeName(); |
| if(type == Node.COMMENT_NODE){ |
| currentSource.comment = currentTarget.comment = child.getNodeValue(); |
| }else if(type == Node.ELEMENT_NODE){ |
| if(name.equals(GROUPS)){ |
| Resource[] next = new Resource[2]; |
| parseGroup(child, next); |
| if(isFirst==true){ |
| // the down cast should be safe .. if not something is terribly wrong!! |
| ((ResourceTable) currentSource).first = next[0]; |
| ((ResourceTable) currentTarget).first = next[1]; |
| currentSource = ((ResourceTable) currentSource).first; |
| currentTarget = ((ResourceTable) currentTarget).first; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| }else if(name.equals(TRANSUNIT)){ |
| Resource[] next = new Resource[2]; |
| parseTransUnit(child, next); |
| if(isFirst==true){ |
| // the down cast should be safe .. if not something is terribly wrong!! |
| ((ResourceTable) currentSource).first = next[0]; |
| ((ResourceTable) currentTarget).first = next[1]; |
| currentSource = ((ResourceTable) currentSource).first; |
| currentTarget = ((ResourceTable) currentTarget).first; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| }else if(name.equals(NOTE)){ |
| String note = child.getFirstChild().getNodeValue(); |
| currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; |
| }else if(name.equals(BINUNIT)){ |
| Resource[] next = new Resource[2]; |
| parseBinUnit(child, next); |
| if(isFirst==true){ |
| // the down cast should be safe .. if not something is terribly wrong!! |
| ((ResourceTable) currentSource).first = next[0]; |
| ((ResourceTable) currentTarget).first = next[1]; |
| currentSource = ((ResourceTable) currentSource).first; |
| currentTarget = ((ResourceTable) currentTarget).first; |
| isFirst = false; |
| }else{ |
| currentSource.next = next[0]; |
| currentTarget.next = next[1]; |
| // set the next pointers |
| currentSource = currentSource.next; |
| currentTarget = currentTarget.next; |
| } |
| } |
| } |
| } |
| } |
| |
| private void parseGroup(Node node, Resource[] set){ |
| |
| // figure out what kind of group this is |
| String resType = getAttributeValue(node, RESTYPE); |
| if(resType.equals(resources[ARRAY_RESOURCE])){ |
| parseArray(node, set); |
| }else if( resType.equals(resources[TABLE_RESOURCE])){ |
| parseTable(node, set); |
| }else if( resType.equals(resources[INTVECTOR_RESOURCE])){ |
| parseIntVector(node, set); |
| } |
| } |
| |
| |
| private void writeLine(OutputStream writer, String line) { |
| try { |
| byte[] bytes = line.getBytes(CHARSET); |
| writer.write(bytes, 0, bytes.length); |
| } catch (Exception e) { |
| System.err.println(e); |
| System.exit(1); |
| } |
| } |
| |
| private void writeHeader(OutputStream writer, String fileName){ |
| final String header = |
| "// ***************************************************************************" + LINESEP + |
| "// *" + LINESEP + |
| "// * Tool: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter.java" + LINESEP + |
| "// * Date & Time: {0,date,MM/dd/yyyy hh:mm:ss a z}"+ LINESEP + |
| "// * Source File: {1}" + LINESEP + |
| "// *" + LINESEP + |
| "// ***************************************************************************" + LINESEP; |
| |
| writeBOM(writer); |
| MessageFormat format = new MessageFormat(header); |
| Object args[] = {new Date(System.currentTimeMillis()), fileName}; |
| |
| writeLine(writer, format.format(args)); |
| } |
| |
| private void writeBOM(OutputStream buffer) { |
| try { |
| byte[] bytes = BOM.getBytes(CHARSET); |
| buffer.write(bytes, 0, bytes.length); |
| } catch(Exception e) { |
| System.err.println(e); |
| System.exit(1); |
| } |
| } |
| } |