2 ******************************************************************************
\r
3 * Copyright (C) 2003-2009, 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 com.ibm.icu.dev.tool.UOption;
\r
12 import org.w3c.dom.Document;
\r
13 import org.w3c.dom.Node;
\r
14 import org.w3c.dom.NamedNodeMap;
\r
15 import org.w3c.dom.NodeList;
\r
16 import org.xml.sax.ErrorHandler;
\r
17 import org.xml.sax.InputSource;
\r
18 import org.xml.sax.SAXException;
\r
19 import org.xml.sax.SAXParseException;
\r
20 import javax.xml.parsers.DocumentBuilder;
\r
21 import javax.xml.parsers.DocumentBuilderFactory;
\r
22 import javax.xml.validation.Schema;
\r
23 import javax.xml.validation.SchemaFactory;
\r
24 import javax.xml.XMLConstants;
\r
30 public final class XLIFF2ICUConverter {
\r
33 * These must be kept in sync with getOptions().
\r
35 private static final int HELP1 = 0;
\r
36 private static final int HELP2 = 1;
\r
37 private static final int SOURCEDIR = 2;
\r
38 private static final int DESTDIR = 3;
\r
39 private static final int TARGETONLY = 4;
\r
40 private static final int SOURCEONLY = 5;
\r
41 private static final int MAKE_SOURCE_ROOT = 6;
\r
42 private static final int XLIFF_1_0 = 7;
\r
44 private static final UOption[] options = new UOption[] {
\r
46 UOption.HELP_QUESTION_MARK(),
\r
47 UOption.SOURCEDIR(),
\r
49 UOption.create("target-only", 't', UOption.OPTIONAL_ARG),
\r
50 UOption.create("source-only", 'c', UOption.OPTIONAL_ARG),
\r
51 UOption.create("make-source-root", 'r', UOption.NO_ARG),
\r
52 UOption.create("xliff-1.0", 'x', UOption.NO_ARG)
\r
55 private static final int ARRAY_RESOURCE = 0;
\r
56 private static final int ALIAS_RESOURCE = 1;
\r
57 private static final int BINARY_RESOURCE = 2;
\r
58 private static final int INTEGER_RESOURCE = 3;
\r
59 private static final int INTVECTOR_RESOURCE = 4;
\r
60 private static final int TABLE_RESOURCE = 5;
\r
62 private static final String NEW_RESOURCES[] = {
\r
71 private static final String OLD_RESOURCES[] = {
\r
80 private String resources[];
\r
82 private static final String ROOT = "root";
\r
83 private static final String RESTYPE = "restype";
\r
84 private static final String RESNAME = "resname";
\r
85 //private static final String YES = "yes";
\r
86 //private static final String NO = "no";
\r
87 private static final String TRANSLATE = "translate";
\r
88 //private static final String BODY = "body";
\r
89 private static final String GROUPS = "group";
\r
90 private static final String FILES = "file";
\r
91 private static final String TRANSUNIT = "trans-unit";
\r
92 private static final String BINUNIT = "bin-unit";
\r
93 private static final String BINSOURCE = "bin-source";
\r
94 //private static final String TS = "ts";
\r
95 //private static final String ORIGINAL = "original";
\r
96 private static final String SOURCELANGUAGE = "source-language";
\r
97 private static final String TARGETLANGUAGE = "target-language";
\r
98 private static final String TARGET = "target";
\r
99 private static final String SOURCE = "source";
\r
100 private static final String NOTE = "note";
\r
101 private static final String XMLLANG = "xml:lang";
\r
102 private static final String FILE = "file";
\r
103 private static final String INTVECTOR = "intvector";
\r
104 private static final String ARRAYS = "array";
\r
105 private static final String STRINGS = "string";
\r
106 private static final String BIN = "bin";
\r
107 private static final String INTS = "int";
\r
108 private static final String TABLE = "table";
\r
109 private static final String IMPORT = "import";
\r
110 private static final String HREF = "href";
\r
111 private static final String EXTERNALFILE = "external-file";
\r
112 private static final String INTERNALFILE = "internal-file";
\r
113 private static final String ALTTRANS = "alt-trans";
\r
114 private static final String CRC = "crc";
\r
115 private static final String ALIAS = "alias";
\r
116 private static final String LINESEP = System.getProperty("line.separator");
\r
117 private static final String BOM = "\uFEFF";
\r
118 private static final String CHARSET = "UTF-8";
\r
119 private static final String OPENBRACE = "{";
\r
120 private static final String CLOSEBRACE = "}";
\r
121 private static final String COLON = ":";
\r
122 private static final String COMMA = ",";
\r
123 private static final String QUOTE = "\"";
\r
124 private static final String COMMENTSTART = "/**";
\r
125 private static final String COMMENTEND = " */";
\r
126 private static final String TAG = " * @";
\r
127 private static final String COMMENTMIDDLE = " * ";
\r
128 private static final String SPACE = " ";
\r
129 private static final String INDENT = " ";
\r
130 private static final String EMPTY = "";
\r
131 private static final String ID = "id";
\r
133 public static void main(String[] args) {
\r
134 XLIFF2ICUConverter cnv = new XLIFF2ICUConverter();
\r
135 cnv.processArgs(args);
\r
137 private String sourceDir = null;
\r
138 //private String fileName = null;
\r
139 private String destDir = null;
\r
140 private boolean targetOnly = false;
\r
141 private String targetFileName = null;
\r
142 private boolean makeSourceRoot = false;
\r
143 private String sourceFileName = null;
\r
144 private boolean sourceOnly = false;
\r
145 private boolean xliff10 = false;
\r
147 private void processArgs(String[] args) {
\r
148 int remainingArgc = 0;
\r
150 remainingArgc = UOption.parseArgs(args, options);
\r
151 }catch (Exception e){
\r
152 System.err.println("ERROR: "+ e.toString());
\r
155 if(args.length==0 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
\r
158 if(remainingArgc==0){
\r
159 System.err.println("ERROR: Either the file name to be processed is not "+
\r
160 "specified or the it is specified after the -t/-c \n"+
\r
161 "option which has an optional argument. Try rearranging "+
\r
165 if(options[SOURCEDIR].doesOccur) {
\r
166 sourceDir = options[SOURCEDIR].value;
\r
169 if(options[DESTDIR].doesOccur) {
\r
170 destDir = options[DESTDIR].value;
\r
173 if(options[TARGETONLY].doesOccur){
\r
175 targetFileName = options[TARGETONLY].value;
\r
178 if(options[SOURCEONLY].doesOccur){
\r
180 sourceFileName = options[SOURCEONLY].value;
\r
183 if(options[MAKE_SOURCE_ROOT].doesOccur){
\r
184 makeSourceRoot = true;
\r
187 if(options[XLIFF_1_0].doesOccur) {
\r
195 if(sourceOnly == true && targetOnly == true){
\r
196 System.err.println("--source-only and --target-only are specified. Please check the arguments and try again.");
\r
200 for (int i = 0; i < remainingArgc; i++) {
\r
201 //int lastIndex = args[i].lastIndexOf(File.separator, args[i].length()) + 1; /* add 1 to skip past the separator */
\r
202 //fileName = args[i].substring(lastIndex, args[i].length());
\r
203 String xmlfileName = getFullPath(false,args[i]);
\r
204 System.out.println("Processing file: "+xmlfileName);
\r
205 createRB(xmlfileName);
\r
209 private void usage() {
\r
210 System.out.println("\nUsage: XLIFF2ICUConverter [OPTIONS] [FILES]\n\n"+
\r
211 "This program is used to convert XLIFF files to ICU ResourceBundle TXT files.\n"+
\r
212 "Please refer to the following options. Options are not case sensitive.\n"+
\r
214 "-s or --sourcedir source directory for files followed by path, default is current directory.\n" +
\r
215 "-d or --destdir destination directory, followed by the path, default is current directory.\n" +
\r
216 "-h or -? or --help this usage text.\n"+
\r
217 "-t or --target-only only generate the target language txt file, followed by optional output file name.\n" +
\r
218 " Cannot be used in conjunction with --source-only.\n"+
\r
219 "-c or --source-only only generate the source language bundle followed by optional output file name.\n"+
\r
220 " Cannot be used in conjunction with --target-only.\n"+
\r
221 "-r or --make-source-root produce root bundle from source elements.\n" +
\r
222 "-x or --xliff-1.0 source file is XLIFF 1.0" +
\r
223 "example: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter -t <optional argument> -s xxx -d yyy myResources.xlf");
\r
227 private String getFullPath(boolean fileType, String fName){
\r
229 int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1; /*add 1 to skip past the separator*/
\r
230 int lastIndex2 = fName.lastIndexOf('.', fName.length());
\r
231 if (fileType == true) {
\r
232 if(lastIndex2 == -1){
\r
233 fName = fName.trim() + ".txt";
\r
235 if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){
\r
236 fName = fName.substring(lastIndex1,lastIndex2) + ".txt";
\r
239 if (destDir != null && fName != null) {
\r
240 str = destDir + File.separator + fName.trim();
\r
242 str = System.getProperty("user.dir") + File.separator + fName.trim();
\r
245 if(lastIndex2 == -1){
\r
246 fName = fName.trim() + ".xlf";
\r
248 if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){
\r
249 fName = fName.substring(lastIndex1,lastIndex2) + ".xlf";
\r
252 if(sourceDir != null && fName != null) {
\r
253 str = sourceDir + File.separator + fName;
\r
254 } else if (lastIndex1 > 0) {
\r
257 str = System.getProperty("user.dir") + File.separator + fName;
\r
264 * Utility method to translate a String filename to URL.
\r
266 * Note: This method is not necessarily proven to get the
\r
267 * correct URL for every possible kind of filename; it should
\r
268 * be improved. It handles the most common cases that we've
\r
269 * encountered when running Conformance tests on Xalan.
\r
270 * Also note, this method does not handle other non-file:
\r
271 * flavors of URLs at all.
\r
273 * If the name is null, return null.
\r
274 * If the name starts with a common URI scheme (namely the ones
\r
275 * found in the examples of RFC2396), then simply return the
\r
276 * name as-is (the assumption is that it's already a URL)
\r
277 * Otherwise we attempt (cheaply) to convert to a file:/// URL.
\r
279 private static String filenameToURL(String filename){
\r
280 // null begets null - something like the commutative property
\r
281 if (null == filename){
\r
285 // Don't translate a string that already looks like a URL
\r
286 if (filename.startsWith("file:")
\r
287 || filename.startsWith("http:")
\r
288 || filename.startsWith("ftp:")
\r
289 || filename.startsWith("gopher:")
\r
290 || filename.startsWith("mailto:")
\r
291 || filename.startsWith("news:")
\r
292 || filename.startsWith("telnet:")
\r
298 File f = new File(filename);
\r
301 // This normally gives a better path
\r
302 tmp = f.getCanonicalPath();
\r
303 }catch (IOException ioe){
\r
304 // But this can be used as a backup, for cases
\r
305 // where the file does not exist, etc.
\r
306 tmp = f.getAbsolutePath();
\r
309 // URLs must explicitly use only forward slashes
\r
310 if (File.separatorChar == '\\') {
\r
311 tmp = tmp.replace('\\', '/');
\r
313 // Note the presumption that it's a file reference
\r
314 // Ensure we have the correct number of slashes at the
\r
315 // start: we always want 3 /// if it's absolute
\r
316 // (which we should have forced above)
\r
317 if (tmp.startsWith("/")){
\r
318 return "file://" + tmp;
\r
321 return "file:///" + tmp;
\r
324 private boolean isXmlLang (String lang){
\r
329 if (lang.length () < 2){
\r
333 c = lang.charAt(1);
\r
335 c = lang.charAt(0);
\r
336 if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')){
\r
340 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
\r
341 c = lang.charAt(0);
\r
342 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){
\r
349 while (suffix < lang.length ()) {
\r
350 c = lang.charAt(suffix);
\r
354 while (++suffix < lang.length ()) {
\r
355 c = lang.charAt(suffix);
\r
356 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){
\r
361 return ((lang.length() == suffix) && (c != '-'));
\r
364 private void createRB(String xmlfileName) {
\r
366 String urls = filenameToURL(xmlfileName);
\r
367 DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
\r
368 dfactory.setNamespaceAware(true);
\r
369 Document doc = null;
\r
372 dfactory.setValidating(true);
\r
373 resources = OLD_RESOURCES;
\r
376 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
\r
377 Schema schema = schemaFactory.newSchema();
\r
379 dfactory.setSchema(schema);
\r
380 } catch (SAXException e) {
\r
381 System.err.println("Can't create the schema...");
\r
383 } catch (UnsupportedOperationException e) {
\r
384 System.err.println("ERROR:\tOne of the schema operations is not supported with this JVM.");
\r
385 System.err.println("\tIf you are using GNU Java, you should try using the latest Sun JVM.");
\r
386 System.err.println("\n*Here is the stack trace:");
\r
387 e.printStackTrace();
\r
391 resources = NEW_RESOURCES;
\r
394 ErrorHandler nullHandler = new ErrorHandler() {
\r
395 public void warning(SAXParseException e) throws SAXException {
\r
398 public void error(SAXParseException e) throws SAXException {
\r
399 System.err.println("The XLIFF document is invalid, please check it first: ");
\r
400 System.err.println("Line "+e.getLineNumber()+", Column "+e.getColumnNumber());
\r
401 System.err.println("Error: " + e.getMessage());
\r
404 public void fatalError(SAXParseException e) throws SAXException {
\r
410 DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
\r
411 docBuilder.setErrorHandler(nullHandler);
\r
412 doc = docBuilder.parse(new InputSource(urls));
\r
414 NodeList nlist = doc.getElementsByTagName(FILES);
\r
415 if(nlist.getLength()>1){
\r
416 throw new RuntimeException("Multiple <file> elements in the XLIFF file not supported.");
\r
419 // get the value of source-language attribute
\r
420 String sourceLang = getLanguageName(doc, SOURCELANGUAGE);
\r
421 // get the value of target-language attribute
\r
422 String targetLang = getLanguageName(doc, TARGETLANGUAGE);
\r
424 // get the list of <source> elements
\r
425 NodeList sourceList = doc.getElementsByTagName(SOURCE);
\r
426 // get the list of target elements
\r
427 NodeList targetList = doc.getElementsByTagName(TARGET);
\r
429 // check if the xliff file has source elements in multiple languages
\r
430 // the source-language value should be the same as xml:lang values
\r
431 // of all the source elements.
\r
432 String xmlSrcLang = checkLangAttribute(sourceList, sourceLang);
\r
434 // check if the xliff file has target elements in multiple languages
\r
435 // the target-language value should be the same as xml:lang values
\r
436 // of all the target elements.
\r
437 String xmlTargetLang = checkLangAttribute(targetList, targetLang);
\r
439 // Create the Resource linked list which will hold the
\r
440 // source and target bundles after parsing
\r
441 Resource[] set = new Resource[2];
\r
442 set[0] = new ResourceTable();
\r
443 set[1] = new ResourceTable();
\r
445 // lenient extraction of source language
\r
446 if(makeSourceRoot == true){
\r
447 set[0].name = ROOT;
\r
448 }else if(sourceLang!=null){
\r
449 set[0].name = sourceLang.replace('-','_');
\r
451 if(xmlSrcLang != null){
\r
452 set[0].name = xmlSrcLang.replace('-','_');
\r
454 System.err.println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file.");
\r
459 // lenient extraction of the target language
\r
460 if(targetLang!=null){
\r
461 set[1].name = targetLang.replace('-','_');
\r
463 if(xmlTargetLang!=null){
\r
464 set[1].name = xmlTargetLang.replace('-','_');
\r
466 System.err.println("WARNING: Could not figure out the target language of the file. Producing source bundle only.");
\r
471 // check if any <alt-trans> elements are present
\r
472 NodeList altTrans = doc.getElementsByTagName(ALTTRANS);
\r
473 if(altTrans.getLength()>0){
\r
474 System.err.println("WARNING: <alt-trans> elements in found. Ignoring all <alt-trans> elements.");
\r
477 // get all the group elements
\r
478 NodeList list = doc.getElementsByTagName(GROUPS);
\r
480 // process the first group element. The first group element is
\r
481 // the base table that must be parsed recursively
\r
482 parseTable(list.item(0), set);
\r
484 // write out the bundle
\r
485 writeResource(set, xmlfileName);
\r
487 catch (Throwable se) {
\r
488 System.err.println("ERROR: " + se.toString());
\r
493 private void writeResource(Resource[] set, String xmlfileName){
\r
494 if(targetOnly==false){
\r
495 writeResource(set[0], xmlfileName, sourceFileName);
\r
497 if(sourceOnly == false){
\r
498 if(targetOnly==true && set[1].name == null){
\r
499 throw new RuntimeException("The "+ xmlfileName +" does not contain translation\n");
\r
501 if(set[1].name != null){
\r
502 writeResource(set[1], xmlfileName, targetFileName);
\r
507 private void writeResource(Resource set, String sourceFilename, String targetFilename){
\r
509 String outputFileName = null;
\r
510 if(targetFilename != null){
\r
511 outputFileName = destDir+File.separator+targetFilename+".txt";
\r
513 outputFileName = destDir+File.separator+set.name+".txt";
\r
515 FileOutputStream file = new FileOutputStream(outputFileName);
\r
516 BufferedOutputStream writer = new BufferedOutputStream(file);
\r
518 writeHeader(writer,sourceFilename);
\r
520 //Now start writing the resource;
\r
521 Resource current = set;
\r
522 while(current!=null){
\r
523 current.write(writer, 0, false);
\r
524 current = current.next;
\r
528 } catch (Exception ie) {
\r
529 System.err.println("ERROR :" + ie.toString());
\r
534 private String getLanguageName(Document doc, String lang){
\r
536 NodeList list = doc.getElementsByTagName(FILE);
\r
537 Node node = list.item(0);
\r
538 NamedNodeMap attr = node.getAttributes();
\r
539 Node orig = attr.getNamedItem(lang);
\r
542 String name = orig.getNodeValue();
\r
543 NodeList groupList = doc.getElementsByTagName(GROUPS);
\r
544 Node group = groupList.item(0);
\r
545 NamedNodeMap groupAtt = group.getAttributes();
\r
546 Node id = groupAtt.getNamedItem(ID);
\r
548 String idVal = id.getNodeValue();
\r
550 if(!name.equals(idVal)){
\r
551 System.out.println("WARNING: The id value != language name. " +
\r
552 "Please compare the output with the orignal " +
\r
553 "ICU ResourceBundle before proceeding.");
\r
556 if(!isXmlLang(name)){
\r
557 System.err.println("The attribute "+ lang + "=\""+ name +
\r
558 "\" of <file> element does not satisfy RFC 1766 conditions.");
\r
567 // check if the xliff file is translated into multiple languages
\r
568 // The XLIFF specification allows for single <target> element
\r
569 // as the child of <trans-unit> but the attributes of the
\r
570 // <target> element may different across <trans-unit> elements
\r
571 // check for it. Similar is the case with <source> elements
\r
572 private String checkLangAttribute(NodeList list, String origName){
\r
573 String oldLangName=origName;
\r
574 for(int i = 0 ;i<list.getLength(); i++){
\r
575 Node node = list.item(i);
\r
576 NamedNodeMap attr = node.getAttributes();
\r
577 Node lang = attr.getNamedItem(XMLLANG);
\r
578 String langName = null;
\r
579 // the target element should always contain xml:lang attribute
\r
581 if(origName==null){
\r
582 System.err.println("Encountered <target> element without xml:lang attribute. Please fix the below element in the XLIFF file.\n"+ node.toString());
\r
585 langName = origName;
\r
588 langName = lang.getNodeValue();
\r
591 if(oldLangName!=null && langName!=null && !langName.equals(oldLangName)){
\r
592 throw new RuntimeException("The <trans-unit> elements must be bilingual, multilingual tranlations not supported. xml:lang = " + oldLangName +
\r
593 " and xml:lang = " + langName);
\r
595 oldLangName = langName;
\r
597 return oldLangName;
\r
600 private class Resource{
\r
601 String[] note = new String[20];
\r
607 public String escapeSyntaxChars(String val){
\r
608 // escape the embedded quotes
\r
609 char[] str = val.toCharArray();
\r
610 StringBuffer result = new StringBuffer();
\r
611 for(int i=0; i<str.length; i++){
\r
614 result.append('\\'); //append backslash
\r
616 result.append(str[i]);
\r
619 return result.toString();
\r
621 public void write(OutputStream writer, int numIndent, boolean bare){
\r
623 next.write(writer, numIndent+1, false);
\r
626 public void writeIndent(OutputStream writer, int numIndent){
\r
627 for(int i=0; i< numIndent; i++){
\r
628 write(writer,INDENT);
\r
631 public void write(OutputStream writer, String value){
\r
633 byte[] bytes = value.getBytes(CHARSET);
\r
634 writer.write(bytes, 0, bytes.length);
\r
635 } catch(Exception e) {
\r
636 System.err.println(e);
\r
640 public void writeComments(OutputStream writer, int numIndent){
\r
641 boolean translateIsDefault = translate == null || translate.equals("yes");
\r
643 if(comment!=null || ! translateIsDefault || noteLen > 0){
\r
644 // print the start of the comment
\r
645 writeIndent(writer, numIndent);
\r
646 write(writer, COMMENTSTART+LINESEP);
\r
648 // print comment if any
\r
650 writeIndent(writer, numIndent);
\r
651 write(writer, COMMENTMIDDLE);
\r
652 write(writer, comment);
\r
653 write(writer, LINESEP);
\r
656 // print the translate attribute if any
\r
657 if(! translateIsDefault){
\r
658 writeIndent(writer, numIndent);
\r
659 write(writer, TAG+TRANSLATE+SPACE);
\r
660 write(writer, translate);
\r
661 write(writer, LINESEP);
\r
664 // print note elements if any
\r
665 for(int i=0; i<noteLen; i++){
\r
667 writeIndent(writer, numIndent);
\r
668 write(writer, TAG+NOTE+SPACE+note[i]);
\r
669 write(writer, LINESEP);
\r
673 // terminate the comment
\r
674 writeIndent(writer, numIndent);
\r
675 write(writer, COMMENTEND+LINESEP);
\r
680 private class ResourceString extends Resource{
\r
682 public void write(OutputStream writer, int numIndent, boolean bare){
\r
683 writeComments(writer, numIndent);
\r
684 writeIndent(writer, numIndent);
\r
687 throw new RuntimeException("Bare option is set to true but the resource has a name!");
\r
690 write(writer,QUOTE+escapeSyntaxChars(val)+QUOTE);
\r
692 write(writer, name+COLON+STRINGS+ OPENBRACE + QUOTE + escapeSyntaxChars(val) + QUOTE+ CLOSEBRACE + LINESEP);
\r
696 private class ResourceAlias extends Resource{
\r
698 public void write(OutputStream writer, int numIndent, boolean bare){
\r
699 writeComments(writer, numIndent);
\r
700 writeIndent(writer, numIndent);
\r
701 String line = ((name==null)? EMPTY: name)+COLON+ALIAS+ OPENBRACE+QUOTE+escapeSyntaxChars(val)+QUOTE+CLOSEBRACE;
\r
704 throw new RuntimeException("Bare option is set to true but the resource has a name!");
\r
706 write(writer,line);
\r
708 write(writer, line+LINESEP);
\r
712 private class ResourceInt extends Resource{
\r
714 public void write(OutputStream writer, int numIndent, boolean bare){
\r
715 writeComments(writer, numIndent);
\r
716 writeIndent(writer, numIndent);
\r
717 String line = ((name==null)? EMPTY: name)+COLON+INTS+ OPENBRACE + val +CLOSEBRACE;
\r
720 throw new RuntimeException("Bare option is set to true but the resource has a name!");
\r
722 write(writer,line);
\r
724 write(writer, line+LINESEP);
\r
728 private class ResourceBinary extends Resource{
\r
731 public void write(OutputStream writer, int numIndent, boolean bare){
\r
732 writeComments(writer, numIndent);
\r
733 writeIndent(writer, numIndent);
\r
734 if(internal==null){
\r
735 String line = ((name==null) ? EMPTY : name)+COLON+IMPORT+ OPENBRACE+QUOTE+external+QUOTE+CLOSEBRACE + ((bare==true) ? EMPTY : LINESEP);
\r
736 write(writer, line);
\r
738 String line = ((name==null) ? EMPTY : name)+COLON+BIN+ OPENBRACE+internal+CLOSEBRACE+ ((bare==true) ? EMPTY : LINESEP);
\r
739 write(writer,line);
\r
744 private class ResourceIntVector extends Resource{
\r
746 public void write(OutputStream writer, int numIndent, boolean bare){
\r
747 writeComments(writer, numIndent);
\r
748 writeIndent(writer, numIndent);
\r
749 write(writer, name+COLON+INTVECTOR+OPENBRACE+LINESEP);
\r
751 ResourceInt current = (ResourceInt) first;
\r
752 while(current != null){
\r
753 //current.write(writer, numIndent, true);
\r
754 writeIndent(writer, numIndent);
\r
755 write(writer, current.val);
\r
756 write(writer, COMMA+LINESEP);
\r
757 current = (ResourceInt) current.next;
\r
760 writeIndent(writer, numIndent);
\r
761 write(writer, CLOSEBRACE+LINESEP);
\r
764 private class ResourceTable extends Resource{
\r
766 public void write(OutputStream writer, int numIndent, boolean bare){
\r
767 writeComments(writer, numIndent);
\r
768 writeIndent(writer, numIndent);
\r
769 write(writer, name+COLON+TABLE+OPENBRACE+LINESEP);
\r
771 Resource current = first;
\r
772 while(current != null){
\r
773 current.write(writer, numIndent, false);
\r
774 current = current.next;
\r
777 writeIndent(writer, numIndent);
\r
778 write(writer, CLOSEBRACE+LINESEP);
\r
781 private class ResourceArray extends Resource{
\r
783 public void write(OutputStream writer, int numIndent, boolean bare){
\r
784 writeComments(writer, numIndent);
\r
785 writeIndent(writer, numIndent);
\r
786 write(writer, name+COLON+ARRAYS+OPENBRACE+LINESEP);
\r
788 Resource current = first;
\r
789 while(current != null){
\r
790 current.write(writer, numIndent, true);
\r
791 write(writer, COMMA+LINESEP);
\r
792 current = current.next;
\r
795 writeIndent(writer, numIndent);
\r
796 write(writer, CLOSEBRACE+LINESEP);
\r
800 private String getAttributeValue(Node sNode, String attribName){
\r
804 NamedNodeMap attributes = node.getAttributes();
\r
805 Node attr = attributes.getNamedItem(attribName);
\r
807 value = attr.getNodeValue();
\r
813 private void parseResourceString(Node node, ResourceString[] set){
\r
814 ResourceString currentSource;
\r
815 ResourceString currentTarget;
\r
816 currentSource = set[0];
\r
817 currentTarget = set[1];
\r
818 String resName = getAttributeValue(node, RESNAME);
\r
819 String translate = getAttributeValue(node, TRANSLATE);
\r
821 // loop to pickup <source>, <note> and <target> elements
\r
822 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
\r
823 short type = transUnit.getNodeType();
\r
824 String name = transUnit.getNodeName();
\r
825 if(type == Node.COMMENT_NODE){
\r
827 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
\r
828 }else if( type == Node.ELEMENT_NODE){
\r
829 if(name.equals(SOURCE)){
\r
830 // save the source and target values
\r
831 currentSource.name = currentTarget.name = resName;
\r
832 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
833 currentSource.translate = currentTarget.translate = translate;
\r
834 }else if(name.equals(NOTE)){
\r
835 // save the note values
\r
836 currentSource.note[currentSource.noteLen++] =
\r
837 currentTarget.note[currentTarget.noteLen++] =
\r
838 transUnit.getFirstChild().getNodeValue();
\r
839 }else if(name.equals(TARGET)){
\r
840 // if there is a target element replace it
\r
841 currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
848 private void parseResourceInt(Node node, ResourceInt[] set){
\r
849 ResourceInt currentSource;
\r
850 ResourceInt currentTarget;
\r
851 currentSource = set[0];
\r
852 currentTarget = set[1];
\r
853 String resName = getAttributeValue(node, RESNAME);
\r
854 String translate = getAttributeValue(node, TRANSLATE);
\r
855 // loop to pickup <source>, <note> and <target> elements
\r
856 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
\r
857 short type = transUnit.getNodeType();
\r
858 String name = transUnit.getNodeName();
\r
859 if(type == Node.COMMENT_NODE){
\r
861 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
\r
862 }else if( type == Node.ELEMENT_NODE){
\r
863 if(name.equals(SOURCE)){
\r
864 // save the source and target values
\r
865 currentSource.name = currentTarget.name = resName;
\r
866 currentSource.translate = currentTarget.translate = translate;
\r
867 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
868 }else if(name.equals(NOTE)){
\r
869 // save the note values
\r
870 currentSource.note[currentSource.noteLen++] =
\r
871 currentTarget.note[currentTarget.noteLen++] =
\r
872 transUnit.getFirstChild().getNodeValue();
\r
873 }else if(name.equals(TARGET)){
\r
874 // if there is a target element replace it
\r
875 currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
882 private void parseResourceAlias(Node node, ResourceAlias[] set){
\r
883 ResourceAlias currentSource;
\r
884 ResourceAlias currentTarget;
\r
885 currentSource = set[0];
\r
886 currentTarget = set[1];
\r
887 String resName = getAttributeValue(node, RESNAME);
\r
888 String translate = getAttributeValue(node, TRANSLATE);
\r
889 // loop to pickup <source>, <note> and <target> elements
\r
890 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
\r
891 short type = transUnit.getNodeType();
\r
892 String name = transUnit.getNodeName();
\r
893 if(type == Node.COMMENT_NODE){
\r
895 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
\r
896 }else if( type == Node.ELEMENT_NODE){
\r
897 if(name.equals(SOURCE)){
\r
898 // save the source and target values
\r
899 currentSource.name = currentTarget.name = resName;
\r
900 currentSource.translate = currentTarget.translate = translate;
\r
901 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
902 }else if(name.equals(NOTE)){
\r
903 // save the note values
\r
904 currentSource.note[currentSource.noteLen++] =
\r
905 currentTarget.note[currentTarget.noteLen++] =
\r
906 transUnit.getFirstChild().getNodeValue();
\r
907 }else if(name.equals(TARGET)){
\r
908 // if there is a target element replace it
\r
909 currentTarget.val = transUnit.getFirstChild().getNodeValue();
\r
915 private void parseResourceBinary(Node node, ResourceBinary[] set){
\r
916 ResourceBinary currentSource;
\r
917 ResourceBinary currentTarget;
\r
918 currentSource = set[0];
\r
919 currentTarget = set[1];
\r
921 // loop to pickup <source>, <note> and <target> elements
\r
922 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){
\r
923 short type = transUnit.getNodeType();
\r
924 String name = transUnit.getNodeName();
\r
925 if(type == Node.COMMENT_NODE){
\r
927 currentSource.comment = currentTarget.comment = transUnit.getNodeValue();
\r
928 }else if( type == Node.ELEMENT_NODE){
\r
929 if(name.equals(BINSOURCE)){
\r
930 // loop to pickup internal-file/extenal-file element
\r
932 }else if(name.equals(NOTE)){
\r
933 // save the note values
\r
934 currentSource.note[currentSource.noteLen++] =
\r
935 currentTarget.note[currentTarget.noteLen++] =
\r
936 transUnit.getFirstChild().getNodeValue();
\r
937 }else if(name.equals(INTERNALFILE)){
\r
938 // if there is a target element replace it
\r
939 String crc = getAttributeValue(transUnit, CRC);
\r
940 String value = transUnit.getFirstChild().getNodeValue();
\r
942 //verify that the binary value conforms to the CRC
\r
943 if(Integer.parseInt(crc, 10) != CalculateCRC32.computeCRC32(value)) {
\r
944 System.err.println("ERROR: CRC value incorrect! Please check.");
\r
948 currentTarget.internal = currentSource.internal= value;
\r
950 }else if(name.equals(EXTERNALFILE)){
\r
951 currentSource.external = getAttributeValue(transUnit, HREF);
\r
952 currentTarget.external = currentSource.external;
\r
958 private void parseTransUnit(Node node, Resource[] set){
\r
960 String attrType = getAttributeValue(node, RESTYPE);
\r
961 String translate = getAttributeValue(node, TRANSLATE);
\r
962 if(attrType==null || attrType.equals(STRINGS)){
\r
963 ResourceString[] strings = new ResourceString[2];
\r
964 strings[0] = new ResourceString();
\r
965 strings[1] = new ResourceString();
\r
966 parseResourceString(node, strings);
\r
967 strings[0].translate = strings[1].translate = translate;
\r
968 set[0] = strings[0];
\r
969 set[1] = strings[1];
\r
970 }else if(attrType.equals(resources[INTEGER_RESOURCE])){
\r
971 ResourceInt[] ints = new ResourceInt[2];
\r
972 ints[0] = new ResourceInt();
\r
973 ints[1] = new ResourceInt();
\r
974 parseResourceInt(node, ints);
\r
975 ints[0].translate = ints[1].translate = translate;
\r
978 }else if(attrType.equals(resources[ALIAS_RESOURCE])){
\r
979 ResourceAlias[] ints = new ResourceAlias[2];
\r
980 ints[0] = new ResourceAlias();
\r
981 ints[1] = new ResourceAlias();
\r
982 parseResourceAlias(node, ints);
\r
983 ints[0].translate = ints[1].translate = translate;
\r
989 private void parseBinUnit(Node node, Resource[] set){
\r
990 if (getAttributeValue(node, RESTYPE).equals(resources[BINARY_RESOURCE])) {
\r
991 ResourceBinary[] bins = new ResourceBinary[2];
\r
993 bins[0] = new ResourceBinary();
\r
994 bins[1] = new ResourceBinary();
\r
996 Resource currentSource = bins[0];
\r
997 Resource currentTarget = bins[1];
\r
998 String resName = getAttributeValue(node, RESNAME);
\r
999 String translate = getAttributeValue(node, TRANSLATE);
\r
1001 currentTarget.name = currentSource.name = resName;
\r
1002 currentSource.translate = currentTarget.translate = translate;
\r
1004 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
\r
1005 short type = child.getNodeType();
\r
1006 String name = child.getNodeName();
\r
1008 if(type == Node.COMMENT_NODE){
\r
1009 currentSource.comment = currentTarget.comment = child.getNodeValue();
\r
1010 }else if(type == Node.ELEMENT_NODE){
\r
1011 if(name.equals(BINSOURCE)){
\r
1012 parseResourceBinary(child, bins);
\r
1013 }else if(name.equals(NOTE)){
\r
1014 String note = child.getFirstChild().getNodeValue();
\r
1016 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
\r
1026 private void parseArray(Node node, Resource[] set){
\r
1028 set[0] = new ResourceArray();
\r
1029 set[1] = new ResourceArray();
\r
1031 Resource currentSource = set[0];
\r
1032 Resource currentTarget = set[1];
\r
1033 String resName = getAttributeValue(node, RESNAME);
\r
1034 currentSource.name = currentTarget.name = resName;
\r
1035 boolean isFirst = true;
\r
1037 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
\r
1038 short type = child.getNodeType();
\r
1039 String name = child.getNodeName();
\r
1040 if(type == Node.COMMENT_NODE){
\r
1041 currentSource.comment = currentTarget.comment = child.getNodeValue();
\r
1042 }else if(type == Node.ELEMENT_NODE){
\r
1043 if(name.equals(TRANSUNIT)){
\r
1044 Resource[] next = new Resource[2];
\r
1045 parseTransUnit(child, next);
\r
1046 if(isFirst==true){
\r
1047 ((ResourceArray) currentSource).first = next[0];
\r
1048 ((ResourceArray) currentTarget).first = next[1];
\r
1049 currentSource = ((ResourceArray) currentSource).first;
\r
1050 currentTarget = ((ResourceArray) currentTarget).first;
\r
1053 currentSource.next = next[0];
\r
1054 currentTarget.next = next[1];
\r
1055 // set the next pointers
\r
1056 currentSource = currentSource.next;
\r
1057 currentTarget = currentTarget.next;
\r
1059 }else if(name.equals(NOTE)){
\r
1060 String note = child.getFirstChild().getNodeValue();
\r
1061 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
\r
1062 }else if(name.equals(BINUNIT)){
\r
1063 Resource[] next = new Resource[2];
\r
1064 parseBinUnit(child, next);
\r
1065 if(isFirst==true){
\r
1066 ((ResourceArray) currentSource).first = next[0];
\r
1067 ((ResourceArray) currentTarget).first = next[1];
\r
1068 currentSource = ((ResourceArray) currentSource).first.next;
\r
1069 currentTarget = ((ResourceArray) currentTarget).first.next;
\r
1072 currentSource.next = next[0];
\r
1073 currentTarget.next = next[1];
\r
1074 // set the next pointers
\r
1075 currentSource = currentSource.next;
\r
1076 currentTarget = currentTarget.next;
\r
1082 private void parseIntVector(Node node, Resource[] set){
\r
1084 set[0] = new ResourceIntVector();
\r
1085 set[1] = new ResourceIntVector();
\r
1087 Resource currentSource = set[0];
\r
1088 Resource currentTarget = set[1];
\r
1089 String resName = getAttributeValue(node, RESNAME);
\r
1090 String translate = getAttributeValue(node,TRANSLATE);
\r
1091 currentSource.name = currentTarget.name = resName;
\r
1092 currentSource.translate = translate;
\r
1093 boolean isFirst = true;
\r
1094 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
\r
1095 short type = child.getNodeType();
\r
1096 String name = child.getNodeName();
\r
1097 if(type == Node.COMMENT_NODE){
\r
1098 currentSource.comment = currentTarget.comment = child.getNodeValue();
\r
1099 }else if(type == Node.ELEMENT_NODE){
\r
1100 if(name.equals(TRANSUNIT)){
\r
1101 Resource[] next = new Resource[2];
\r
1102 parseTransUnit(child, next);
\r
1103 if(isFirst==true){
\r
1104 // the down cast should be safe .. if not something is terribly wrong!!
\r
1105 ((ResourceIntVector) currentSource).first = (ResourceInt)next[0];
\r
1106 ((ResourceIntVector) currentTarget).first = (ResourceInt) next[1];
\r
1107 currentSource = ((ResourceIntVector) currentSource).first;
\r
1108 currentTarget = ((ResourceIntVector) currentTarget).first;
\r
1111 currentSource.next = next[0];
\r
1112 currentTarget.next = next[1];
\r
1113 // set the next pointers
\r
1114 currentSource = currentSource.next;
\r
1115 currentTarget = currentTarget.next;
\r
1117 }else if(name.equals(NOTE)){
\r
1118 String note = child.getFirstChild().getNodeValue();
\r
1119 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
\r
1124 private void parseTable(Node node, Resource[] set){
\r
1126 set[0] = new ResourceTable();
\r
1127 set[1] = new ResourceTable();
\r
1129 Resource currentSource = set[0];
\r
1130 Resource currentTarget = set[1];
\r
1132 String resName = getAttributeValue(node, RESNAME);
\r
1133 String translate = getAttributeValue(node,TRANSLATE);
\r
1134 if(resName!=null && currentSource.name==null && currentTarget.name==null){
\r
1135 currentSource.name = currentTarget.name = resName;
\r
1137 currentTarget.translate = currentSource.translate = translate;
\r
1139 boolean isFirst = true;
\r
1140 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){
\r
1141 short type = child.getNodeType();
\r
1142 String name = child.getNodeName();
\r
1143 if(type == Node.COMMENT_NODE){
\r
1144 currentSource.comment = currentTarget.comment = child.getNodeValue();
\r
1145 }else if(type == Node.ELEMENT_NODE){
\r
1146 if(name.equals(GROUPS)){
\r
1147 Resource[] next = new Resource[2];
\r
1148 parseGroup(child, next);
\r
1149 if(isFirst==true){
\r
1150 // the down cast should be safe .. if not something is terribly wrong!!
\r
1151 ((ResourceTable) currentSource).first = next[0];
\r
1152 ((ResourceTable) currentTarget).first = next[1];
\r
1153 currentSource = ((ResourceTable) currentSource).first;
\r
1154 currentTarget = ((ResourceTable) currentTarget).first;
\r
1157 currentSource.next = next[0];
\r
1158 currentTarget.next = next[1];
\r
1159 // set the next pointers
\r
1160 currentSource = currentSource.next;
\r
1161 currentTarget = currentTarget.next;
\r
1163 }else if(name.equals(TRANSUNIT)){
\r
1164 Resource[] next = new Resource[2];
\r
1165 parseTransUnit(child, next);
\r
1166 if(isFirst==true){
\r
1167 // the down cast should be safe .. if not something is terribly wrong!!
\r
1168 ((ResourceTable) currentSource).first = next[0];
\r
1169 ((ResourceTable) currentTarget).first = next[1];
\r
1170 currentSource = ((ResourceTable) currentSource).first;
\r
1171 currentTarget = ((ResourceTable) currentTarget).first;
\r
1174 currentSource.next = next[0];
\r
1175 currentTarget.next = next[1];
\r
1176 // set the next pointers
\r
1177 currentSource = currentSource.next;
\r
1178 currentTarget = currentTarget.next;
\r
1180 }else if(name.equals(NOTE)){
\r
1181 String note = child.getFirstChild().getNodeValue();
\r
1182 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
\r
1183 }else if(name.equals(BINUNIT)){
\r
1184 Resource[] next = new Resource[2];
\r
1185 parseBinUnit(child, next);
\r
1186 if(isFirst==true){
\r
1187 // the down cast should be safe .. if not something is terribly wrong!!
\r
1188 ((ResourceTable) currentSource).first = next[0];
\r
1189 ((ResourceTable) currentTarget).first = next[1];
\r
1190 currentSource = ((ResourceTable) currentSource).first;
\r
1191 currentTarget = ((ResourceTable) currentTarget).first;
\r
1194 currentSource.next = next[0];
\r
1195 currentTarget.next = next[1];
\r
1196 // set the next pointers
\r
1197 currentSource = currentSource.next;
\r
1198 currentTarget = currentTarget.next;
\r
1205 private void parseGroup(Node node, Resource[] set){
\r
1207 // figure out what kind of group this is
\r
1208 String resType = getAttributeValue(node, RESTYPE);
\r
1209 if(resType.equals(resources[ARRAY_RESOURCE])){
\r
1210 parseArray(node, set);
\r
1211 }else if( resType.equals(resources[TABLE_RESOURCE])){
\r
1212 parseTable(node, set);
\r
1213 }else if( resType.equals(resources[INTVECTOR_RESOURCE])){
\r
1214 parseIntVector(node, set);
\r
1219 private void writeLine(OutputStream writer, String line) {
\r
1221 byte[] bytes = line.getBytes(CHARSET);
\r
1222 writer.write(bytes, 0, bytes.length);
\r
1223 } catch (Exception e) {
\r
1224 System.err.println(e);
\r
1229 private void writeHeader(OutputStream writer, String fileName){
\r
1230 final String header =
\r
1231 "// ***************************************************************************" + LINESEP +
\r
1232 "// *" + LINESEP +
\r
1233 "// * Tool: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter.java" + LINESEP +
\r
1234 "// * Date & Time: {0,date,MM/dd/yyyy hh:mm:ss a z}"+ LINESEP +
\r
1235 "// * Source File: {1}" + LINESEP +
\r
1236 "// *" + LINESEP +
\r
1237 "// ***************************************************************************" + LINESEP;
\r
1240 MessageFormat format = new MessageFormat(header);
\r
1241 Object args[] = {new Date(System.currentTimeMillis()), fileName};
\r
1243 writeLine(writer, format.format(args));
\r
1246 private void writeBOM(OutputStream buffer) {
\r
1248 byte[] bytes = BOM.getBytes(CHARSET);
\r
1249 buffer.write(bytes, 0, bytes.length);
\r
1250 } catch(Exception e) {
\r
1251 System.err.println(e);
\r