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