2 ******************************************************************************
3 * Copyright (C) 2003-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
8 package com.ibm.icu.dev.tool.localeconverter;
10 import java.io.BufferedOutputStream;
12 import java.io.FileOutputStream;
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.text.MessageFormat;
16 import java.util.Date;
18 import javax.xml.XMLConstants;
19 import javax.xml.parsers.DocumentBuilder;
20 import javax.xml.parsers.DocumentBuilderFactory;
21 import javax.xml.validation.Schema;
22 import javax.xml.validation.SchemaFactory;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.NamedNodeMap;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28 import org.xml.sax.ErrorHandler;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.SAXParseException;
33 import com.ibm.icu.dev.tool.UOption;
35 public final class XLIFF2ICUConverter {
38 * These must be kept in sync with getOptions().
40 private static final int HELP1 = 0;
41 private static final int HELP2 = 1;
42 private static final int SOURCEDIR = 2;
43 private static final int DESTDIR = 3;
44 private static final int TARGETONLY = 4;
45 private static final int SOURCEONLY = 5;
46 private static final int MAKE_SOURCE_ROOT = 6;
47 private static final int XLIFF_1_0 = 7;
49 private static final UOption[] options = new UOption[] {
51 UOption.HELP_QUESTION_MARK(),
54 UOption.create("target-only", 't', UOption.OPTIONAL_ARG),
55 UOption.create("source-only", 'c', UOption.OPTIONAL_ARG),
56 UOption.create("make-source-root", 'r', UOption.NO_ARG),
57 UOption.create("xliff-1.0", 'x', UOption.NO_ARG)
60 private static final int ARRAY_RESOURCE = 0;
61 private static final int ALIAS_RESOURCE = 1;
62 private static final int BINARY_RESOURCE = 2;
63 private static final int INTEGER_RESOURCE = 3;
64 private static final int INTVECTOR_RESOURCE = 4;
65 private static final int TABLE_RESOURCE = 5;
67 private static final String NEW_RESOURCES[] = {
76 private static final String OLD_RESOURCES[] = {
85 private String resources[];
87 private static final String ROOT = "root";
88 private static final String RESTYPE = "restype";
89 private static final String RESNAME = "resname";
90 //private static final String YES = "yes";
91 //private static final String NO = "no";
92 private static final String TRANSLATE = "translate";
93 //private static final String BODY = "body";
94 private static final String GROUPS = "group";
95 private static final String FILES = "file";
96 private static final String TRANSUNIT = "trans-unit";
97 private static final String BINUNIT = "bin-unit";
98 private static final String BINSOURCE = "bin-source";
99 //private static final String TS = "ts";
100 //private static final String ORIGINAL = "original";
101 private static final String SOURCELANGUAGE = "source-language";
102 private static final String TARGETLANGUAGE = "target-language";
103 private static final String TARGET = "target";
104 private static final String SOURCE = "source";
105 private static final String NOTE = "note";
106 private static final String XMLLANG = "xml:lang";
107 private static final String FILE = "file";
108 private static final String INTVECTOR = "intvector";
109 private static final String ARRAYS = "array";
110 private static final String STRINGS = "string";
111 private static final String BIN = "bin";
112 private static final String INTS = "int";
113 private static final String TABLE = "table";
114 private static final String IMPORT = "import";
115 private static final String HREF = "href";
116 private static final String EXTERNALFILE = "external-file";
117 private static final String INTERNALFILE = "internal-file";
118 private static final String ALTTRANS = "alt-trans";
119 private static final String CRC = "crc";
120 private static final String ALIAS = "alias";
121 private static final String LINESEP = System.getProperty("line.separator");
122 private static final String BOM = "\uFEFF";
123 private static final String CHARSET = "UTF-8";
124 private static final String OPENBRACE = "{";
125 private static final String CLOSEBRACE = "}";
126 private static final String COLON = ":";
127 private static final String COMMA = ",";
128 private static final String QUOTE = "\"";
129 private static final String COMMENTSTART = "/**";
130 private static final String COMMENTEND = " */";
131 private static final String TAG = " * @";
132 private static final String COMMENTMIDDLE = " * ";
133 private static final String SPACE = " ";
134 private static final String INDENT = " ";
135 private static final String EMPTY = "";
136 private static final String ID = "id";
138 public static void main(String[] args) {
139 XLIFF2ICUConverter cnv = new XLIFF2ICUConverter();
140 cnv.processArgs(args);
142 private String sourceDir = null;
143 //private String fileName = null;
144 private String destDir = null;
145 private boolean targetOnly = false;
146 private String targetFileName = null;
147 private boolean makeSourceRoot = false;
148 private String sourceFileName = null;
149 private boolean sourceOnly = false;
150 private boolean xliff10 = false;
152 private void processArgs(String[] args) {
153 int remainingArgc = 0;
155 remainingArgc = UOption.parseArgs(args, options);
156 }catch (Exception e){
157 System.err.println("ERROR: "+ e.toString());
160 if(args.length==0 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
163 if(remainingArgc==0){
164 System.err.println("ERROR: Either the file name to be processed is not "+
165 "specified or the it is specified after the -t/-c \n"+
166 "option which has an optional argument. Try rearranging "+
170 if(options[SOURCEDIR].doesOccur) {
171 sourceDir = options[SOURCEDIR].value;
174 if(options[DESTDIR].doesOccur) {
175 destDir = options[DESTDIR].value;
178 if(options[TARGETONLY].doesOccur){
180 targetFileName = options[TARGETONLY].value;
183 if(options[SOURCEONLY].doesOccur){
185 sourceFileName = options[SOURCEONLY].value;
188 if(options[MAKE_SOURCE_ROOT].doesOccur){
189 makeSourceRoot = true;
192 if(options[XLIFF_1_0].doesOccur) {
200 if(sourceOnly == true && targetOnly == true){
201 System.err.println("--source-only and --target-only are specified. Please check the arguments and try again.");
205 for (int i = 0; i < remainingArgc; i++) {
206 //int lastIndex = args[i].lastIndexOf(File.separator, args[i].length()) + 1; /* add 1 to skip past the separator */
207 //fileName = args[i].substring(lastIndex, args[i].length());
208 String xmlfileName = getFullPath(false,args[i]);
209 System.out.println("Processing file: "+xmlfileName);
210 createRB(xmlfileName);
214 private void usage() {
215 System.out.println("\nUsage: XLIFF2ICUConverter [OPTIONS] [FILES]\n\n"+
216 "This program is used to convert XLIFF files to ICU ResourceBundle TXT files.\n"+
217 "Please refer to the following options. Options are not case sensitive.\n"+
219 "-s or --sourcedir source directory for files followed by path, default is current directory.\n" +
220 "-d or --destdir destination directory, followed by the path, default is current directory.\n" +
221 "-h or -? or --help this usage text.\n"+
222 "-t or --target-only only generate the target language txt file, followed by optional output file name.\n" +
223 " Cannot be used in conjunction with --source-only.\n"+
224 "-c or --source-only only generate the source language bundle followed by optional output file name.\n"+
225 " Cannot be used in conjunction with --target-only.\n"+
226 "-r or --make-source-root produce root bundle from source elements.\n" +
227 "-x or --xliff-1.0 source file is XLIFF 1.0" +
228 "example: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter -t <optional argument> -s xxx -d yyy myResources.xlf");
232 private String getFullPath(boolean fileType, String fName){
234 int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1; /*add 1 to skip past the separator*/
235 int lastIndex2 = fName.lastIndexOf('.', fName.length());
236 if (fileType == true) {
237 if(lastIndex2 == -1){
238 fName = fName.trim() + ".txt";
240 if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){
241 fName = fName.substring(lastIndex1,lastIndex2) + ".txt";
244 if (destDir != null && fName != null) {
245 str = destDir + File.separator + fName.trim();
247 str = System.getProperty("user.dir") + File.separator + fName.trim();
250 if(lastIndex2 == -1){
251 fName = fName.trim() + ".xlf";
253 if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){
254 fName = fName.substring(lastIndex1,lastIndex2) + ".xlf";
257 if(sourceDir != null && fName != null) {
258 str = sourceDir + File.separator + fName;
259 } else if (lastIndex1 > 0) {
262 str = System.getProperty("user.dir") + File.separator + fName;
269 * Utility method to translate a String filename to URL.
271 * Note: This method is not necessarily proven to get the
272 * correct URL for every possible kind of filename; it should
273 * be improved. It handles the most common cases that we've
274 * encountered when running Conformance tests on Xalan.
275 * Also note, this method does not handle other non-file:
276 * flavors of URLs at all.
278 * If the name is null, return null.
279 * If the name starts with a common URI scheme (namely the ones
280 * found in the examples of RFC2396), then simply return the
281 * name as-is (the assumption is that it's already a URL)
282 * Otherwise we attempt (cheaply) to convert to a file:/// URL.
284 private static String filenameToURL(String filename){
285 // null begets null - something like the commutative property
286 if (null == filename){
290 // Don't translate a string that already looks like a URL
291 if (filename.startsWith("file:")
292 || filename.startsWith("http:")
293 || filename.startsWith("ftp:")
294 || filename.startsWith("gopher:")
295 || filename.startsWith("mailto:")
296 || filename.startsWith("news:")
297 || filename.startsWith("telnet:")
303 File f = new File(filename);
306 // This normally gives a better path
307 tmp = f.getCanonicalPath();
308 }catch (IOException ioe){
309 // But this can be used as a backup, for cases
310 // where the file does not exist, etc.
311 tmp = f.getAbsolutePath();
314 // URLs must explicitly use only forward slashes
315 if (File.separatorChar == '\\') {
316 tmp = tmp.replace('\\', '/');
318 // Note the presumption that it's a file reference
319 // Ensure we have the correct number of slashes at the
320 // start: we always want 3 /// if it's absolute
321 // (which we should have forced above)
322 if (tmp.startsWith("/")){
323 return "file://" + tmp;
326 return "file:///" + tmp;
329 private boolean isXmlLang (String lang){
334 if (lang.length () < 2){
341 if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')){
345 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
347 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){
354 while (suffix < lang.length ()) {
355 c = lang.charAt(suffix);
359 while (++suffix < lang.length ()) {
360 c = lang.charAt(suffix);
361 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))){
366 return ((lang.length() == suffix) && (c != '-'));
369 private void createRB(String xmlfileName) {
371 String urls = filenameToURL(xmlfileName);
372 DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
373 dfactory.setNamespaceAware(true);
377 dfactory.setValidating(true);
378 resources = OLD_RESOURCES;
381 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
382 Schema schema = schemaFactory.newSchema();
384 dfactory.setSchema(schema);
385 } catch (SAXException e) {
386 System.err.println("Can't create the schema...");
388 } catch (UnsupportedOperationException e) {
389 System.err.println("ERROR:\tOne of the schema operations is not supported with this JVM.");
390 System.err.println("\tIf you are using GNU Java, you should try using the latest Sun JVM.");
391 System.err.println("\n*Here is the stack trace:");
396 resources = NEW_RESOURCES;
399 ErrorHandler nullHandler = new ErrorHandler() {
400 public void warning(SAXParseException e) throws SAXException {
403 public void error(SAXParseException e) throws SAXException {
404 System.err.println("The XLIFF document is invalid, please check it first: ");
405 System.err.println("Line "+e.getLineNumber()+", Column "+e.getColumnNumber());
406 System.err.println("Error: " + e.getMessage());
409 public void fatalError(SAXParseException e) throws SAXException {
415 DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
416 docBuilder.setErrorHandler(nullHandler);
417 doc = docBuilder.parse(new InputSource(urls));
419 NodeList nlist = doc.getElementsByTagName(FILES);
420 if(nlist.getLength()>1){
421 throw new RuntimeException("Multiple <file> elements in the XLIFF file not supported.");
424 // get the value of source-language attribute
425 String sourceLang = getLanguageName(doc, SOURCELANGUAGE);
426 // get the value of target-language attribute
427 String targetLang = getLanguageName(doc, TARGETLANGUAGE);
429 // get the list of <source> elements
430 NodeList sourceList = doc.getElementsByTagName(SOURCE);
431 // get the list of target elements
432 NodeList targetList = doc.getElementsByTagName(TARGET);
434 // check if the xliff file has source elements in multiple languages
435 // the source-language value should be the same as xml:lang values
436 // of all the source elements.
437 String xmlSrcLang = checkLangAttribute(sourceList, sourceLang);
439 // check if the xliff file has target elements in multiple languages
440 // the target-language value should be the same as xml:lang values
441 // of all the target elements.
442 String xmlTargetLang = checkLangAttribute(targetList, targetLang);
444 // Create the Resource linked list which will hold the
445 // source and target bundles after parsing
446 Resource[] set = new Resource[2];
447 set[0] = new ResourceTable();
448 set[1] = new ResourceTable();
450 // lenient extraction of source language
451 if(makeSourceRoot == true){
453 }else if(sourceLang!=null){
454 set[0].name = sourceLang.replace('-','_');
456 if(xmlSrcLang != null){
457 set[0].name = xmlSrcLang.replace('-','_');
459 System.err.println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file.");
464 // lenient extraction of the target language
465 if(targetLang!=null){
466 set[1].name = targetLang.replace('-','_');
468 if(xmlTargetLang!=null){
469 set[1].name = xmlTargetLang.replace('-','_');
471 System.err.println("WARNING: Could not figure out the target language of the file. Producing source bundle only.");
476 // check if any <alt-trans> elements are present
477 NodeList altTrans = doc.getElementsByTagName(ALTTRANS);
478 if(altTrans.getLength()>0){
479 System.err.println("WARNING: <alt-trans> elements in found. Ignoring all <alt-trans> elements.");
482 // get all the group elements
483 NodeList list = doc.getElementsByTagName(GROUPS);
485 // process the first group element. The first group element is
486 // the base table that must be parsed recursively
487 parseTable(list.item(0), set);
489 // write out the bundle
490 writeResource(set, xmlfileName);
492 catch (Throwable se) {
493 System.err.println("ERROR: " + se.toString());
498 private void writeResource(Resource[] set, String xmlfileName){
499 if(targetOnly==false){
500 writeResource(set[0], xmlfileName, sourceFileName);
502 if(sourceOnly == false){
503 if(targetOnly==true && set[1].name == null){
504 throw new RuntimeException("The "+ xmlfileName +" does not contain translation\n");
506 if(set[1].name != null){
507 writeResource(set[1], xmlfileName, targetFileName);
512 private void writeResource(Resource set, String sourceFilename, String targetFilename){
514 String outputFileName = null;
515 if(targetFilename != null){
516 outputFileName = destDir+File.separator+targetFilename+".txt";
518 outputFileName = destDir+File.separator+set.name+".txt";
520 FileOutputStream file = new FileOutputStream(outputFileName);
521 BufferedOutputStream writer = new BufferedOutputStream(file);
523 writeHeader(writer,sourceFilename);
525 //Now start writing the resource;
526 Resource current = set;
527 while(current!=null){
528 current.write(writer, 0, false);
529 current = current.next;
533 } catch (Exception ie) {
534 System.err.println("ERROR :" + ie.toString());
539 private String getLanguageName(Document doc, String lang){
541 NodeList list = doc.getElementsByTagName(FILE);
542 Node node = list.item(0);
543 NamedNodeMap attr = node.getAttributes();
544 Node orig = attr.getNamedItem(lang);
547 String name = orig.getNodeValue();
548 NodeList groupList = doc.getElementsByTagName(GROUPS);
549 Node group = groupList.item(0);
550 NamedNodeMap groupAtt = group.getAttributes();
551 Node id = groupAtt.getNamedItem(ID);
553 String idVal = id.getNodeValue();
555 if(!name.equals(idVal)){
556 System.out.println("WARNING: The id value != language name. " +
557 "Please compare the output with the orignal " +
558 "ICU ResourceBundle before proceeding.");
561 if(!isXmlLang(name)){
562 System.err.println("The attribute "+ lang + "=\""+ name +
563 "\" of <file> element is invalid.");
572 // check if the xliff file is translated into multiple languages
573 // The XLIFF specification allows for single <target> element
574 // as the child of <trans-unit> but the attributes of the
575 // <target> element may different across <trans-unit> elements
576 // check for it. Similar is the case with <source> elements
577 private String checkLangAttribute(NodeList list, String origName){
578 String oldLangName=origName;
579 for(int i = 0 ;i<list.getLength(); i++){
580 Node node = list.item(i);
581 NamedNodeMap attr = node.getAttributes();
582 Node lang = attr.getNamedItem(XMLLANG);
583 String langName = null;
584 // the target element should always contain xml:lang attribute
587 System.err.println("Encountered <target> element without xml:lang attribute. Please fix the below element in the XLIFF file.\n"+ node.toString());
593 langName = lang.getNodeValue();
596 if(oldLangName!=null && langName!=null && !langName.equals(oldLangName)){
597 throw new RuntimeException("The <trans-unit> elements must be bilingual, multilingual tranlations not supported. xml:lang = " + oldLangName +
598 " and xml:lang = " + langName);
600 oldLangName = langName;
605 private class Resource{
606 String[] note = new String[20];
612 public String escapeSyntaxChars(String val){
613 // escape the embedded quotes
614 char[] str = val.toCharArray();
615 StringBuffer result = new StringBuffer();
616 for(int i=0; i<str.length; i++){
619 result.append('\\'); //append backslash
621 result.append(str[i]);
624 return result.toString();
626 public void write(OutputStream writer, int numIndent, boolean bare){
628 next.write(writer, numIndent+1, false);
631 public void writeIndent(OutputStream writer, int numIndent){
632 for(int i=0; i< numIndent; i++){
633 write(writer,INDENT);
636 public void write(OutputStream writer, String value){
638 byte[] bytes = value.getBytes(CHARSET);
639 writer.write(bytes, 0, bytes.length);
640 } catch(Exception e) {
641 System.err.println(e);
645 public void writeComments(OutputStream writer, int numIndent){
646 boolean translateIsDefault = translate == null || translate.equals("yes");
648 if(comment!=null || ! translateIsDefault || noteLen > 0){
649 // print the start of the comment
650 writeIndent(writer, numIndent);
651 write(writer, COMMENTSTART+LINESEP);
653 // print comment if any
655 writeIndent(writer, numIndent);
656 write(writer, COMMENTMIDDLE);
657 write(writer, comment);
658 write(writer, LINESEP);
661 // print the translate attribute if any
662 if(! translateIsDefault){
663 writeIndent(writer, numIndent);
664 write(writer, TAG+TRANSLATE+SPACE);
665 write(writer, translate);
666 write(writer, LINESEP);
669 // print note elements if any
670 for(int i=0; i<noteLen; i++){
672 writeIndent(writer, numIndent);
673 write(writer, TAG+NOTE+SPACE+note[i]);
674 write(writer, LINESEP);
678 // terminate the comment
679 writeIndent(writer, numIndent);
680 write(writer, COMMENTEND+LINESEP);
685 private class ResourceString extends Resource{
687 public void write(OutputStream writer, int numIndent, boolean bare){
688 writeComments(writer, numIndent);
689 writeIndent(writer, numIndent);
692 throw new RuntimeException("Bare option is set to true but the resource has a name!");
695 write(writer,QUOTE+escapeSyntaxChars(val)+QUOTE);
697 write(writer, name+COLON+STRINGS+ OPENBRACE + QUOTE + escapeSyntaxChars(val) + QUOTE+ CLOSEBRACE + LINESEP);
701 private class ResourceAlias extends Resource{
703 public void write(OutputStream writer, int numIndent, boolean bare){
704 writeComments(writer, numIndent);
705 writeIndent(writer, numIndent);
706 String line = ((name==null)? EMPTY: name)+COLON+ALIAS+ OPENBRACE+QUOTE+escapeSyntaxChars(val)+QUOTE+CLOSEBRACE;
709 throw new RuntimeException("Bare option is set to true but the resource has a name!");
713 write(writer, line+LINESEP);
717 private class ResourceInt extends Resource{
719 public void write(OutputStream writer, int numIndent, boolean bare){
720 writeComments(writer, numIndent);
721 writeIndent(writer, numIndent);
722 String line = ((name==null)? EMPTY: name)+COLON+INTS+ OPENBRACE + val +CLOSEBRACE;
725 throw new RuntimeException("Bare option is set to true but the resource has a name!");
729 write(writer, line+LINESEP);
733 private class ResourceBinary extends Resource{
736 public void write(OutputStream writer, int numIndent, boolean bare){
737 writeComments(writer, numIndent);
738 writeIndent(writer, numIndent);
740 String line = ((name==null) ? EMPTY : name)+COLON+IMPORT+ OPENBRACE+QUOTE+external+QUOTE+CLOSEBRACE + ((bare==true) ? EMPTY : LINESEP);
743 String line = ((name==null) ? EMPTY : name)+COLON+BIN+ OPENBRACE+internal+CLOSEBRACE+ ((bare==true) ? EMPTY : LINESEP);
749 private class ResourceIntVector extends Resource{
751 public void write(OutputStream writer, int numIndent, boolean bare){
752 writeComments(writer, numIndent);
753 writeIndent(writer, numIndent);
754 write(writer, name+COLON+INTVECTOR+OPENBRACE+LINESEP);
756 ResourceInt current = (ResourceInt) first;
757 while(current != null){
758 //current.write(writer, numIndent, true);
759 writeIndent(writer, numIndent);
760 write(writer, current.val);
761 write(writer, COMMA+LINESEP);
762 current = (ResourceInt) current.next;
765 writeIndent(writer, numIndent);
766 write(writer, CLOSEBRACE+LINESEP);
769 private class ResourceTable extends Resource{
771 public void write(OutputStream writer, int numIndent, boolean bare){
772 writeComments(writer, numIndent);
773 writeIndent(writer, numIndent);
774 write(writer, name+COLON+TABLE+OPENBRACE+LINESEP);
776 Resource current = first;
777 while(current != null){
778 current.write(writer, numIndent, false);
779 current = current.next;
782 writeIndent(writer, numIndent);
783 write(writer, CLOSEBRACE+LINESEP);
786 private class ResourceArray extends Resource{
788 public void write(OutputStream writer, int numIndent, boolean bare){
789 writeComments(writer, numIndent);
790 writeIndent(writer, numIndent);
791 write(writer, name+COLON+ARRAYS+OPENBRACE+LINESEP);
793 Resource current = first;
794 while(current != null){
795 current.write(writer, numIndent, true);
796 write(writer, COMMA+LINESEP);
797 current = current.next;
800 writeIndent(writer, numIndent);
801 write(writer, CLOSEBRACE+LINESEP);
805 private String getAttributeValue(Node sNode, String attribName){
809 NamedNodeMap attributes = node.getAttributes();
810 Node attr = attributes.getNamedItem(attribName);
812 value = attr.getNodeValue();
818 private void parseResourceString(Node node, ResourceString[] set){
819 ResourceString currentSource;
820 ResourceString currentTarget;
821 currentSource = set[0];
822 currentTarget = set[1];
823 String resName = getAttributeValue(node, RESNAME);
824 String translate = getAttributeValue(node, TRANSLATE);
826 // loop to pickup <source>, <note> and <target> elements
827 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
828 short type = transUnit.getNodeType();
829 String name = transUnit.getNodeName();
830 if(type == Node.COMMENT_NODE){
832 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
833 }else if( type == Node.ELEMENT_NODE){
834 if(name.equals(SOURCE)){
835 // save the source and target values
836 currentSource.name = currentTarget.name = resName;
837 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
838 currentSource.translate = currentTarget.translate = translate;
839 }else if(name.equals(NOTE)){
840 // save the note values
841 currentSource.note[currentSource.noteLen++] =
842 currentTarget.note[currentTarget.noteLen++] =
843 transUnit.getFirstChild().getNodeValue();
844 }else if(name.equals(TARGET)){
845 // if there is a target element replace it
846 currentTarget.val = transUnit.getFirstChild().getNodeValue();
853 private void parseResourceInt(Node node, ResourceInt[] set){
854 ResourceInt currentSource;
855 ResourceInt currentTarget;
856 currentSource = set[0];
857 currentTarget = set[1];
858 String resName = getAttributeValue(node, RESNAME);
859 String translate = getAttributeValue(node, TRANSLATE);
860 // loop to pickup <source>, <note> and <target> elements
861 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
862 short type = transUnit.getNodeType();
863 String name = transUnit.getNodeName();
864 if(type == Node.COMMENT_NODE){
866 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
867 }else if( type == Node.ELEMENT_NODE){
868 if(name.equals(SOURCE)){
869 // save the source and target values
870 currentSource.name = currentTarget.name = resName;
871 currentSource.translate = currentTarget.translate = translate;
872 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
873 }else if(name.equals(NOTE)){
874 // save the note values
875 currentSource.note[currentSource.noteLen++] =
876 currentTarget.note[currentTarget.noteLen++] =
877 transUnit.getFirstChild().getNodeValue();
878 }else if(name.equals(TARGET)){
879 // if there is a target element replace it
880 currentTarget.val = transUnit.getFirstChild().getNodeValue();
887 private void parseResourceAlias(Node node, ResourceAlias[] set){
888 ResourceAlias currentSource;
889 ResourceAlias currentTarget;
890 currentSource = set[0];
891 currentTarget = set[1];
892 String resName = getAttributeValue(node, RESNAME);
893 String translate = getAttributeValue(node, TRANSLATE);
894 // loop to pickup <source>, <note> and <target> elements
895 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
896 short type = transUnit.getNodeType();
897 String name = transUnit.getNodeName();
898 if(type == Node.COMMENT_NODE){
900 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
901 }else if( type == Node.ELEMENT_NODE){
902 if(name.equals(SOURCE)){
903 // save the source and target values
904 currentSource.name = currentTarget.name = resName;
905 currentSource.translate = currentTarget.translate = translate;
906 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
907 }else if(name.equals(NOTE)){
908 // save the note values
909 currentSource.note[currentSource.noteLen++] =
910 currentTarget.note[currentTarget.noteLen++] =
911 transUnit.getFirstChild().getNodeValue();
912 }else if(name.equals(TARGET)){
913 // if there is a target element replace it
914 currentTarget.val = transUnit.getFirstChild().getNodeValue();
920 private void parseResourceBinary(Node node, ResourceBinary[] set){
921 ResourceBinary currentSource;
922 ResourceBinary currentTarget;
923 currentSource = set[0];
924 currentTarget = set[1];
926 // loop to pickup <source>, <note> and <target> elements
927 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
928 short type = transUnit.getNodeType();
929 String name = transUnit.getNodeName();
930 if(type == Node.COMMENT_NODE){
932 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
933 }else if( type == Node.ELEMENT_NODE){
934 if(name.equals(BINSOURCE)){
935 // loop to pickup internal-file/extenal-file element
937 }else if(name.equals(NOTE)){
938 // save the note values
939 currentSource.note[currentSource.noteLen++] =
940 currentTarget.note[currentTarget.noteLen++] =
941 transUnit.getFirstChild().getNodeValue();
942 }else if(name.equals(INTERNALFILE)){
943 // if there is a target element replace it
944 String crc = getAttributeValue(transUnit, CRC);
945 String value = transUnit.getFirstChild().getNodeValue();
947 //verify that the binary value conforms to the CRC
948 if(Integer.parseInt(crc, 10) != CalculateCRC32.computeCRC32(value)) {
949 System.err.println("ERROR: CRC value incorrect! Please check.");
953 currentTarget.internal = currentSource.internal= value;
955 }else if(name.equals(EXTERNALFILE)){
956 currentSource.external = getAttributeValue(transUnit, HREF);
957 currentTarget.external = currentSource.external;
963 private void parseTransUnit(Node node, Resource[] set){
965 String attrType = getAttributeValue(node, RESTYPE);
966 String translate = getAttributeValue(node, TRANSLATE);
967 if(attrType==null || attrType.equals(STRINGS)){
968 ResourceString[] strings = new ResourceString[2];
969 strings[0] = new ResourceString();
970 strings[1] = new ResourceString();
971 parseResourceString(node, strings);
972 strings[0].translate = strings[1].translate = translate;
975 }else if(attrType.equals(resources[INTEGER_RESOURCE])){
976 ResourceInt[] ints = new ResourceInt[2];
977 ints[0] = new ResourceInt();
978 ints[1] = new ResourceInt();
979 parseResourceInt(node, ints);
980 ints[0].translate = ints[1].translate = translate;
983 }else if(attrType.equals(resources[ALIAS_RESOURCE])){
984 ResourceAlias[] ints = new ResourceAlias[2];
985 ints[0] = new ResourceAlias();
986 ints[1] = new ResourceAlias();
987 parseResourceAlias(node, ints);
988 ints[0].translate = ints[1].translate = translate;
994 private void parseBinUnit(Node node, Resource[] set){
995 if (getAttributeValue(node, RESTYPE).equals(resources[BINARY_RESOURCE])) {
996 ResourceBinary[] bins = new ResourceBinary[2];
998 bins[0] = new ResourceBinary();
999 bins[1] = new ResourceBinary();
1001 Resource currentSource = bins[0];
1002 Resource currentTarget = bins[1];
1003 String resName = getAttributeValue(node, RESNAME);
1004 String translate = getAttributeValue(node, TRANSLATE);
1006 currentTarget.name = currentSource.name = resName;
1007 currentSource.translate = currentTarget.translate = translate;
1009 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
1010 short type = child.getNodeType();
1011 String name = child.getNodeName();
1013 if(type == Node.COMMENT_NODE){
1014 currentSource.comment = currentTarget.comment = child.getNodeValue();
1015 }else if(type == Node.ELEMENT_NODE){
1016 if(name.equals(BINSOURCE)){
1017 parseResourceBinary(child, bins);
1018 }else if(name.equals(NOTE)){
1019 String note = child.getFirstChild().getNodeValue();
1021 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1031 private void parseArray(Node node, Resource[] set){
1033 set[0] = new ResourceArray();
1034 set[1] = new ResourceArray();
1036 Resource currentSource = set[0];
1037 Resource currentTarget = set[1];
1038 String resName = getAttributeValue(node, RESNAME);
1039 currentSource.name = currentTarget.name = resName;
1040 boolean isFirst = true;
1042 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
1043 short type = child.getNodeType();
1044 String name = child.getNodeName();
1045 if(type == Node.COMMENT_NODE){
1046 currentSource.comment = currentTarget.comment = child.getNodeValue();
1047 }else if(type == Node.ELEMENT_NODE){
1048 if(name.equals(TRANSUNIT)){
1049 Resource[] next = new Resource[2];
1050 parseTransUnit(child, next);
1052 ((ResourceArray) currentSource).first = next[0];
1053 ((ResourceArray) currentTarget).first = next[1];
1054 currentSource = ((ResourceArray) currentSource).first;
1055 currentTarget = ((ResourceArray) currentTarget).first;
1058 currentSource.next = next[0];
1059 currentTarget.next = next[1];
1060 // set the next pointers
1061 currentSource = currentSource.next;
1062 currentTarget = currentTarget.next;
1064 }else if(name.equals(NOTE)){
1065 String note = child.getFirstChild().getNodeValue();
1066 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1067 }else if(name.equals(BINUNIT)){
1068 Resource[] next = new Resource[2];
1069 parseBinUnit(child, next);
1071 ((ResourceArray) currentSource).first = next[0];
1072 ((ResourceArray) currentTarget).first = next[1];
1073 currentSource = ((ResourceArray) currentSource).first.next;
1074 currentTarget = ((ResourceArray) currentTarget).first.next;
1077 currentSource.next = next[0];
1078 currentTarget.next = next[1];
1079 // set the next pointers
1080 currentSource = currentSource.next;
1081 currentTarget = currentTarget.next;
1087 private void parseIntVector(Node node, Resource[] set){
1089 set[0] = new ResourceIntVector();
1090 set[1] = new ResourceIntVector();
1092 Resource currentSource = set[0];
1093 Resource currentTarget = set[1];
1094 String resName = getAttributeValue(node, RESNAME);
1095 String translate = getAttributeValue(node,TRANSLATE);
1096 currentSource.name = currentTarget.name = resName;
1097 currentSource.translate = translate;
1098 boolean isFirst = true;
1099 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
1100 short type = child.getNodeType();
1101 String name = child.getNodeName();
1102 if(type == Node.COMMENT_NODE){
1103 currentSource.comment = currentTarget.comment = child.getNodeValue();
1104 }else if(type == Node.ELEMENT_NODE){
1105 if(name.equals(TRANSUNIT)){
1106 Resource[] next = new Resource[2];
1107 parseTransUnit(child, next);
1109 // the down cast should be safe .. if not something is terribly wrong!!
1110 ((ResourceIntVector) currentSource).first = (ResourceInt)next[0];
1111 ((ResourceIntVector) currentTarget).first = (ResourceInt) next[1];
1112 currentSource = ((ResourceIntVector) currentSource).first;
1113 currentTarget = ((ResourceIntVector) currentTarget).first;
1116 currentSource.next = next[0];
1117 currentTarget.next = next[1];
1118 // set the next pointers
1119 currentSource = currentSource.next;
1120 currentTarget = currentTarget.next;
1122 }else if(name.equals(NOTE)){
1123 String note = child.getFirstChild().getNodeValue();
1124 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1129 private void parseTable(Node node, Resource[] set){
1131 set[0] = new ResourceTable();
1132 set[1] = new ResourceTable();
1134 Resource currentSource = set[0];
1135 Resource currentTarget = set[1];
1137 String resName = getAttributeValue(node, RESNAME);
1138 String translate = getAttributeValue(node,TRANSLATE);
1139 if(resName!=null && currentSource.name==null && currentTarget.name==null){
1140 currentSource.name = currentTarget.name = resName;
1142 currentTarget.translate = currentSource.translate = translate;
1144 boolean isFirst = true;
1145 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
1146 short type = child.getNodeType();
1147 String name = child.getNodeName();
1148 if(type == Node.COMMENT_NODE){
1149 currentSource.comment = currentTarget.comment = child.getNodeValue();
1150 }else if(type == Node.ELEMENT_NODE){
1151 if(name.equals(GROUPS)){
1152 Resource[] next = new Resource[2];
1153 parseGroup(child, next);
1155 // the down cast should be safe .. if not something is terribly wrong!!
1156 ((ResourceTable) currentSource).first = next[0];
1157 ((ResourceTable) currentTarget).first = next[1];
1158 currentSource = ((ResourceTable) currentSource).first;
1159 currentTarget = ((ResourceTable) currentTarget).first;
1162 currentSource.next = next[0];
1163 currentTarget.next = next[1];
1164 // set the next pointers
1165 currentSource = currentSource.next;
1166 currentTarget = currentTarget.next;
1168 }else if(name.equals(TRANSUNIT)){
1169 Resource[] next = new Resource[2];
1170 parseTransUnit(child, next);
1172 // the down cast should be safe .. if not something is terribly wrong!!
1173 ((ResourceTable) currentSource).first = next[0];
1174 ((ResourceTable) currentTarget).first = next[1];
1175 currentSource = ((ResourceTable) currentSource).first;
1176 currentTarget = ((ResourceTable) currentTarget).first;
1179 currentSource.next = next[0];
1180 currentTarget.next = next[1];
1181 // set the next pointers
1182 currentSource = currentSource.next;
1183 currentTarget = currentTarget.next;
1185 }else if(name.equals(NOTE)){
1186 String note = child.getFirstChild().getNodeValue();
1187 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1188 }else if(name.equals(BINUNIT)){
1189 Resource[] next = new Resource[2];
1190 parseBinUnit(child, next);
1192 // the down cast should be safe .. if not something is terribly wrong!!
1193 ((ResourceTable) currentSource).first = next[0];
1194 ((ResourceTable) currentTarget).first = next[1];
1195 currentSource = ((ResourceTable) currentSource).first;
1196 currentTarget = ((ResourceTable) currentTarget).first;
1199 currentSource.next = next[0];
1200 currentTarget.next = next[1];
1201 // set the next pointers
1202 currentSource = currentSource.next;
1203 currentTarget = currentTarget.next;
1210 private void parseGroup(Node node, Resource[] set){
1212 // figure out what kind of group this is
1213 String resType = getAttributeValue(node, RESTYPE);
1214 if(resType.equals(resources[ARRAY_RESOURCE])){
1215 parseArray(node, set);
1216 }else if( resType.equals(resources[TABLE_RESOURCE])){
1217 parseTable(node, set);
1218 }else if( resType.equals(resources[INTVECTOR_RESOURCE])){
1219 parseIntVector(node, set);
1224 private void writeLine(OutputStream writer, String line) {
1226 byte[] bytes = line.getBytes(CHARSET);
1227 writer.write(bytes, 0, bytes.length);
1228 } catch (Exception e) {
1229 System.err.println(e);
1234 private void writeHeader(OutputStream writer, String fileName){
1235 final String header =
1236 "// ***************************************************************************" + LINESEP +
1238 "// * Tool: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter.java" + LINESEP +
1239 "// * Date & Time: {0,date,MM/dd/yyyy hh:mm:ss a z}"+ LINESEP +
1240 "// * Source File: {1}" + LINESEP +
1242 "// ***************************************************************************" + LINESEP;
1245 MessageFormat format = new MessageFormat(header);
1246 Object args[] = {new Date(System.currentTimeMillis()), fileName};
1248 writeLine(writer, format.format(args));
1251 private void writeBOM(OutputStream buffer) {
1253 byte[] bytes = BOM.getBytes(CHARSET);
1254 buffer.write(bytes, 0, bytes.length);
1255 } catch(Exception e) {
1256 System.err.println(e);