1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ****************************************************************************** 5 * Copyright (C) 2003-2013, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ****************************************************************************** 8 */ 9 10 package com.ibm.icu.dev.tool.localeconverter; 11 12 import java.io.BufferedOutputStream; 13 import java.io.File; 14 import java.io.FileOutputStream; 15 import java.io.IOException; 16 import java.io.OutputStream; 17 import java.text.MessageFormat; 18 import java.util.Date; 19 20 import javax.xml.XMLConstants; 21 import javax.xml.parsers.DocumentBuilder; 22 import javax.xml.parsers.DocumentBuilderFactory; 23 import javax.xml.validation.Schema; 24 import javax.xml.validation.SchemaFactory; 25 26 import org.w3c.dom.Document; 27 import org.w3c.dom.NamedNodeMap; 28 import org.w3c.dom.Node; 29 import org.w3c.dom.NodeList; 30 import org.xml.sax.ErrorHandler; 31 import org.xml.sax.InputSource; 32 import org.xml.sax.SAXException; 33 import org.xml.sax.SAXParseException; 34 35 import com.ibm.icu.dev.tool.UOption; 36 37 public final class XLIFF2ICUConverter { 38 39 /** 40 * These must be kept in sync with getOptions(). 41 */ 42 private static final int HELP1 = 0; 43 private static final int HELP2 = 1; 44 private static final int SOURCEDIR = 2; 45 private static final int DESTDIR = 3; 46 private static final int TARGETONLY = 4; 47 private static final int SOURCEONLY = 5; 48 private static final int MAKE_SOURCE_ROOT = 6; 49 private static final int XLIFF_1_0 = 7; 50 51 private static final UOption[] options = new UOption[] { 52 UOption.HELP_H(), 53 UOption.HELP_QUESTION_MARK(), 54 UOption.SOURCEDIR(), 55 UOption.DESTDIR(), 56 UOption.create("target-only", 't', UOption.OPTIONAL_ARG), 57 UOption.create("source-only", 'c', UOption.OPTIONAL_ARG), 58 UOption.create("make-source-root", 'r', UOption.NO_ARG), 59 UOption.create("xliff-1.0", 'x', UOption.NO_ARG) 60 }; 61 62 private static final int ARRAY_RESOURCE = 0; 63 private static final int ALIAS_RESOURCE = 1; 64 private static final int BINARY_RESOURCE = 2; 65 private static final int INTEGER_RESOURCE = 3; 66 private static final int INTVECTOR_RESOURCE = 4; 67 private static final int TABLE_RESOURCE = 5; 68 69 private static final String NEW_RESOURCES[] = { 70 "x-icu-array", 71 "x-icu-alias", 72 "x-icu-binary", 73 "x-icu-integer", 74 "x-icu-intvector", 75 "x-icu-table" 76 }; 77 78 private static final String OLD_RESOURCES[] = { 79 "array", 80 "alias", 81 "bin", 82 "int", 83 "intvector", 84 "table" 85 }; 86 87 private String resources[]; 88 89 private static final String ROOT = "root"; 90 private static final String RESTYPE = "restype"; 91 private static final String RESNAME = "resname"; 92 //private static final String YES = "yes"; 93 //private static final String NO = "no"; 94 private static final String TRANSLATE = "translate"; 95 //private static final String BODY = "body"; 96 private static final String GROUPS = "group"; 97 private static final String FILES = "file"; 98 private static final String TRANSUNIT = "trans-unit"; 99 private static final String BINUNIT = "bin-unit"; 100 private static final String BINSOURCE = "bin-source"; 101 //private static final String TS = "ts"; 102 //private static final String ORIGINAL = "original"; 103 private static final String SOURCELANGUAGE = "source-language"; 104 private static final String TARGETLANGUAGE = "target-language"; 105 private static final String TARGET = "target"; 106 private static final String SOURCE = "source"; 107 private static final String NOTE = "note"; 108 private static final String XMLLANG = "xml:lang"; 109 private static final String FILE = "file"; 110 private static final String INTVECTOR = "intvector"; 111 private static final String ARRAYS = "array"; 112 private static final String STRINGS = "string"; 113 private static final String BIN = "bin"; 114 private static final String INTS = "int"; 115 private static final String TABLE = "table"; 116 private static final String IMPORT = "import"; 117 private static final String HREF = "href"; 118 private static final String EXTERNALFILE = "external-file"; 119 private static final String INTERNALFILE = "internal-file"; 120 private static final String ALTTRANS = "alt-trans"; 121 private static final String CRC = "crc"; 122 private static final String ALIAS = "alias"; 123 private static final String LINESEP = System.getProperty("line.separator"); 124 private static final String BOM = "\uFEFF"; 125 private static final String CHARSET = "UTF-8"; 126 private static final String OPENBRACE = "{"; 127 private static final String CLOSEBRACE = "}"; 128 private static final String COLON = ":"; 129 private static final String COMMA = ","; 130 private static final String QUOTE = "\""; 131 private static final String COMMENTSTART = "/**"; 132 private static final String COMMENTEND = " */"; 133 private static final String TAG = " * @"; 134 private static final String COMMENTMIDDLE = " * "; 135 private static final String SPACE = " "; 136 private static final String INDENT = " "; 137 private static final String EMPTY = ""; 138 private static final String ID = "id"; 139 main(String[] args)140 public static void main(String[] args) { 141 XLIFF2ICUConverter cnv = new XLIFF2ICUConverter(); 142 cnv.processArgs(args); 143 } 144 private String sourceDir = null; 145 //private String fileName = null; 146 private String destDir = null; 147 private boolean targetOnly = false; 148 private String targetFileName = null; 149 private boolean makeSourceRoot = false; 150 private String sourceFileName = null; 151 private boolean sourceOnly = false; 152 private boolean xliff10 = false; 153 processArgs(String[] args)154 private void processArgs(String[] args) { 155 int remainingArgc = 0; 156 try{ 157 remainingArgc = UOption.parseArgs(args, options); 158 }catch (Exception e){ 159 System.err.println("ERROR: "+ e.toString()); 160 usage(); 161 } 162 if(args.length==0 || options[HELP1].doesOccur || options[HELP2].doesOccur) { 163 usage(); 164 } 165 if(remainingArgc==0){ 166 System.err.println("ERROR: Either the file name to be processed is not "+ 167 "specified or the it is specified after the -t/-c \n"+ 168 "option which has an optional argument. Try rearranging "+ 169 "the options."); 170 usage(); 171 } 172 if(options[SOURCEDIR].doesOccur) { 173 sourceDir = options[SOURCEDIR].value; 174 } 175 176 if(options[DESTDIR].doesOccur) { 177 destDir = options[DESTDIR].value; 178 } 179 180 if(options[TARGETONLY].doesOccur){ 181 targetOnly = true; 182 targetFileName = options[TARGETONLY].value; 183 } 184 185 if(options[SOURCEONLY].doesOccur){ 186 sourceOnly = true; 187 sourceFileName = options[SOURCEONLY].value; 188 } 189 190 if(options[MAKE_SOURCE_ROOT].doesOccur){ 191 makeSourceRoot = true; 192 } 193 194 if(options[XLIFF_1_0].doesOccur) { 195 xliff10 = true; 196 } 197 198 if(destDir==null){ 199 destDir = "."; 200 } 201 202 if(sourceOnly == true && targetOnly == true){ 203 System.err.println("--source-only and --target-only are specified. Please check the arguments and try again."); 204 usage(); 205 } 206 207 for (int i = 0; i < remainingArgc; i++) { 208 //int lastIndex = args[i].lastIndexOf(File.separator, args[i].length()) + 1; /* add 1 to skip past the separator */ 209 //fileName = args[i].substring(lastIndex, args[i].length()); 210 String xmlfileName = getFullPath(false,args[i]); 211 System.out.println("Processing file: "+xmlfileName); 212 createRB(xmlfileName); 213 } 214 } 215 usage()216 private void usage() { 217 System.out.println("\nUsage: XLIFF2ICUConverter [OPTIONS] [FILES]\n\n"+ 218 "This program is used to convert XLIFF files to ICU ResourceBundle TXT files.\n"+ 219 "Please refer to the following options. Options are not case sensitive.\n"+ 220 "Options:\n"+ 221 "-s or --sourcedir source directory for files followed by path, default is current directory.\n" + 222 "-d or --destdir destination directory, followed by the path, default is current directory.\n" + 223 "-h or -? or --help this usage text.\n"+ 224 "-t or --target-only only generate the target language txt file, followed by optional output file name.\n" + 225 " Cannot be used in conjunction with --source-only.\n"+ 226 "-c or --source-only only generate the source language bundle followed by optional output file name.\n"+ 227 " Cannot be used in conjunction with --target-only.\n"+ 228 "-r or --make-source-root produce root bundle from source elements.\n" + 229 "-x or --xliff-1.0 source file is XLIFF 1.0" + 230 "example: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter -t <optional argument> -s xxx -d yyy myResources.xlf"); 231 System.exit(-1); 232 } 233 getFullPath(boolean fileType, String fName)234 private String getFullPath(boolean fileType, String fName){ 235 String str; 236 int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1; /*add 1 to skip past the separator*/ 237 int lastIndex2 = fName.lastIndexOf('.', fName.length()); 238 if (fileType == true) { 239 if(lastIndex2 == -1){ 240 fName = fName.trim() + ".txt"; 241 }else{ 242 if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){ 243 fName = fName.substring(lastIndex1,lastIndex2) + ".txt"; 244 } 245 } 246 if (destDir != null && fName != null) { 247 str = destDir + File.separator + fName.trim(); 248 } else { 249 str = System.getProperty("user.dir") + File.separator + fName.trim(); 250 } 251 } else { 252 if(lastIndex2 == -1){ 253 fName = fName.trim() + ".xlf"; 254 }else{ 255 if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){ 256 fName = fName.substring(lastIndex1,lastIndex2) + ".xlf"; 257 } 258 } 259 if(sourceDir != null && fName != null) { 260 str = sourceDir + File.separator + fName; 261 } else if (lastIndex1 > 0) { 262 str = fName; 263 } else { 264 str = System.getProperty("user.dir") + File.separator + fName; 265 } 266 } 267 return str; 268 } 269 270 /* 271 * Utility method to translate a String filename to URL. 272 * 273 * Note: This method is not necessarily proven to get the 274 * correct URL for every possible kind of filename; it should 275 * be improved. It handles the most common cases that we've 276 * encountered when running Conformance tests on Xalan. 277 * Also note, this method does not handle other non-file: 278 * flavors of URLs at all. 279 * 280 * If the name is null, return null. 281 * If the name starts with a common URI scheme (namely the ones 282 * found in the examples of RFC2396), then simply return the 283 * name as-is (the assumption is that it's already a URL) 284 * Otherwise we attempt (cheaply) to convert to a file:/// URL. 285 */ filenameToURL(String filename)286 private static String filenameToURL(String filename){ 287 // null begets null - something like the commutative property 288 if (null == filename){ 289 return null; 290 } 291 292 // Don't translate a string that already looks like a URL 293 if (filename.startsWith("file:") 294 || filename.startsWith("http:") 295 || filename.startsWith("ftp:") 296 || filename.startsWith("gopher:") 297 || filename.startsWith("mailto:") 298 || filename.startsWith("news:") 299 || filename.startsWith("telnet:") 300 ){ 301 return filename; 302 } 303 304 305 File f = new File(filename); 306 String tmp = null; 307 try{ 308 // This normally gives a better path 309 tmp = f.getCanonicalPath(); 310 }catch (IOException ioe){ 311 // But this can be used as a backup, for cases 312 // where the file does not exist, etc. 313 tmp = f.getAbsolutePath(); 314 } 315 316 // URLs must explicitly use only forward slashes 317 if (File.separatorChar == '\\') { 318 tmp = tmp.replace('\\', '/'); 319 } 320 // Note the presumption that it's a file reference 321 // Ensure we have the correct number of slashes at the 322 // start: we always want 3 /// if it's absolute 323 // (which we should have forced above) 324 if (tmp.startsWith("/")){ 325 return "file://" + tmp; 326 } 327 else{ 328 return "file:///" + tmp; 329 } 330 } isXmlLang(String lang)331 private boolean isXmlLang (String lang){ 332 333 int suffix; 334 char c; 335 336 if (lang.length () < 2){ 337 return false; 338 } 339 340 c = lang.charAt(1); 341 if (c == '-') { 342 c = lang.charAt(0); 343 if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')){ 344 return false; 345 } 346 suffix = 1; 347 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 348 c = lang.charAt(0); 349 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))){ 350 return false; 351 } 352 suffix = 2; 353 } else{ 354 return false; 355 } 356 while (suffix < lang.length ()) { 357 c = lang.charAt(suffix); 358 if (c != '-'){ 359 break; 360 } 361 while (++suffix < lang.length ()) { 362 c = lang.charAt(suffix); 363 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))){ 364 break; 365 } 366 } 367 } 368 return ((lang.length() == suffix) && (c != '-')); 369 } 370 createRB(String xmlfileName)371 private void createRB(String xmlfileName) { 372 373 String urls = filenameToURL(xmlfileName); 374 DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); 375 dfactory.setNamespaceAware(true); 376 Document doc = null; 377 378 if (xliff10) { 379 dfactory.setValidating(true); 380 resources = OLD_RESOURCES; 381 } else { 382 try { 383 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 384 Schema schema = schemaFactory.newSchema(); 385 386 dfactory.setSchema(schema); 387 } catch (SAXException e) { 388 System.err.println("Can't create the schema..."); 389 System.exit(-1); 390 } catch (UnsupportedOperationException e) { 391 System.err.println("ERROR:\tOne of the schema operations is not supported with this JVM."); 392 System.err.println("\tIf you are using GNU Java, you should try using the latest Sun JVM."); 393 System.err.println("\n*Here is the stack trace:"); 394 e.printStackTrace(); 395 System.exit(-1); 396 } 397 398 resources = NEW_RESOURCES; 399 } 400 401 ErrorHandler nullHandler = new ErrorHandler() { 402 public void warning(SAXParseException e) throws SAXException { 403 404 } 405 public void error(SAXParseException e) throws SAXException { 406 System.err.println("The XLIFF document is invalid, please check it first: "); 407 System.err.println("Line "+e.getLineNumber()+", Column "+e.getColumnNumber()); 408 System.err.println("Error: " + e.getMessage()); 409 System.exit(-1); 410 } 411 public void fatalError(SAXParseException e) throws SAXException { 412 throw e; 413 } 414 }; 415 416 try { 417 DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); 418 docBuilder.setErrorHandler(nullHandler); 419 doc = docBuilder.parse(new InputSource(urls)); 420 421 NodeList nlist = doc.getElementsByTagName(FILES); 422 if(nlist.getLength()>1){ 423 throw new RuntimeException("Multiple <file> elements in the XLIFF file not supported."); 424 } 425 426 // get the value of source-language attribute 427 String sourceLang = getLanguageName(doc, SOURCELANGUAGE); 428 // get the value of target-language attribute 429 String targetLang = getLanguageName(doc, TARGETLANGUAGE); 430 431 // get the list of <source> elements 432 NodeList sourceList = doc.getElementsByTagName(SOURCE); 433 // get the list of target elements 434 NodeList targetList = doc.getElementsByTagName(TARGET); 435 436 // check if the xliff file has source elements in multiple languages 437 // the source-language value should be the same as xml:lang values 438 // of all the source elements. 439 String xmlSrcLang = checkLangAttribute(sourceList, sourceLang); 440 441 // check if the xliff file has target elements in multiple languages 442 // the target-language value should be the same as xml:lang values 443 // of all the target elements. 444 String xmlTargetLang = checkLangAttribute(targetList, targetLang); 445 446 // Create the Resource linked list which will hold the 447 // source and target bundles after parsing 448 Resource[] set = new Resource[2]; 449 set[0] = new ResourceTable(); 450 set[1] = new ResourceTable(); 451 452 // lenient extraction of source language 453 if(makeSourceRoot == true){ 454 set[0].name = ROOT; 455 }else if(sourceLang!=null){ 456 set[0].name = sourceLang.replace('-','_'); 457 }else{ 458 if(xmlSrcLang != null){ 459 set[0].name = xmlSrcLang.replace('-','_'); 460 }else{ 461 System.err.println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file."); 462 System.exit(-1); 463 } 464 } 465 466 // lenient extraction of the target language 467 if(targetLang!=null){ 468 set[1].name = targetLang.replace('-','_'); 469 }else{ 470 if(xmlTargetLang!=null){ 471 set[1].name = xmlTargetLang.replace('-','_'); 472 }else{ 473 System.err.println("WARNING: Could not figure out the target language of the file. Producing source bundle only."); 474 } 475 } 476 477 478 // check if any <alt-trans> elements are present 479 NodeList altTrans = doc.getElementsByTagName(ALTTRANS); 480 if(altTrans.getLength()>0){ 481 System.err.println("WARNING: <alt-trans> elements in found. Ignoring all <alt-trans> elements."); 482 } 483 484 // get all the group elements 485 NodeList list = doc.getElementsByTagName(GROUPS); 486 487 // process the first group element. The first group element is 488 // the base table that must be parsed recursively 489 parseTable(list.item(0), set); 490 491 // write out the bundle 492 writeResource(set, xmlfileName); 493 } 494 catch (Throwable se) { 495 System.err.println("ERROR: " + se.toString()); 496 System.exit(1); 497 } 498 } 499 writeResource(Resource[] set, String xmlfileName)500 private void writeResource(Resource[] set, String xmlfileName){ 501 if(targetOnly==false){ 502 writeResource(set[0], xmlfileName, sourceFileName); 503 } 504 if(sourceOnly == false){ 505 if(targetOnly==true && set[1].name == null){ 506 throw new RuntimeException("The "+ xmlfileName +" does not contain translation\n"); 507 } 508 if(set[1].name != null){ 509 writeResource(set[1], xmlfileName, targetFileName); 510 } 511 } 512 } 513 writeResource(Resource set, String sourceFilename, String targetFilename)514 private void writeResource(Resource set, String sourceFilename, String targetFilename){ 515 try { 516 String outputFileName = null; 517 if(targetFilename != null){ 518 outputFileName = destDir+File.separator+targetFilename+".txt"; 519 }else{ 520 outputFileName = destDir+File.separator+set.name+".txt"; 521 } 522 FileOutputStream file = new FileOutputStream(outputFileName); 523 BufferedOutputStream writer = new BufferedOutputStream(file); 524 525 writeHeader(writer,sourceFilename); 526 527 //Now start writing the resource; 528 Resource current = set; 529 while(current!=null){ 530 current.write(writer, 0, false); 531 current = current.next; 532 } 533 writer.flush(); 534 writer.close(); 535 } catch (Exception ie) { 536 System.err.println("ERROR :" + ie.toString()); 537 return; 538 } 539 } 540 getLanguageName(Document doc, String lang)541 private String getLanguageName(Document doc, String lang){ 542 if(doc!=null){ 543 NodeList list = doc.getElementsByTagName(FILE); 544 Node node = list.item(0); 545 NamedNodeMap attr = node.getAttributes(); 546 Node orig = attr.getNamedItem(lang); 547 548 if(orig != null){ 549 String name = orig.getNodeValue(); 550 NodeList groupList = doc.getElementsByTagName(GROUPS); 551 Node group = groupList.item(0); 552 NamedNodeMap groupAtt = group.getAttributes(); 553 Node id = groupAtt.getNamedItem(ID); 554 if(id!=null){ 555 String idVal = id.getNodeValue(); 556 557 if(!name.equals(idVal)){ 558 System.out.println("WARNING: The id value != language name. " + 559 "Please compare the output with the orignal " + 560 "ICU ResourceBundle before proceeding."); 561 } 562 } 563 if(!isXmlLang(name)){ 564 System.err.println("The attribute "+ lang + "=\""+ name + 565 "\" of <file> element is invalid."); 566 System.exit(-1); 567 } 568 return name; 569 } 570 } 571 return null; 572 } 573 574 // check if the xliff file is translated into multiple languages 575 // The XLIFF specification allows for single <target> element 576 // as the child of <trans-unit> but the attributes of the 577 // <target> element may different across <trans-unit> elements 578 // check for it. Similar is the case with <source> elements checkLangAttribute(NodeList list, String origName)579 private String checkLangAttribute(NodeList list, String origName){ 580 String oldLangName=origName; 581 for(int i = 0 ;i<list.getLength(); i++){ 582 Node node = list.item(i); 583 NamedNodeMap attr = node.getAttributes(); 584 Node lang = attr.getNamedItem(XMLLANG); 585 String langName = null; 586 // the target element should always contain xml:lang attribute 587 if(lang==null ){ 588 if(origName==null){ 589 System.err.println("Encountered <target> element without xml:lang attribute. Please fix the below element in the XLIFF file.\n"+ node.toString()); 590 System.exit(-1); 591 }else{ 592 langName = origName; 593 } 594 }else{ 595 langName = lang.getNodeValue(); 596 } 597 598 if(oldLangName!=null && langName!=null && !langName.equals(oldLangName)){ 599 throw new RuntimeException("The <trans-unit> elements must be bilingual, multilingual tranlations not supported. xml:lang = " + oldLangName + 600 " and xml:lang = " + langName); 601 } 602 oldLangName = langName; 603 } 604 return oldLangName; 605 } 606 607 private class Resource{ 608 String[] note = new String[20]; 609 int noteLen = 0; 610 String translate; 611 String comment; 612 String name; 613 Resource next; escapeSyntaxChars(String val)614 public String escapeSyntaxChars(String val){ 615 // escape the embedded quotes 616 char[] str = val.toCharArray(); 617 StringBuffer result = new StringBuffer(); 618 for(int i=0; i<str.length; i++){ 619 switch (str[i]){ 620 case '\u0022': 621 result.append('\\'); //append backslash 622 default: 623 result.append(str[i]); 624 } 625 } 626 return result.toString(); 627 } write(OutputStream writer, int numIndent, boolean bare)628 public void write(OutputStream writer, int numIndent, boolean bare){ 629 while(next!=null){ 630 next.write(writer, numIndent+1, false); 631 } 632 } writeIndent(OutputStream writer, int numIndent)633 public void writeIndent(OutputStream writer, int numIndent){ 634 for(int i=0; i< numIndent; i++){ 635 write(writer,INDENT); 636 } 637 } write(OutputStream writer, String value)638 public void write(OutputStream writer, String value){ 639 try { 640 byte[] bytes = value.getBytes(CHARSET); 641 writer.write(bytes, 0, bytes.length); 642 } catch(Exception e) { 643 System.err.println(e); 644 System.exit(1); 645 } 646 } writeComments(OutputStream writer, int numIndent)647 public void writeComments(OutputStream writer, int numIndent){ 648 boolean translateIsDefault = translate == null || translate.equals("yes"); 649 650 if(comment!=null || ! translateIsDefault || noteLen > 0){ 651 // print the start of the comment 652 writeIndent(writer, numIndent); 653 write(writer, COMMENTSTART+LINESEP); 654 655 // print comment if any 656 if(comment!=null){ 657 writeIndent(writer, numIndent); 658 write(writer, COMMENTMIDDLE); 659 write(writer, comment); 660 write(writer, LINESEP); 661 } 662 663 // print the translate attribute if any 664 if(! translateIsDefault){ 665 writeIndent(writer, numIndent); 666 write(writer, TAG+TRANSLATE+SPACE); 667 write(writer, translate); 668 write(writer, LINESEP); 669 } 670 671 // print note elements if any 672 for(int i=0; i<noteLen; i++){ 673 if(note[i]!=null){ 674 writeIndent(writer, numIndent); 675 write(writer, TAG+NOTE+SPACE+note[i]); 676 write(writer, LINESEP); 677 } 678 } 679 680 // terminate the comment 681 writeIndent(writer, numIndent); 682 write(writer, COMMENTEND+LINESEP); 683 } 684 } 685 } 686 687 private class ResourceString extends Resource{ 688 String val; write(OutputStream writer, int numIndent, boolean bare)689 public void write(OutputStream writer, int numIndent, boolean bare){ 690 writeComments(writer, numIndent); 691 writeIndent(writer, numIndent); 692 if(bare==true){ 693 if(name!=null){ 694 throw new RuntimeException("Bare option is set to true but the resource has a name!"); 695 } 696 697 write(writer,QUOTE+escapeSyntaxChars(val)+QUOTE); 698 }else{ 699 write(writer, name+COLON+STRINGS+ OPENBRACE + QUOTE + escapeSyntaxChars(val) + QUOTE+ CLOSEBRACE + LINESEP); 700 } 701 } 702 } 703 private class ResourceAlias extends Resource{ 704 String val; write(OutputStream writer, int numIndent, boolean bare)705 public void write(OutputStream writer, int numIndent, boolean bare){ 706 writeComments(writer, numIndent); 707 writeIndent(writer, numIndent); 708 String line = ((name==null)? EMPTY: name)+COLON+ALIAS+ OPENBRACE+QUOTE+escapeSyntaxChars(val)+QUOTE+CLOSEBRACE; 709 if(bare==true){ 710 if(name!=null){ 711 throw new RuntimeException("Bare option is set to true but the resource has a name!"); 712 } 713 write(writer,line); 714 }else{ 715 write(writer, line+LINESEP); 716 } 717 } 718 } 719 private class ResourceInt extends Resource{ 720 String val; write(OutputStream writer, int numIndent, boolean bare)721 public void write(OutputStream writer, int numIndent, boolean bare){ 722 writeComments(writer, numIndent); 723 writeIndent(writer, numIndent); 724 String line = ((name==null)? EMPTY: name)+COLON+INTS+ OPENBRACE + val +CLOSEBRACE; 725 if(bare==true){ 726 if(name!=null){ 727 throw new RuntimeException("Bare option is set to true but the resource has a name!"); 728 } 729 write(writer,line); 730 }else{ 731 write(writer, line+LINESEP); 732 } 733 } 734 } 735 private class ResourceBinary extends Resource{ 736 String internal; 737 String external; write(OutputStream writer, int numIndent, boolean bare)738 public void write(OutputStream writer, int numIndent, boolean bare){ 739 writeComments(writer, numIndent); 740 writeIndent(writer, numIndent); 741 if(internal==null){ 742 String line = ((name==null) ? EMPTY : name)+COLON+IMPORT+ OPENBRACE+QUOTE+external+QUOTE+CLOSEBRACE + ((bare==true) ? EMPTY : LINESEP); 743 write(writer, line); 744 }else{ 745 String line = ((name==null) ? EMPTY : name)+COLON+BIN+ OPENBRACE+internal+CLOSEBRACE+ ((bare==true) ? EMPTY : LINESEP); 746 write(writer,line); 747 } 748 749 } 750 } 751 private class ResourceIntVector extends Resource{ 752 ResourceInt first; write(OutputStream writer, int numIndent, boolean bare)753 public void write(OutputStream writer, int numIndent, boolean bare){ 754 writeComments(writer, numIndent); 755 writeIndent(writer, numIndent); 756 write(writer, name+COLON+INTVECTOR+OPENBRACE+LINESEP); 757 numIndent++; 758 ResourceInt current = (ResourceInt) first; 759 while(current != null){ 760 //current.write(writer, numIndent, true); 761 writeIndent(writer, numIndent); 762 write(writer, current.val); 763 write(writer, COMMA+LINESEP); 764 current = (ResourceInt) current.next; 765 } 766 numIndent--; 767 writeIndent(writer, numIndent); 768 write(writer, CLOSEBRACE+LINESEP); 769 } 770 } 771 private class ResourceTable extends Resource{ 772 Resource first; write(OutputStream writer, int numIndent, boolean bare)773 public void write(OutputStream writer, int numIndent, boolean bare){ 774 writeComments(writer, numIndent); 775 writeIndent(writer, numIndent); 776 write(writer, name+COLON+TABLE+OPENBRACE+LINESEP); 777 numIndent++; 778 Resource current = first; 779 while(current != null){ 780 current.write(writer, numIndent, false); 781 current = current.next; 782 } 783 numIndent--; 784 writeIndent(writer, numIndent); 785 write(writer, CLOSEBRACE+LINESEP); 786 } 787 } 788 private class ResourceArray extends Resource{ 789 Resource first; write(OutputStream writer, int numIndent, boolean bare)790 public void write(OutputStream writer, int numIndent, boolean bare){ 791 writeComments(writer, numIndent); 792 writeIndent(writer, numIndent); 793 write(writer, name+COLON+ARRAYS+OPENBRACE+LINESEP); 794 numIndent++; 795 Resource current = first; 796 while(current != null){ 797 current.write(writer, numIndent, true); 798 write(writer, COMMA+LINESEP); 799 current = current.next; 800 } 801 numIndent--; 802 writeIndent(writer, numIndent); 803 write(writer, CLOSEBRACE+LINESEP); 804 } 805 } 806 getAttributeValue(Node sNode, String attribName)807 private String getAttributeValue(Node sNode, String attribName){ 808 String value=null; 809 Node node = sNode; 810 811 NamedNodeMap attributes = node.getAttributes(); 812 Node attr = attributes.getNamedItem(attribName); 813 if(attr!=null){ 814 value = attr.getNodeValue(); 815 } 816 817 return value; 818 } 819 parseResourceString(Node node, ResourceString[] set)820 private void parseResourceString(Node node, ResourceString[] set){ 821 ResourceString currentSource; 822 ResourceString currentTarget; 823 currentSource = set[0]; 824 currentTarget = set[1]; 825 String resName = getAttributeValue(node, RESNAME); 826 String translate = getAttributeValue(node, TRANSLATE); 827 828 // loop to pickup <source>, <note> and <target> elements 829 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ 830 short type = transUnit.getNodeType(); 831 String name = transUnit.getNodeName(); 832 if(type == Node.COMMENT_NODE){ 833 // get the comment 834 currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); 835 }else if( type == Node.ELEMENT_NODE){ 836 if(name.equals(SOURCE)){ 837 // save the source and target values 838 currentSource.name = currentTarget.name = resName; 839 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); 840 currentSource.translate = currentTarget.translate = translate; 841 }else if(name.equals(NOTE)){ 842 // save the note values 843 currentSource.note[currentSource.noteLen++] = 844 currentTarget.note[currentTarget.noteLen++] = 845 transUnit.getFirstChild().getNodeValue(); 846 }else if(name.equals(TARGET)){ 847 // if there is a target element replace it 848 currentTarget.val = transUnit.getFirstChild().getNodeValue(); 849 } 850 } 851 852 } 853 } 854 parseResourceInt(Node node, ResourceInt[] set)855 private void parseResourceInt(Node node, ResourceInt[] set){ 856 ResourceInt currentSource; 857 ResourceInt currentTarget; 858 currentSource = set[0]; 859 currentTarget = set[1]; 860 String resName = getAttributeValue(node, RESNAME); 861 String translate = getAttributeValue(node, TRANSLATE); 862 // loop to pickup <source>, <note> and <target> elements 863 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ 864 short type = transUnit.getNodeType(); 865 String name = transUnit.getNodeName(); 866 if(type == Node.COMMENT_NODE){ 867 // get the comment 868 currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); 869 }else if( type == Node.ELEMENT_NODE){ 870 if(name.equals(SOURCE)){ 871 // save the source and target values 872 currentSource.name = currentTarget.name = resName; 873 currentSource.translate = currentTarget.translate = translate; 874 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); 875 }else if(name.equals(NOTE)){ 876 // save the note values 877 currentSource.note[currentSource.noteLen++] = 878 currentTarget.note[currentTarget.noteLen++] = 879 transUnit.getFirstChild().getNodeValue(); 880 }else if(name.equals(TARGET)){ 881 // if there is a target element replace it 882 currentTarget.val = transUnit.getFirstChild().getNodeValue(); 883 } 884 } 885 886 } 887 } 888 parseResourceAlias(Node node, ResourceAlias[] set)889 private void parseResourceAlias(Node node, ResourceAlias[] set){ 890 ResourceAlias currentSource; 891 ResourceAlias currentTarget; 892 currentSource = set[0]; 893 currentTarget = set[1]; 894 String resName = getAttributeValue(node, RESNAME); 895 String translate = getAttributeValue(node, TRANSLATE); 896 // loop to pickup <source>, <note> and <target> elements 897 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ 898 short type = transUnit.getNodeType(); 899 String name = transUnit.getNodeName(); 900 if(type == Node.COMMENT_NODE){ 901 // get the comment 902 currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); 903 }else if( type == Node.ELEMENT_NODE){ 904 if(name.equals(SOURCE)){ 905 // save the source and target values 906 currentSource.name = currentTarget.name = resName; 907 currentSource.translate = currentTarget.translate = translate; 908 currentSource.val = currentTarget.val = transUnit.getFirstChild().getNodeValue(); 909 }else if(name.equals(NOTE)){ 910 // save the note values 911 currentSource.note[currentSource.noteLen++] = 912 currentTarget.note[currentTarget.noteLen++] = 913 transUnit.getFirstChild().getNodeValue(); 914 }else if(name.equals(TARGET)){ 915 // if there is a target element replace it 916 currentTarget.val = transUnit.getFirstChild().getNodeValue(); 917 } 918 } 919 920 } 921 } parseResourceBinary(Node node, ResourceBinary[] set)922 private void parseResourceBinary(Node node, ResourceBinary[] set){ 923 ResourceBinary currentSource; 924 ResourceBinary currentTarget; 925 currentSource = set[0]; 926 currentTarget = set[1]; 927 928 // loop to pickup <source>, <note> and <target> elements 929 for(Node transUnit = node.getFirstChild(); transUnit!=null; transUnit = transUnit.getNextSibling()){ 930 short type = transUnit.getNodeType(); 931 String name = transUnit.getNodeName(); 932 if(type == Node.COMMENT_NODE){ 933 // get the comment 934 currentSource.comment = currentTarget.comment = transUnit.getNodeValue(); 935 }else if( type == Node.ELEMENT_NODE){ 936 if(name.equals(BINSOURCE)){ 937 // loop to pickup internal-file/extenal-file element 938 continue; 939 }else if(name.equals(NOTE)){ 940 // save the note values 941 currentSource.note[currentSource.noteLen++] = 942 currentTarget.note[currentTarget.noteLen++] = 943 transUnit.getFirstChild().getNodeValue(); 944 }else if(name.equals(INTERNALFILE)){ 945 // if there is a target element replace it 946 String crc = getAttributeValue(transUnit, CRC); 947 String value = transUnit.getFirstChild().getNodeValue(); 948 949 //verify that the binary value conforms to the CRC 950 if(Integer.parseInt(crc, 10) != CalculateCRC32.computeCRC32(value)) { 951 System.err.println("ERROR: CRC value incorrect! Please check."); 952 System.exit(1); 953 } 954 955 currentTarget.internal = currentSource.internal= value; 956 957 }else if(name.equals(EXTERNALFILE)){ 958 currentSource.external = getAttributeValue(transUnit, HREF); 959 currentTarget.external = currentSource.external; 960 } 961 } 962 963 } 964 } parseTransUnit(Node node, Resource[] set)965 private void parseTransUnit(Node node, Resource[] set){ 966 967 String attrType = getAttributeValue(node, RESTYPE); 968 String translate = getAttributeValue(node, TRANSLATE); 969 if(attrType==null || attrType.equals(STRINGS)){ 970 ResourceString[] strings = new ResourceString[2]; 971 strings[0] = new ResourceString(); 972 strings[1] = new ResourceString(); 973 parseResourceString(node, strings); 974 strings[0].translate = strings[1].translate = translate; 975 set[0] = strings[0]; 976 set[1] = strings[1]; 977 }else if(attrType.equals(resources[INTEGER_RESOURCE])){ 978 ResourceInt[] ints = new ResourceInt[2]; 979 ints[0] = new ResourceInt(); 980 ints[1] = new ResourceInt(); 981 parseResourceInt(node, ints); 982 ints[0].translate = ints[1].translate = translate; 983 set[0] = ints[0]; 984 set[1] = ints[1]; 985 }else if(attrType.equals(resources[ALIAS_RESOURCE])){ 986 ResourceAlias[] ints = new ResourceAlias[2]; 987 ints[0] = new ResourceAlias(); 988 ints[1] = new ResourceAlias(); 989 parseResourceAlias(node, ints); 990 ints[0].translate = ints[1].translate = translate; 991 set[0] = ints[0]; 992 set[1] = ints[1]; 993 } 994 } 995 parseBinUnit(Node node, Resource[] set)996 private void parseBinUnit(Node node, Resource[] set){ 997 if (getAttributeValue(node, RESTYPE).equals(resources[BINARY_RESOURCE])) { 998 ResourceBinary[] bins = new ResourceBinary[2]; 999 1000 bins[0] = new ResourceBinary(); 1001 bins[1] = new ResourceBinary(); 1002 1003 Resource currentSource = bins[0]; 1004 Resource currentTarget = bins[1]; 1005 String resName = getAttributeValue(node, RESNAME); 1006 String translate = getAttributeValue(node, TRANSLATE); 1007 1008 currentTarget.name = currentSource.name = resName; 1009 currentSource.translate = currentTarget.translate = translate; 1010 1011 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ 1012 short type = child.getNodeType(); 1013 String name = child.getNodeName(); 1014 1015 if(type == Node.COMMENT_NODE){ 1016 currentSource.comment = currentTarget.comment = child.getNodeValue(); 1017 }else if(type == Node.ELEMENT_NODE){ 1018 if(name.equals(BINSOURCE)){ 1019 parseResourceBinary(child, bins); 1020 }else if(name.equals(NOTE)){ 1021 String note = child.getFirstChild().getNodeValue(); 1022 1023 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; 1024 } 1025 } 1026 } 1027 1028 set[0] = bins[0]; 1029 set[1] = bins[1]; 1030 } 1031 } 1032 parseArray(Node node, Resource[] set)1033 private void parseArray(Node node, Resource[] set){ 1034 if(set[0]==null){ 1035 set[0] = new ResourceArray(); 1036 set[1] = new ResourceArray(); 1037 } 1038 Resource currentSource = set[0]; 1039 Resource currentTarget = set[1]; 1040 String resName = getAttributeValue(node, RESNAME); 1041 currentSource.name = currentTarget.name = resName; 1042 boolean isFirst = true; 1043 1044 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ 1045 short type = child.getNodeType(); 1046 String name = child.getNodeName(); 1047 if(type == Node.COMMENT_NODE){ 1048 currentSource.comment = currentTarget.comment = child.getNodeValue(); 1049 }else if(type == Node.ELEMENT_NODE){ 1050 if(name.equals(TRANSUNIT)){ 1051 Resource[] next = new Resource[2]; 1052 parseTransUnit(child, next); 1053 if(isFirst==true){ 1054 ((ResourceArray) currentSource).first = next[0]; 1055 ((ResourceArray) currentTarget).first = next[1]; 1056 currentSource = ((ResourceArray) currentSource).first; 1057 currentTarget = ((ResourceArray) currentTarget).first; 1058 isFirst = false; 1059 }else{ 1060 currentSource.next = next[0]; 1061 currentTarget.next = next[1]; 1062 // set the next pointers 1063 currentSource = currentSource.next; 1064 currentTarget = currentTarget.next; 1065 } 1066 }else if(name.equals(NOTE)){ 1067 String note = child.getFirstChild().getNodeValue(); 1068 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; 1069 }else if(name.equals(BINUNIT)){ 1070 Resource[] next = new Resource[2]; 1071 parseBinUnit(child, next); 1072 if(isFirst==true){ 1073 ((ResourceArray) currentSource).first = next[0]; 1074 ((ResourceArray) currentTarget).first = next[1]; 1075 currentSource = ((ResourceArray) currentSource).first.next; 1076 currentTarget = ((ResourceArray) currentTarget).first.next; 1077 isFirst = false; 1078 }else{ 1079 currentSource.next = next[0]; 1080 currentTarget.next = next[1]; 1081 // set the next pointers 1082 currentSource = currentSource.next; 1083 currentTarget = currentTarget.next; 1084 } 1085 } 1086 } 1087 } 1088 } parseIntVector(Node node, Resource[] set)1089 private void parseIntVector(Node node, Resource[] set){ 1090 if(set[0]==null){ 1091 set[0] = new ResourceIntVector(); 1092 set[1] = new ResourceIntVector(); 1093 } 1094 Resource currentSource = set[0]; 1095 Resource currentTarget = set[1]; 1096 String resName = getAttributeValue(node, RESNAME); 1097 String translate = getAttributeValue(node,TRANSLATE); 1098 currentSource.name = currentTarget.name = resName; 1099 currentSource.translate = translate; 1100 boolean isFirst = true; 1101 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ 1102 short type = child.getNodeType(); 1103 String name = child.getNodeName(); 1104 if(type == Node.COMMENT_NODE){ 1105 currentSource.comment = currentTarget.comment = child.getNodeValue(); 1106 }else if(type == Node.ELEMENT_NODE){ 1107 if(name.equals(TRANSUNIT)){ 1108 Resource[] next = new Resource[2]; 1109 parseTransUnit(child, next); 1110 if(isFirst==true){ 1111 // the down cast should be safe .. if not something is terribly wrong!! 1112 ((ResourceIntVector) currentSource).first = (ResourceInt)next[0]; 1113 ((ResourceIntVector) currentTarget).first = (ResourceInt) next[1]; 1114 currentSource = ((ResourceIntVector) currentSource).first; 1115 currentTarget = ((ResourceIntVector) currentTarget).first; 1116 isFirst = false; 1117 }else{ 1118 currentSource.next = next[0]; 1119 currentTarget.next = next[1]; 1120 // set the next pointers 1121 currentSource = currentSource.next; 1122 currentTarget = currentTarget.next; 1123 } 1124 }else if(name.equals(NOTE)){ 1125 String note = child.getFirstChild().getNodeValue(); 1126 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; 1127 } 1128 } 1129 } 1130 } parseTable(Node node, Resource[] set)1131 private void parseTable(Node node, Resource[] set){ 1132 if(set[0]==null){ 1133 set[0] = new ResourceTable(); 1134 set[1] = new ResourceTable(); 1135 } 1136 Resource currentSource = set[0]; 1137 Resource currentTarget = set[1]; 1138 1139 String resName = getAttributeValue(node, RESNAME); 1140 String translate = getAttributeValue(node,TRANSLATE); 1141 if(resName!=null && currentSource.name==null && currentTarget.name==null){ 1142 currentSource.name = currentTarget.name = resName; 1143 } 1144 currentTarget.translate = currentSource.translate = translate; 1145 1146 boolean isFirst = true; 1147 for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()){ 1148 short type = child.getNodeType(); 1149 String name = child.getNodeName(); 1150 if(type == Node.COMMENT_NODE){ 1151 currentSource.comment = currentTarget.comment = child.getNodeValue(); 1152 }else if(type == Node.ELEMENT_NODE){ 1153 if(name.equals(GROUPS)){ 1154 Resource[] next = new Resource[2]; 1155 parseGroup(child, next); 1156 if(isFirst==true){ 1157 // the down cast should be safe .. if not something is terribly wrong!! 1158 ((ResourceTable) currentSource).first = next[0]; 1159 ((ResourceTable) currentTarget).first = next[1]; 1160 currentSource = ((ResourceTable) currentSource).first; 1161 currentTarget = ((ResourceTable) currentTarget).first; 1162 isFirst = false; 1163 }else{ 1164 currentSource.next = next[0]; 1165 currentTarget.next = next[1]; 1166 // set the next pointers 1167 currentSource = currentSource.next; 1168 currentTarget = currentTarget.next; 1169 } 1170 }else if(name.equals(TRANSUNIT)){ 1171 Resource[] next = new Resource[2]; 1172 parseTransUnit(child, next); 1173 if(isFirst==true){ 1174 // the down cast should be safe .. if not something is terribly wrong!! 1175 ((ResourceTable) currentSource).first = next[0]; 1176 ((ResourceTable) currentTarget).first = next[1]; 1177 currentSource = ((ResourceTable) currentSource).first; 1178 currentTarget = ((ResourceTable) currentTarget).first; 1179 isFirst = false; 1180 }else{ 1181 currentSource.next = next[0]; 1182 currentTarget.next = next[1]; 1183 // set the next pointers 1184 currentSource = currentSource.next; 1185 currentTarget = currentTarget.next; 1186 } 1187 }else if(name.equals(NOTE)){ 1188 String note = child.getFirstChild().getNodeValue(); 1189 currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note; 1190 }else if(name.equals(BINUNIT)){ 1191 Resource[] next = new Resource[2]; 1192 parseBinUnit(child, next); 1193 if(isFirst==true){ 1194 // the down cast should be safe .. if not something is terribly wrong!! 1195 ((ResourceTable) currentSource).first = next[0]; 1196 ((ResourceTable) currentTarget).first = next[1]; 1197 currentSource = ((ResourceTable) currentSource).first; 1198 currentTarget = ((ResourceTable) currentTarget).first; 1199 isFirst = false; 1200 }else{ 1201 currentSource.next = next[0]; 1202 currentTarget.next = next[1]; 1203 // set the next pointers 1204 currentSource = currentSource.next; 1205 currentTarget = currentTarget.next; 1206 } 1207 } 1208 } 1209 } 1210 } 1211 parseGroup(Node node, Resource[] set)1212 private void parseGroup(Node node, Resource[] set){ 1213 1214 // figure out what kind of group this is 1215 String resType = getAttributeValue(node, RESTYPE); 1216 if(resType.equals(resources[ARRAY_RESOURCE])){ 1217 parseArray(node, set); 1218 }else if( resType.equals(resources[TABLE_RESOURCE])){ 1219 parseTable(node, set); 1220 }else if( resType.equals(resources[INTVECTOR_RESOURCE])){ 1221 parseIntVector(node, set); 1222 } 1223 } 1224 1225 writeLine(OutputStream writer, String line)1226 private void writeLine(OutputStream writer, String line) { 1227 try { 1228 byte[] bytes = line.getBytes(CHARSET); 1229 writer.write(bytes, 0, bytes.length); 1230 } catch (Exception e) { 1231 System.err.println(e); 1232 System.exit(1); 1233 } 1234 } 1235 writeHeader(OutputStream writer, String fileName)1236 private void writeHeader(OutputStream writer, String fileName){ 1237 final String header = 1238 "// ***************************************************************************" + LINESEP + 1239 "// *" + LINESEP + 1240 "// * Tool: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter.java" + LINESEP + 1241 "// * Date & Time: {0,date,MM/dd/yyyy hh:mm:ss a z}"+ LINESEP + 1242 "// * Source File: {1}" + LINESEP + 1243 "// *" + LINESEP + 1244 "// ***************************************************************************" + LINESEP; 1245 1246 writeBOM(writer); 1247 MessageFormat format = new MessageFormat(header); 1248 Object args[] = {new Date(System.currentTimeMillis()), fileName}; 1249 1250 writeLine(writer, format.format(args)); 1251 } 1252 writeBOM(OutputStream buffer)1253 private void writeBOM(OutputStream buffer) { 1254 try { 1255 byte[] bytes = BOM.getBytes(CHARSET); 1256 buffer.write(bytes, 0, bytes.length); 1257 } catch(Exception e) { 1258 System.err.println(e); 1259 System.exit(1); 1260 } 1261 } 1262 } 1263