]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/tools/misc/src/com/ibm/icu/dev/tool/localeconverter/XLIFF2ICUConverter.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / tools / misc / src / com / ibm / icu / dev / tool / localeconverter / XLIFF2ICUConverter.java
1 /*\r
2  ******************************************************************************\r
3  * Copyright (C) 2003-2010, International Business Machines Corporation and   *\r
4  * others. All Rights Reserved.                                               *\r
5  ******************************************************************************\r
6  */\r
7 \r
8 package com.ibm.icu.dev.tool.localeconverter;\r
9 \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
17 \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
23 \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
32 \r
33 import com.ibm.icu.dev.tool.UOption;\r
34 \r
35 public final class XLIFF2ICUConverter {\r
36     \r
37     /**\r
38      * These must be kept in sync with getOptions().\r
39      */\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
48        \r
49     private static final UOption[] options = new UOption[] {\r
50         UOption.HELP_H(),\r
51         UOption.HELP_QUESTION_MARK(),\r
52         UOption.SOURCEDIR(),\r
53         UOption.DESTDIR(),\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
58     };\r
59     \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
66     \r
67     private static final String NEW_RESOURCES[] = {\r
68         "x-icu-array",\r
69         "x-icu-alias",\r
70         "x-icu-binary",\r
71         "x-icu-integer",\r
72         "x-icu-intvector",\r
73         "x-icu-table"\r
74     };\r
75     \r
76     private static final String OLD_RESOURCES[] = {\r
77         "array",\r
78         "alias",\r
79         "bin",\r
80         "int",\r
81         "intvector",\r
82         "table"\r
83     };\r
84     \r
85     private String resources[];\r
86     \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
137     \r
138     public static void main(String[] args) {\r
139         XLIFF2ICUConverter cnv = new XLIFF2ICUConverter();\r
140         cnv.processArgs(args);\r
141     }\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
151     \r
152     private void processArgs(String[] args) {\r
153         int remainingArgc = 0;\r
154         try{\r
155             remainingArgc = UOption.parseArgs(args, options);\r
156         }catch (Exception e){\r
157             System.err.println("ERROR: "+ e.toString());\r
158             usage();\r
159         }\r
160         if(args.length==0 || options[HELP1].doesOccur || options[HELP2].doesOccur) {\r
161             usage();\r
162         }\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
167                                "the options.");\r
168             usage();\r
169         }\r
170         if(options[SOURCEDIR].doesOccur) {\r
171             sourceDir = options[SOURCEDIR].value;\r
172         }\r
173         \r
174         if(options[DESTDIR].doesOccur) {\r
175             destDir = options[DESTDIR].value;\r
176         }\r
177         \r
178         if(options[TARGETONLY].doesOccur){\r
179             targetOnly = true;\r
180             targetFileName = options[TARGETONLY].value;\r
181         }\r
182         \r
183         if(options[SOURCEONLY].doesOccur){\r
184             sourceOnly = true;\r
185             sourceFileName = options[SOURCEONLY].value;\r
186         }\r
187         \r
188         if(options[MAKE_SOURCE_ROOT].doesOccur){\r
189             makeSourceRoot = true;\r
190         }\r
191         \r
192         if(options[XLIFF_1_0].doesOccur) {\r
193             xliff10 = true;\r
194         }\r
195         \r
196         if(destDir==null){\r
197             destDir = ".";\r
198         }\r
199         \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
202             usage();\r
203         }\r
204         \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
211         }\r
212     }\r
213     \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
218             "Options:\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
229         System.exit(-1);\r
230     }\r
231     \r
232     private String getFullPath(boolean fileType, String fName){\r
233         String str;\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
239             }else{\r
240                 if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){\r
241                     fName =  fName.substring(lastIndex1,lastIndex2) + ".txt";\r
242                 }\r
243             }\r
244             if (destDir != null && fName != null) {\r
245                 str = destDir + File.separator + fName.trim();                   \r
246             } else {\r
247                 str = System.getProperty("user.dir") + File.separator + fName.trim();\r
248             }\r
249         } else {\r
250             if(lastIndex2 == -1){\r
251                 fName = fName.trim() + ".xlf";\r
252             }else{\r
253                 if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){\r
254                     fName = fName.substring(lastIndex1,lastIndex2) + ".xlf";\r
255                 }\r
256             }\r
257             if(sourceDir != null && fName != null) {\r
258                 str = sourceDir + File.separator + fName;\r
259             } else if (lastIndex1 > 0) {\r
260                 str = fName;\r
261             } else {\r
262                 str = System.getProperty("user.dir") + File.separator + fName;\r
263             }\r
264         }\r
265         return str;\r
266     }   \r
267 \r
268     /*\r
269      * Utility method to translate a String filename to URL.  \r
270      *\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
277      *\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
283      */\r
284     private static String filenameToURL(String filename){\r
285         // null begets null - something like the commutative property\r
286         if (null == filename){\r
287             return null;\r
288         }\r
289 \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
298            ){\r
299                return filename;\r
300            }\r
301         \r
302 \r
303         File f = new File(filename);\r
304         String tmp = null;\r
305         try{\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
312         }\r
313 \r
314         // URLs must explicitly use only forward slashes\r
315         if (File.separatorChar == '\\') {\r
316             tmp = tmp.replace('\\', '/');\r
317         }\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
324         }\r
325         else{\r
326             return "file:///" + tmp;\r
327         }\r
328     }\r
329     private boolean isXmlLang (String lang){\r
330 \r
331         int suffix;\r
332         char c;\r
333         \r
334         if (lang.length () < 2){\r
335             return false;\r
336         }\r
337 \r
338         c = lang.charAt(1);\r
339         if (c == '-') {        \r
340             c = lang.charAt(0);\r
341             if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')){\r
342                 return false;\r
343             }\r
344             suffix = 1;\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
348                 return false;\r
349             }\r
350             suffix = 2;\r
351         } else{\r
352             return false;\r
353         }\r
354         while (suffix < lang.length ()) {\r
355             c = lang.charAt(suffix);\r
356             if (c != '-'){\r
357                 break;\r
358             }\r
359             while (++suffix < lang.length ()) {\r
360                 c = lang.charAt(suffix);\r
361                 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){\r
362                     break;\r
363                 }\r
364             }\r
365         }\r
366         return  ((lang.length() == suffix) && (c != '-'));\r
367     }\r
368     \r
369     private void createRB(String xmlfileName) {\r
370        \r
371         String urls = filenameToURL(xmlfileName);\r
372         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();\r
373         dfactory.setNamespaceAware(true);\r
374         Document doc = null;\r
375         \r
376         if (xliff10) {\r
377             dfactory.setValidating(true);\r
378             resources = OLD_RESOURCES;\r
379         } else {\r
380             try {\r
381                 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\r
382                 Schema schema = schemaFactory.newSchema();\r
383                 \r
384                 dfactory.setSchema(schema);\r
385             } catch (SAXException e) {\r
386                 System.err.println("Can't create the schema...");\r
387                 System.exit(-1);\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
393                 System.exit(-1);\r
394             }\r
395             \r
396             resources = NEW_RESOURCES;\r
397         }\r
398         \r
399         ErrorHandler nullHandler = new ErrorHandler() {\r
400             public void warning(SAXParseException e) throws SAXException {\r
401                             \r
402             }\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
407                 System.exit(-1);\r
408             }\r
409             public void fatalError(SAXParseException e) throws SAXException {\r
410                 throw e;\r
411             }\r
412         };\r
413         \r
414         try {\r
415             DocumentBuilder docBuilder = dfactory.newDocumentBuilder();\r
416             docBuilder.setErrorHandler(nullHandler);\r
417             doc = docBuilder.parse(new InputSource(urls));\r
418             \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
422             }\r
423                         \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
428             \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
433             \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
438             \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
443             \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
449             \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
455             }else{\r
456                 if(xmlSrcLang != null){\r
457                     set[0].name = xmlSrcLang.replace('-','_');\r
458                 }else{\r
459                     System.err.println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file.");\r
460                     System.exit(-1);\r
461                 }\r
462             }\r
463             \r
464             // lenient extraction of the target language\r
465             if(targetLang!=null){\r
466                 set[1].name = targetLang.replace('-','_');\r
467             }else{\r
468                 if(xmlTargetLang!=null){\r
469                     set[1].name = xmlTargetLang.replace('-','_');\r
470                 }else{\r
471                     System.err.println("WARNING: Could not figure out the target language of the file. Producing source bundle only.");\r
472                 }\r
473             }\r
474    \r
475             \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
480             }\r
481             \r
482             // get all the group elements\r
483             NodeList list = doc.getElementsByTagName(GROUPS);\r
484             \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
488             \r
489             // write out the bundle\r
490             writeResource(set, xmlfileName);\r
491          }\r
492         catch (Throwable se) {\r
493             System.err.println("ERROR: " + se.toString());\r
494             System.exit(1);\r
495         }        \r
496     }\r
497     \r
498     private void writeResource(Resource[] set, String xmlfileName){\r
499         if(targetOnly==false){\r
500             writeResource(set[0], xmlfileName, sourceFileName);\r
501         }\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
505             }\r
506             if(set[1].name != null){\r
507                 writeResource(set[1], xmlfileName, targetFileName);\r
508             }\r
509         }\r
510     }\r
511     \r
512     private void writeResource(Resource set, String sourceFilename, String targetFilename){\r
513         try {\r
514             String outputFileName = null;\r
515             if(targetFilename != null){\r
516                 outputFileName = destDir+File.separator+targetFilename+".txt";\r
517             }else{\r
518                 outputFileName = destDir+File.separator+set.name+".txt";\r
519             }\r
520             FileOutputStream file = new FileOutputStream(outputFileName);\r
521             BufferedOutputStream writer = new BufferedOutputStream(file);\r
522 \r
523             writeHeader(writer,sourceFilename);\r
524             \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
530             }\r
531             writer.flush();\r
532             writer.close();\r
533         } catch (Exception ie) {\r
534             System.err.println("ERROR :" + ie.toString());\r
535             return;\r
536         }\r
537     }\r
538     \r
539     private String getLanguageName(Document doc, String lang){\r
540         if(doc!=null){\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
545             \r
546             if(orig != null){\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
552                 if(id!=null){\r
553                     String idVal = id.getNodeValue();\r
554                     \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
559                     }\r
560                 }\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
564                     System.exit(-1);\r
565                 }\r
566                 return name;\r
567             }\r
568         }\r
569         return null;\r
570     }\r
571     \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
585             if(lang==null ){\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
588                     System.exit(-1);\r
589                 }else{\r
590                     langName = origName;\r
591                 }\r
592             }else{\r
593                 langName = lang.getNodeValue();\r
594             }\r
595 \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
599             }\r
600             oldLangName = langName;\r
601         }\r
602         return oldLangName;\r
603     }\r
604     \r
605     private class Resource{\r
606         String[] note = new String[20];\r
607         int noteLen = 0;\r
608         String translate;\r
609         String comment;\r
610         String name;\r
611         Resource next;\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
617                 switch (str[i]){\r
618                     case '\u0022':\r
619                         result.append('\\'); //append backslash\r
620                     default:\r
621                         result.append(str[i]);\r
622                 }      \r
623             }\r
624             return result.toString();\r
625         }\r
626         public void write(OutputStream writer, int numIndent, boolean bare){\r
627             while(next!=null){\r
628                 next.write(writer, numIndent+1, false);\r
629             }\r
630         }\r
631         public void writeIndent(OutputStream writer, int numIndent){\r
632             for(int i=0; i< numIndent; i++){\r
633                 write(writer,INDENT);\r
634             }\r
635         }\r
636         public void write(OutputStream writer, String value){\r
637             try {\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
642                 System.exit(1);\r
643             }\r
644         }\r
645         public void writeComments(OutputStream writer, int numIndent){\r
646             boolean translateIsDefault = translate == null || translate.equals("yes");\r
647             \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
652                 \r
653                 // print comment if any\r
654                 if(comment!=null){\r
655                     writeIndent(writer, numIndent);\r
656                     write(writer, COMMENTMIDDLE);\r
657                     write(writer, comment);\r
658                     write(writer, LINESEP);\r
659                 }\r
660                 \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
667                 }\r
668                 \r
669                 // print note elements if any\r
670                 for(int i=0; i<noteLen; i++){\r
671                     if(note[i]!=null){\r
672                         writeIndent(writer, numIndent);\r
673                         write(writer, TAG+NOTE+SPACE+note[i]);\r
674                         write(writer, LINESEP);\r
675                     }\r
676                 }\r
677                 \r
678                 // terminate the comment\r
679                 writeIndent(writer, numIndent);\r
680                 write(writer, COMMENTEND+LINESEP);\r
681             }          \r
682         }\r
683     }\r
684 \r
685     private class ResourceString extends Resource{\r
686         String val;\r
687         public void write(OutputStream writer, int numIndent, boolean bare){\r
688             writeComments(writer, numIndent);\r
689             writeIndent(writer, numIndent);\r
690             if(bare==true){\r
691                 if(name!=null){\r
692                     throw new RuntimeException("Bare option is set to true but the resource has a name!");\r
693                 }\r
694                 \r
695                 write(writer,QUOTE+escapeSyntaxChars(val)+QUOTE); \r
696             }else{\r
697                 write(writer, name+COLON+STRINGS+ OPENBRACE + QUOTE + escapeSyntaxChars(val) + QUOTE+ CLOSEBRACE + LINESEP);\r
698             }\r
699         }\r
700     }\r
701     private class ResourceAlias extends Resource{\r
702         String val;\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
707             if(bare==true){\r
708                 if(name!=null){\r
709                     throw new RuntimeException("Bare option is set to true but the resource has a name!");\r
710                 }\r
711                 write(writer,line); \r
712             }else{\r
713                 write(writer, line+LINESEP);\r
714             }\r
715         }\r
716     }\r
717     private class ResourceInt extends Resource{\r
718         String val;\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
723             if(bare==true){\r
724                 if(name!=null){\r
725                     throw new RuntimeException("Bare option is set to true but the resource has a name!");\r
726                 }\r
727                 write(writer,line); \r
728             }else{\r
729                 write(writer, line+LINESEP);\r
730             }\r
731         }\r
732     }\r
733     private class ResourceBinary extends Resource{\r
734         String internal;\r
735         String external;\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
742             }else{\r
743                 String line = ((name==null) ? EMPTY : name)+COLON+BIN+ OPENBRACE+internal+CLOSEBRACE+ ((bare==true) ?  EMPTY : LINESEP);\r
744                 write(writer,line);\r
745             }\r
746             \r
747         }\r
748     }\r
749     private class ResourceIntVector extends Resource{\r
750         ResourceInt first;\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
755             numIndent++;\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
763             }\r
764             numIndent--;\r
765             writeIndent(writer, numIndent);\r
766             write(writer, CLOSEBRACE+LINESEP);\r
767         }\r
768     }\r
769     private class ResourceTable extends Resource{\r
770         Resource first;\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
775             numIndent++;\r
776             Resource current = first;\r
777             while(current != null){\r
778                 current.write(writer, numIndent, false);\r
779                 current = current.next;\r
780             }\r
781             numIndent--;\r
782             writeIndent(writer, numIndent);\r
783             write(writer, CLOSEBRACE+LINESEP);\r
784         }\r
785     }\r
786     private class ResourceArray extends Resource{\r
787         Resource first;\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
792             numIndent++;\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
798             }\r
799             numIndent--;\r
800             writeIndent(writer, numIndent);\r
801             write(writer, CLOSEBRACE+LINESEP);\r
802         }\r
803     }\r
804     \r
805     private String getAttributeValue(Node sNode, String attribName){\r
806         String value=null;\r
807         Node node = sNode;\r
808 \r
809         NamedNodeMap attributes = node.getAttributes();\r
810         Node attr = attributes.getNamedItem(attribName);\r
811         if(attr!=null){\r
812             value = attr.getNodeValue();\r
813         }\r
814 \r
815         return value;\r
816     }\r
817     \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
825         \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
831                 // get the comment\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
847                 }\r
848             }\r
849             \r
850         }\r
851     }\r
852 \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
865                 // get the comment\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
881                 }\r
882             }\r
883             \r
884         }\r
885     }\r
886     \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
899                 // get the comment\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
915                 }\r
916             }\r
917             \r
918         }\r
919     }\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
925 \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
931                 // get the comment\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
936                     continue;\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
946                     \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
950                         System.exit(1);\r
951                     }\r
952                     \r
953                     currentTarget.internal = currentSource.internal= value;\r
954                     \r
955                 }else if(name.equals(EXTERNALFILE)){\r
956                     currentSource.external = getAttributeValue(transUnit, HREF);\r
957                     currentTarget.external = currentSource.external;\r
958                 }\r
959             }\r
960             \r
961         }\r
962     }\r
963     private void parseTransUnit(Node node, Resource[] set){\r
964 \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
981             set[0] = ints[0];\r
982             set[1] = ints[1];\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
989             set[0] = ints[0];\r
990             set[1] = ints[1];\r
991         }\r
992     }\r
993 \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
997             \r
998             bins[0] = new ResourceBinary();\r
999             bins[1] = new ResourceBinary();\r
1000             \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
1005             \r
1006             currentTarget.name = currentSource.name = resName;\r
1007             currentSource.translate = currentTarget.translate = translate;\r
1008             \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
1012                 \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
1020                         \r
1021                         currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;\r
1022                     }\r
1023                 }\r
1024             }\r
1025             \r
1026             set[0] = bins[0];\r
1027             set[1] = bins[1];\r
1028         }\r
1029     }\r
1030     \r
1031     private void parseArray(Node node, Resource[] set){\r
1032         if(set[0]==null){\r
1033             set[0] = new ResourceArray();\r
1034             set[1] = new ResourceArray();\r
1035         }\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
1041         \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
1056                        isFirst = false;\r
1057                     }else{\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
1063                     }\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
1075                        isFirst = false;\r
1076                     }else{\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
1082                     }\r
1083                 }\r
1084             }\r
1085         }\r
1086     }\r
1087     private void parseIntVector(Node node, Resource[] set){\r
1088         if(set[0]==null){\r
1089             set[0] = new ResourceIntVector();\r
1090             set[1] = new ResourceIntVector();\r
1091         }\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
1114                        isFirst = false;\r
1115                     }else{\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
1121                     }\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
1125                 }\r
1126             }\r
1127         }\r
1128     }\r
1129     private void parseTable(Node node, Resource[] set){\r
1130         if(set[0]==null){\r
1131             set[0] = new ResourceTable();\r
1132             set[1] = new ResourceTable();\r
1133         }\r
1134         Resource currentSource = set[0];\r
1135         Resource currentTarget = set[1];\r
1136         \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
1141         }\r
1142         currentTarget.translate = currentSource.translate = translate;\r
1143         \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
1160                        isFirst = false;\r
1161                     }else{\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
1167                     }\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
1177                        isFirst = false;\r
1178                     }else{\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
1184                     }\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
1197                        isFirst = false;\r
1198                     }else{\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
1204                     }\r
1205                 }\r
1206             }\r
1207         }\r
1208     }\r
1209     \r
1210     private void parseGroup(Node node, Resource[] set){\r
1211 \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
1220         }\r
1221     }\r
1222     \r
1223 \r
1224     private void writeLine(OutputStream writer, String line) {\r
1225         try {\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
1230             System.exit(1);\r
1231         }\r
1232     }\r
1233     \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
1243             \r
1244         writeBOM(writer);\r
1245         MessageFormat format = new MessageFormat(header);\r
1246         Object args[] = {new Date(System.currentTimeMillis()), fileName};\r
1247 \r
1248         writeLine(writer, format.format(args));\r
1249     }\r
1250     \r
1251     private  void writeBOM(OutputStream buffer) {\r
1252         try {\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
1257             System.exit(1);\r
1258         }\r
1259     }\r
1260 }\r