1 /* 2 ******************************************************************************* 3 * Copyright (C) 2004-2012, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * Created on Jul 28, 2004 8 * 9 */ 10 package org.unicode.cldr.util; 11 12 import java.io.BufferedReader; 13 import java.io.File; 14 import java.io.FileReader; 15 import java.io.IOException; 16 import java.io.PrintWriter; 17 import java.util.ArrayList; 18 import java.util.HashMap; 19 import java.util.Iterator; 20 import java.util.List; 21 import java.util.Map; 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 import javax.xml.parsers.DocumentBuilder; 26 import javax.xml.parsers.DocumentBuilderFactory; 27 import javax.xml.transform.OutputKeys; 28 import javax.xml.transform.Transformer; 29 import javax.xml.transform.TransformerException; 30 import javax.xml.transform.TransformerFactory; 31 import javax.xml.transform.dom.DOMSource; 32 import javax.xml.transform.stream.StreamResult; 33 import javax.xml.xpath.XPath; 34 import javax.xml.xpath.XPathConstants; 35 import javax.xml.xpath.XPathExpression; 36 import javax.xml.xpath.XPathExpressionException; 37 import javax.xml.xpath.XPathFactory; 38 39 import org.unicode.cldr.icu.LDMLConstants; 40 import org.unicode.cldr.util.XMLFileReader.SimpleHandler; 41 import org.w3c.dom.Document; 42 import org.w3c.dom.NamedNodeMap; 43 import org.w3c.dom.Node; 44 import org.w3c.dom.NodeList; 45 import org.xml.sax.ErrorHandler; 46 import org.xml.sax.InputSource; 47 import org.xml.sax.SAXException; 48 import org.xml.sax.SAXParseException; 49 50 /** 51 * @author ram 52 * 53 * TODO To change the template for this generated type comment go to 54 * Window - Preferences - Java - Code Generation - Code and Comments 55 */ 56 public class LDMLUtilities { 57 58 public static final int XML = 0, 59 TXT = 1; 60 private static final boolean DEBUG = false; 61 62 /** 63 * Creates a fully resolved locale starting with root and 64 * 65 * @param sourceDir 66 * @param locale 67 * @return 68 */ getFullyResolvedLDML(String sourceDir, String locale, boolean ignoreRoot, boolean ignoreUnavailable, boolean ignoreIfNoneAvailable, boolean ignoreDraft)69 public static Document getFullyResolvedLDML(String sourceDir, String locale, 70 boolean ignoreRoot, boolean ignoreUnavailable, 71 boolean ignoreIfNoneAvailable, boolean ignoreDraft) { 72 return getFullyResolvedLDML(sourceDir, locale, ignoreRoot, ignoreUnavailable, ignoreIfNoneAvailable, 73 ignoreDraft, null); 74 } 75 getFullyResolvedLDML(String sourceDir, String locale, boolean ignoreRoot, boolean ignoreUnavailable, boolean ignoreIfNoneAvailable, boolean ignoreDraft, Map<String, String> stack)76 private static Document getFullyResolvedLDML(String sourceDir, String locale, 77 boolean ignoreRoot, boolean ignoreUnavailable, 78 boolean ignoreIfNoneAvailable, boolean ignoreDraft, Map<String, String> stack) { 79 Document full = null; 80 if (stack != null) { 81 // For guarding against cicular references 82 String key = "SRC:" + sourceDir + File.separator + locale + ".xml"; 83 if (stack.get(key) != null) { 84 System.err.println("Found circular aliases! " + key); 85 System.exit(-1); 86 } 87 stack.put(key, ""); 88 } 89 // System.err.println("In getFullyResolvedLDML "+sourceDir + " " + locale); 90 try { 91 full = parse(sourceDir + File.separator + "root.xml", ignoreRoot); 92 /* 93 * Debugging 94 * 95 * Node[] list = getNodeArray(full, LDMLConstants.ALIAS); 96 * if(list.length>0){ 97 * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length); 98 * } 99 */ 100 101 if (DEBUG) { 102 try { 103 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter( 104 new java.io.FileOutputStream("./" + File.separator 105 + "root_debug.xml"), 106 "UTF-8"); 107 LDMLUtilities.printDOMTree(full, new PrintWriter(writer), 108 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null); 109 writer.flush(); 110 } catch (IOException e) { 111 // throw the exceptionaway .. this is for debugging 112 } 113 } 114 } catch (RuntimeException ex) { 115 if (!ignoreRoot) { 116 throw ex; 117 } 118 } 119 int index = locale.indexOf(".xml"); 120 if (index > -1) { 121 locale = locale.substring(0, index); 122 } 123 if (locale.equals("root")) { 124 full = resolveAliases(full, sourceDir, locale, ignoreDraft, stack); 125 return full; 126 } 127 String[] constituents = locale.split("_"); 128 String loc = null; 129 boolean isAvailable = false; 130 // String lastLoc = "root"; 131 for (int i = 0; i < constituents.length; i++) { 132 if (loc == null) { 133 loc = constituents[i]; 134 } else { 135 loc = loc + "_" + constituents[i]; 136 } 137 Document doc = null; 138 139 // Try cache 140 // doc = readMergeCache(sourceDir, lastLoc, loc); 141 // if(doc == null) { .. 142 String fileName = sourceDir + File.separator + loc + ".xml"; 143 File file = new File(fileName); 144 if (file.exists()) { 145 isAvailable = true; 146 doc = parseAndResolveAlias(fileName, loc, ignoreUnavailable); 147 148 /* 149 * Debugging 150 * 151 * Node[] list = getNodeArray(doc, LDMLConstants.ALIAS); 152 * if(list.length>0){ 153 * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length); 154 * } 155 */ 156 if (full == null) { 157 full = doc; 158 } else { 159 StringBuffer xpath = new StringBuffer(); 160 mergeLDMLDocuments(full, doc, xpath, loc, sourceDir, ignoreDraft, false); 161 if (DEBUG) { 162 try { 163 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter( 164 new java.io.FileOutputStream("./" + File.separator + loc 165 + "_debug.xml"), 166 "UTF-8"); 167 LDMLUtilities.printDOMTree(full, new PrintWriter(writer), 168 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null); 169 writer.flush(); 170 } catch (IOException e) { 171 // throw the exceptionaway .. this is for debugging 172 } 173 } 174 } 175 /* 176 * debugging 177 * 178 * Node ec = getNode(full, "//ldml/characters/exemplarCharacters"); 179 * if(ec==null){ 180 * System.err.println("Could not find exemplarCharacters"); 181 * }else{ 182 * System.out.println("The chars are: "+ getNodeValue(ec)); 183 * } 184 */ 185 186 // writeMergeCache(sourceDir, lastLoc, loc, full); 187 // lastLoc = loc; 188 } else { 189 if (!ignoreUnavailable) { 190 throw new RuntimeException("Could not find: " + fileName); 191 } 192 } 193 // TODO: investigate if we really need to revalidate the DOM tree! 194 // full = revalidate(full, locale); 195 } 196 197 if (ignoreIfNoneAvailable == true && isAvailable == false) { 198 return null; 199 } 200 201 if (DEBUG) { 202 try { 203 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter( 204 new java.io.FileOutputStream("./" + File.separator + locale 205 + "_ba_debug.xml"), 206 "UTF-8"); 207 LDMLUtilities.printDOMTree(full, new PrintWriter(writer), 208 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null); 209 writer.flush(); 210 } catch (IOException e) { 211 // throw the exceptionaway .. this is for debugging 212 } 213 } 214 // get the real locale name 215 locale = getLocaleName(full); 216 // Resolve the aliases once the data is built 217 full = resolveAliases(full, sourceDir, locale, ignoreDraft, stack); 218 219 if (DEBUG) { 220 try { 221 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter( 222 new java.io.FileOutputStream("./" + File.separator + locale 223 + "_aa_debug.xml"), 224 "UTF-8"); 225 LDMLUtilities.printDOMTree(full, new PrintWriter(writer), 226 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null); 227 writer.flush(); 228 } catch (IOException e) { 229 // throw the exceptionaway .. this is for debugging 230 } 231 } 232 return full; 233 } 234 getLocaleName(Document doc)235 public static String getLocaleName(Document doc) { 236 237 Node ln = LDMLUtilities.getNode(doc, "//ldml/identity/language"); 238 Node tn = LDMLUtilities.getNode(doc, "//ldml/identity/territory"); 239 Node sn = LDMLUtilities.getNode(doc, "//ldml/identity/script"); 240 Node vn = LDMLUtilities.getNode(doc, "//ldml/identity/variant"); 241 242 StringBuffer locName = new StringBuffer(); 243 String lang = LDMLUtilities.getAttributeValue(ln, LDMLConstants.TYPE); 244 if (lang != null) { 245 locName.append(lang); 246 } else { 247 throw new IllegalArgumentException("Did not get any value for language node from identity."); 248 } 249 if (sn != null) { 250 String script = LDMLUtilities.getAttributeValue(sn, LDMLConstants.TYPE); 251 if (script != null) { 252 locName.append("_"); 253 locName.append(script); 254 } 255 } 256 if (tn != null) { 257 String terr = LDMLUtilities.getAttributeValue(tn, LDMLConstants.TYPE); 258 if (terr != null) { 259 locName.append("_"); 260 locName.append(terr); 261 } 262 } 263 if (vn != null) { 264 String variant = LDMLUtilities.getAttributeValue(vn, LDMLConstants.TYPE); 265 if (variant != null && tn != null) { 266 locName.append("_"); 267 locName.append(variant); 268 } 269 } 270 return locName.toString(); 271 } 272 273 // revalidate wasn't called anywhere. 274 // TODO: if needed, reimplement using DOM level 3 275 /* 276 * public static Document revalidate(Document doc, String fileName){ 277 * // what a waste!! 278 * // to revalidate an in-memory DOM tree we need to first 279 * // serialize it to byte array and read it back again. 280 * // in DOM level 3 implementation there is API to validate 281 * // in-memory DOM trees but the latest implementation of Xerces 282 * // can only validate against schemas not DTDs!!! 283 * try{ 284 * // revalidate the document 285 * Serializer serializer = SerializerFactory.getSerializer(OutputProperties.getDefaultMethodProperties("xml")); 286 * ByteArrayOutputStream os = new ByteArrayOutputStream(); 287 * serializer.setOutputStream(os); 288 * DOMSerializer ds = serializer.asDOMSerializer(); 289 * //ds.serialize(doc); 290 * os.flush(); 291 * ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); 292 * doc = parse(new InputSource(is),"Fully resolved: "+fileName, false); 293 * return doc; 294 * }catch(IOException ex){ 295 * throw new RuntimeException(ex); 296 * } 297 * } 298 */ 299 @Deprecated convertXPath2ICU(Node alias, Node namespaceNode, StringBuffer fullPath)300 public static String convertXPath2ICU(Node alias, Node namespaceNode, StringBuffer fullPath) 301 throws TransformerException { 302 StringBuilder sb = new StringBuilder(fullPath.toString()); 303 return convertXPath2ICU(alias, namespaceNode, sb); 304 } 305 convertXPath2ICU(Node alias, Node namespaceNode, StringBuilder fullPath)306 public static String convertXPath2ICU(Node alias, Node namespaceNode, StringBuilder fullPath) 307 throws TransformerException { 308 Node context = alias.getParentNode(); 309 StringBuffer icu = new StringBuffer(); 310 String source = getAttributeValue(alias, LDMLConstants.SOURCE); 311 String xpath = getAttributeValue(alias, LDMLConstants.PATH); 312 313 // make sure that the xpaths are valid 314 if (namespaceNode == null) { 315 XPathAPI_eval(context, fullPath.toString()); 316 if (xpath != null) { 317 XPathAPI_eval(context, xpath); 318 } 319 } else { 320 XPathAPI_eval(context, fullPath.toString(), namespaceNode); 321 if (xpath != null) { 322 XPathAPI_eval(context, xpath, namespaceNode); 323 } 324 } 325 if (source.equals(LDMLConstants.LOCALE)) { 326 icu.append("/"); 327 icu.append(source.toUpperCase()); 328 } else { 329 icu.append(source); 330 } 331 if (xpath != null) { 332 StringBuilder resolved = XPathTokenizer.relativeToAbsolute(xpath, fullPath); 333 // make sure that fullPath is not corrupted! 334 XPathAPI_eval(context, fullPath.toString()); 335 336 // TODO .. do the conversion 337 XPathTokenizer tokenizer = new XPathTokenizer(resolved.toString()); 338 339 String token = tokenizer.nextToken(); 340 while (token != null) { 341 if (!token.equals("ldml")) { 342 String equiv = getICUEquivalent(token); 343 if (equiv == null) { 344 throw new IllegalArgumentException("Could not find ICU equivalent for token: " + token); 345 } 346 if (equiv.length() > 0) { 347 icu.append("/"); 348 icu.append(equiv); 349 } 350 } 351 token = tokenizer.nextToken(); 352 } 353 } 354 return icu.toString(); 355 } 356 convertXPath2ICU(String source, String xpath, String basePath, String fullPath)357 public static String convertXPath2ICU(String source, String xpath, String basePath, String fullPath) 358 throws TransformerException { 359 // Node context = alias.getParentNode(); 360 StringBuffer icu = new StringBuffer(); 361 362 // TODO: make sure that the xpaths are valid. How? 363 364 if (source.equals(LDMLConstants.LOCALE)) { 365 icu.append("/"); 366 icu.append(source.toUpperCase()); 367 } else { 368 icu.append(source); 369 } 370 371 if (xpath != null) { 372 StringBuilder fullPathBuffer = new StringBuilder(fullPath); 373 StringBuilder resolved = XPathTokenizer.relativeToAbsolute(xpath, fullPathBuffer); 374 // TODO: make sure that fullPath is not corrupted! How? 375 // XPathAPI.eval(context, fullPath.toString()); 376 377 // TODO .. do the conversion 378 XPathTokenizer tokenizer = new XPathTokenizer(resolved); 379 380 String token = tokenizer.nextToken(); 381 while (token != null) { 382 if (!token.equals("ldml")) { 383 String equiv = getICUEquivalent(token); 384 if (equiv == null) { 385 throw new IllegalArgumentException("Could not find ICU equivalent for token: " + token); 386 } 387 if (equiv.length() > 0) { 388 icu.append("/"); 389 icu.append(equiv); 390 } 391 } 392 token = tokenizer.nextToken(); 393 } 394 } 395 return icu.toString(); 396 } 397 getDayIndexAsString(String type)398 public static String getDayIndexAsString(String type) { 399 if (type.equals("sun")) { 400 return "0"; 401 } else if (type.equals("mon")) { 402 return "1"; 403 } else if (type.equals("tue")) { 404 return "2"; 405 } else if (type.equals("wed")) { 406 return "3"; 407 } else if (type.equals("thu")) { 408 return "4"; 409 } else if (type.equals("fri")) { 410 return "5"; 411 } else if (type.equals("sat")) { 412 return "6"; 413 } else { 414 throw new IllegalArgumentException("Unknown type: " + type); 415 } 416 } 417 getMonthIndexAsString(String type)418 public static String getMonthIndexAsString(String type) { 419 return Integer.toString(Integer.parseInt(type) - 1); 420 } 421 getICUEquivalent(String token)422 private static String getICUEquivalent(String token) { 423 int index = 0; 424 if (token.indexOf(LDMLConstants.LDN) > -1) { 425 return ""; 426 } else if (token.indexOf(LDMLConstants.LANGUAGES) > -1) { 427 return "Languages"; 428 } else if (token.indexOf(LDMLConstants.LANGUAGE) > -1) { 429 return getAttributeValue(token, LDMLConstants.TYPE); 430 } else if (token.indexOf(LDMLConstants.TERRITORIES) > -1) { 431 return "Countries"; 432 } else if (token.indexOf(LDMLConstants.TERRITORY) > -1) { 433 return getAttributeValue(token, LDMLConstants.TYPE); 434 } else if (token.indexOf(LDMLConstants.SCRIPTS) > -1) { 435 return "Scripts"; 436 } else if (token.indexOf(LDMLConstants.SCRIPT) > -1) { 437 return getAttributeValue(token, LDMLConstants.TYPE); 438 } else if (token.indexOf(LDMLConstants.VARIANTS) > -1) { 439 return "Variants"; 440 } else if (token.indexOf(LDMLConstants.VARIANT) > -1) { 441 return getAttributeValue(token, LDMLConstants.TYPE); 442 } else if (token.indexOf(LDMLConstants.KEYS) > -1) { 443 return "Keys"; 444 } else if (token.indexOf(LDMLConstants.KEY) > -1) { 445 return getAttributeValue(token, LDMLConstants.TYPE); 446 } else if (token.indexOf(LDMLConstants.TYPES) > -1) { 447 return "Types"; 448 } else if ((index = token.indexOf(LDMLConstants.TYPE)) > -1 && token.charAt(index - 1) != '@') { 449 String type = getAttributeValue(token, LDMLConstants.TYPE); 450 String key = getAttributeValue(token, LDMLConstants.KEY); 451 return type + "/" + key; 452 } else if (token.indexOf(LDMLConstants.LAYOUT) > -1) { 453 return "Layout"; 454 } else if (token.indexOf(LDMLConstants.ORIENTATION) > -1) { 455 // TODO fix this 456 } else if (token.indexOf(LDMLConstants.CONTEXT_TRANSFORMS) > -1) { 457 return "contextTransforms"; 458 } else if (token.indexOf(LDMLConstants.CONTEXT_TRANSFORM_USAGE) > -1) { 459 return getAttributeValue(token, LDMLConstants.TYPE); 460 } else if (token.indexOf(LDMLConstants.CHARACTERS) > -1) { 461 return ""; 462 } else if (token.indexOf(LDMLConstants.EXEMPLAR_CHARACTERS) > -1) { 463 return "ExemplarCharacters"; 464 } else if (token.indexOf(LDMLConstants.MEASUREMENT) > -1) { 465 return ""; 466 } else if (token.indexOf(LDMLConstants.MS) > -1) { 467 return "MeasurementSystem"; 468 } else if (token.indexOf(LDMLConstants.PAPER_SIZE) > -1) { 469 return "PaperSize"; 470 } else if (token.indexOf(LDMLConstants.HEIGHT) > -1) { 471 return "0"; 472 } else if (token.indexOf(LDMLConstants.WIDTH) > -1) { 473 return "1"; 474 } else if (token.indexOf(LDMLConstants.DATES) > -1) { 475 return ""; 476 } else if (token.indexOf(LDMLConstants.LPC) > -1) { 477 return "localPatternCharacters"; 478 } else if (token.indexOf(LDMLConstants.CALENDARS) > -1) { 479 return "calendar"; 480 } else if (token.indexOf(LDMLConstants.DEFAULT) > -1) { 481 return "default"; 482 } else if (token.indexOf(LDMLConstants.CALENDAR) > -1) { 483 return getAttributeValue(token, LDMLConstants.TYPE); 484 } else if (token.indexOf(LDMLConstants.ERAS) > -1) { 485 return "eras"; 486 } else if (token.indexOf(LDMLConstants.ERAABBR) > -1) { 487 return "abbreviated"; 488 } else if (token.indexOf(LDMLConstants.ERA) > -1) { 489 return getAttributeValue(token, LDMLConstants.TYPE); 490 } else if (token.indexOf(LDMLConstants.NUMBERS) > -1) { 491 // TODO fix this 492 } else if (token.indexOf(LDMLConstants.SYMBOLS) > -1) { 493 return "NumberElements"; 494 } else if (token.indexOf(LDMLConstants.DATE_FORMATS) > -1) { 495 // TODO fix this 496 } else if (token.indexOf(LDMLConstants.DFL) > -1) { 497 // TODO fix this 498 } else if (token.indexOf(LDMLConstants.DATE_FORMAT) > -1) { 499 // TODO fix this 500 } else if (token.indexOf(LDMLConstants.TIME_FORMATS) > -1) { 501 // TODO fix this 502 } else if (token.indexOf(LDMLConstants.TFL) > -1) { 503 // TODO fix this 504 } else if (token.indexOf(LDMLConstants.TIME_FORMAT) > -1) { 505 // TODO fix this 506 } else if (token.indexOf(LDMLConstants.DATE_TIME_FORMATS) > -1) { 507 // TODO fix this 508 return "DateTimePatterns"; 509 } else if (token.indexOf(LDMLConstants.INTVL_FMTS) > -1) { 510 return "intervalFormats"; 511 // TODO fix this 512 } else if (token.indexOf(LDMLConstants.DTFL) > -1) { 513 // TODO fix this 514 } else if (token.indexOf(LDMLConstants.DATE_TIME_FORMAT) > -1) { 515 // TODO fix this 516 } else if (token.indexOf(LDMLConstants.INTVL_FMTS) > -1) { 517 // TODO fix this 518 } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_SETS) > -1) { 519 return "cyclicNameSets"; 520 } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_SET) > -1) { 521 return getAttributeValue(token, LDMLConstants.TYPE); 522 } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_CONTEXT) > -1) { 523 return getAttributeValue(token, LDMLConstants.TYPE); 524 } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_WIDTH) > -1) { 525 return getAttributeValue(token, LDMLConstants.TYPE); 526 } else if (token.indexOf(LDMLConstants.CYCLIC_NAME) > -1) { 527 String valStr = getAttributeValue(token, LDMLConstants.TYPE); 528 return getMonthIndexAsString(valStr); 529 } else if (token.indexOf(LDMLConstants.MONTHS) > -1) { 530 return "monthNames"; 531 } else if (token.indexOf(LDMLConstants.MONTH_PATTERNS) > -1) { 532 return "monthPatterns"; 533 } else if (token.indexOf(LDMLConstants.MONTH_PATTERN_CONTEXT) > -1) { 534 return getAttributeValue(token, LDMLConstants.TYPE); 535 } else if (token.indexOf(LDMLConstants.MONTH_PATTERN_WIDTH) > -1) { 536 return getAttributeValue(token, LDMLConstants.TYPE); 537 } else if (token.indexOf(LDMLConstants.MONTH_PATTERN) > -1) { 538 return getAttributeValue(token, LDMLConstants.TYPE); 539 } else if (token.indexOf(LDMLConstants.MONTH_CONTEXT) > -1) { 540 return getAttributeValue(token, LDMLConstants.TYPE); 541 } else if (token.indexOf(LDMLConstants.MONTH_WIDTH) > -1) { 542 return getAttributeValue(token, LDMLConstants.TYPE); 543 } else if (token.indexOf(LDMLConstants.MONTH) > -1) { 544 String valStr = getAttributeValue(token, LDMLConstants.TYPE); 545 return getMonthIndexAsString(valStr); 546 } else if (token.indexOf(LDMLConstants.DAYPERIODS) > -1) { 547 return "dayPeriods"; 548 } else if (token.indexOf(LDMLConstants.DAYS) > -1) { 549 return "dayNames"; 550 } else if (token.indexOf(LDMLConstants.DAY_CONTEXT) > -1) { 551 return getAttributeValue(token, LDMLConstants.TYPE); 552 } else if (token.indexOf(LDMLConstants.DAY_WIDTH) > -1) { 553 return getAttributeValue(token, LDMLConstants.TYPE); 554 } else if (token.indexOf(LDMLConstants.DAY) > -1) { 555 String dayName = getAttributeValue(token, LDMLConstants.TYPE); 556 return getDayIndexAsString(dayName); 557 } else if (token.indexOf(LDMLConstants.QUARTER_WIDTH) > -1) { 558 return getAttributeValue(token, LDMLConstants.TYPE); 559 } else if (token.indexOf(LDMLConstants.QUARTER_CONTEXT) > -1) { 560 return getAttributeValue(token, LDMLConstants.TYPE); 561 } else if (token.indexOf(LDMLConstants.QUARTERS) > -1) { 562 return "quarters"; 563 } else if (token.indexOf(LDMLConstants.QUARTER) > -1) { 564 String valStr = getAttributeValue(token, LDMLConstants.TYPE); 565 return getMonthIndexAsString(valStr); 566 } else if (token.indexOf(LDMLConstants.COLLATIONS) > -1) { 567 return "collations"; 568 } else if (token.indexOf(LDMLConstants.COLLATION) > -1) { 569 return getAttributeValue(token, LDMLConstants.TYPE); 570 } 571 572 // TODO: this method is not finished yet 573 // the conversion of Xpath to ICU alias path 574 // is not as straight forward as I thought 575 // need to cater to idiosynchracies of each 576 // element node :( 577 throw new IllegalArgumentException("Unknown Xpath fragment: " + token); 578 } 579 580 /** 581 * 582 * @param token 583 * XPath token fragment 584 * @param attrib 585 * attribute whose value must be fetched 586 * @return 587 */ getAttributeValue(String token, String attrib)588 private static String getAttributeValue(String token, String attrib) { 589 int attribStart = token.indexOf(attrib); 590 int valStart = token.indexOf('=', attribStart) + 1/* skip past the separtor */; 591 int valEnd = token.indexOf('@', valStart); 592 if (valEnd < 0) { 593 valEnd = valStart + (token.length() - valStart - 1); 594 } else { 595 valEnd = token.length() - 1 /* valEnd should be index */; 596 } 597 String value = token.substring(valStart, valEnd); 598 int s = value.indexOf('\''); 599 if (s > -1) { 600 s++; 601 int e = value.lastIndexOf('\''); 602 return value.substring(s, e); 603 } else { 604 // also handle "" 605 s = value.indexOf('"'); 606 if (s > -1) { 607 s++; 608 int e = value.lastIndexOf('"'); 609 return value.substring(s, e); 610 } 611 } 612 return value; 613 } 614 615 @Deprecated mergeLDMLDocuments(Document source, Node override, StringBuffer xpath, String thisName, String sourceDir, boolean ignoreDraft, boolean ignoreVersion)616 public static Node mergeLDMLDocuments(Document source, Node override, StringBuffer xpath, 617 String thisName, String sourceDir, boolean ignoreDraft, 618 boolean ignoreVersion) { 619 StringBuilder sb = new StringBuilder(xpath.toString()); 620 return mergeLDMLDocuments(source, override, sb, thisName, sourceDir, ignoreDraft, ignoreVersion); 621 } 622 623 /** 624 * Resolved Data File 625 * <p> 626 * To produce fully resolved locale data file from CLDR for a locale ID L, you start with root, and replace/add 627 * items from the child locales until you get down to L. More formally, this can be expressed as the following 628 * procedure. 629 * </p> 630 * <ol> 631 * <li>Let Result be an empty LDML file.</li> 632 * 633 * <li>For each Li in the locale chain for L 634 * <ol> 635 * <li>For each element pair P in the LDML file for Li: 636 * <ol> 637 * <li>If Result has an element pair Q with an equivalent element chain, remove Q.</li> 638 * <li>Add P to Result.</li> 639 * </ol> 640 * </li> 641 * </ol> 642 * 643 * </li> 644 * </ol> 645 * <p> 646 * Note: when adding an element pair to a result, it has to go in the right order for it to be valid according to 647 * the DTD. 648 * </p> 649 * 650 * @param source 651 * @param override 652 * @return the merged document 653 */ mergeLDMLDocuments(Document source, Node override, StringBuilder xpath, String thisName, String sourceDir, boolean ignoreDraft, boolean ignoreVersion)654 public static Node mergeLDMLDocuments(Document source, Node override, StringBuilder xpath, 655 String thisName, String sourceDir, boolean ignoreDraft, 656 boolean ignoreVersion) { 657 if (source == null) { 658 return override; 659 } 660 if (xpath.length() == 0) { 661 xpath.append("/"); 662 } 663 664 // boolean gotcha = false; 665 // String oldx = new String(xpath); 666 // if(override.getNodeName().equals("week")) { 667 // gotcha = true; 668 // System.out.println("SRC: " + getNode(source, xpath.toString()).toString()); 669 // System.out.println("OVR: " + override.toString()); 670 // } 671 672 // we know that every child xml file either adds or 673 // overrides the elements in parent 674 // so we traverse the child, at every node check if 675 // if the node is present in the source, 676 // if (present) 677 // recurse to replace any nodes that need to be overridded 678 // else 679 // import the node into source 680 Node child = override.getFirstChild(); 681 while (child != null) { 682 // we are only concerned with element nodes 683 if (child.getNodeType() != Node.ELEMENT_NODE) { 684 child = child.getNextSibling(); 685 continue; 686 } 687 String childName = child.getNodeName(); 688 689 int savedLength = xpath.length(); 690 xpath.append("/"); 691 xpath.append(childName); 692 appendXPathAttribute(child, xpath, false, false); 693 Node nodeInSource = null; 694 695 if (childName.indexOf(":") > -1) { 696 nodeInSource = getNode(source, xpath.toString(), child); 697 } else { 698 nodeInSource = getNode(source, xpath.toString()); 699 } 700 701 Node parentNodeInSource = null; 702 if (nodeInSource == null) { 703 // the child xml has a new node 704 // that should be added to parent 705 String parentXpath = xpath.substring(0, savedLength); 706 707 if (childName.indexOf(":") > -1) { 708 parentNodeInSource = getNode(source, parentXpath, child); 709 } else { 710 parentNodeInSource = getNode(source, parentXpath); 711 } 712 if (parentNodeInSource == null) { 713 throw new RuntimeException("Internal Error"); 714 } 715 716 Node childToImport = source.importNode(child, true); 717 parentNodeInSource.appendChild(childToImport); 718 } else if (childName.equals(LDMLConstants.IDENTITY)) { 719 if (!ignoreVersion) { 720 // replace the source doc 721 // none of the elements under collations are inherited 722 // only the node as a whole!! 723 parentNodeInSource = nodeInSource.getParentNode(); 724 Node childToImport = source.importNode(child, true); 725 parentNodeInSource.replaceChild(childToImport, nodeInSource); 726 } 727 } else if (childName.equals(LDMLConstants.COLLATION)) { 728 // replace the source doc 729 // none of the elements under collations are inherited 730 // only the node as a whole!! 731 parentNodeInSource = nodeInSource.getParentNode(); 732 Node childToImport = source.importNode(child, true); 733 parentNodeInSource.replaceChild(childToImport, nodeInSource); 734 // override the validSubLocales attribute 735 String val = LDMLUtilities.getAttributeValue(child.getParentNode(), LDMLConstants.VALID_SUBLOCALE); 736 NamedNodeMap map = parentNodeInSource.getAttributes(); 737 Node vs = map.getNamedItem(LDMLConstants.VALID_SUBLOCALE); 738 vs.setNodeValue(val); 739 } else { 740 boolean childElementNodes = areChildrenElementNodes(child); 741 boolean sourceElementNodes = areChildrenElementNodes(nodeInSource); 742 // System.out.println(childName + ":" + childElementNodes + "/" + sourceElementNodes); 743 if (childElementNodes && sourceElementNodes) { 744 // recurse to pickup any children! 745 mergeLDMLDocuments(source, child, xpath, thisName, sourceDir, ignoreDraft, ignoreVersion); 746 } else { 747 // we have reached a leaf node now get the 748 // replace to the source doc 749 parentNodeInSource = nodeInSource.getParentNode(); 750 Node childToImport = source.importNode(child, true); 751 parentNodeInSource.replaceChild(childToImport, nodeInSource); 752 } 753 } 754 xpath.delete(savedLength, xpath.length()); 755 child = child.getNextSibling(); 756 } 757 // if(gotcha==true) { 758 // System.out.println("Final: " + getNode(source, oldx).toString()); 759 // } 760 return source; 761 } 762 getNodeArray(Document doc, String tagName)763 private static Node[] getNodeArray(Document doc, String tagName) { 764 NodeList list = doc.getElementsByTagName(tagName); 765 // node list is dynamic .. if a node is deleted, then 766 // list is immidiately updated. 767 // so first cache the nodes returned and do stuff 768 Node[] array = new Node[list.getLength()]; 769 for (int i = 0; i < list.getLength(); i++) { 770 array[i] = list.item(i); 771 } 772 return array; 773 } 774 775 /** 776 * Utility to create abosolute Xpath from 1.1 style alias element 777 * 778 * @param node 779 * @param type 780 * @return 781 */ getAbsoluteXPath(Node node, String type)782 public static String getAbsoluteXPath(Node node, String type) { 783 StringBuffer xpath = new StringBuffer(); 784 StringBuffer xpathFragment = new StringBuffer(); 785 node = node.getParentNode(); // the node is alias node .. get its parent 786 if (node == null) { 787 throw new IllegalArgumentException("Alias node's parent is null!"); 788 } 789 xpath.append(node.getNodeName()); 790 if (type != null) { 791 xpath.append("[@type='" + type + "']"); // TODO: double quotes? 792 } 793 Node parent = node; 794 while ((parent = parent.getParentNode()) != null) { 795 xpathFragment.setLength(0); 796 xpathFragment.append(parent.getNodeName()); 797 if (parent.getNodeType() != Node.DOCUMENT_NODE) { 798 appendXPathAttribute(parent, xpathFragment); 799 xpath.insert(0, "/"); 800 xpath.insert(0, xpathFragment); 801 } 802 } 803 xpath.insert(0, "//"); 804 return xpath.toString(); 805 } 806 807 /** 808 * 809 * @param n1 810 * @param n2 811 * preferred list 812 * @param xpath 813 * @return 814 */ mergeNodeLists(Object[] n1, Object[] n2)815 private static Node[] mergeNodeLists(Object[] n1, Object[] n2) { 816 StringBuffer xp1 = new StringBuffer(); 817 StringBuffer xp2 = new StringBuffer(); 818 int l1 = xp1.length(), l2 = xp2.length(); 819 Map<String, Object> map = new HashMap<String, Object>(); 820 if (n2 == null || n2.length == 0) { 821 Node[] na = new Node[n1.length]; 822 for (int i = 0; i < n1.length; i++) { 823 na[i] = (Node) n1[i]; 824 } 825 return na; 826 } 827 for (int i = 0; i < n1.length; i++) { 828 xp1.append(((Node) n1[i]).getNodeName()); 829 appendXPathAttribute((Node) n1[i], xp1); 830 map.put(xp1.toString(), n1[i]); 831 xp1.setLength(l1); 832 } 833 for (int i = 0; i < n2.length; i++) { 834 xp2.append(((Node) n2[i]).getNodeName()); 835 appendXPathAttribute((Node) n2[i], xp2); 836 map.put(xp2.toString(), n2[i]); 837 xp2.setLength(l2); 838 } 839 Object[] arr = map.values().toArray(); 840 Node[] na = new Node[arr.length]; 841 for (int i = 0; i < arr.length; i++) { 842 na[i] = (Node) arr[i]; 843 } 844 return na; 845 } 846 847 /** 848 * 849 * @param fullyResolvedDoc 850 * @param sourceDir 851 * @param thisLocale 852 */ 853 // TODO guard against circular aliases resolveAliases(Document fullyResolvedDoc, String sourceDir, String thisLocale, boolean ignoreDraft, Map<String, String> stack)854 public static Document resolveAliases(Document fullyResolvedDoc, String sourceDir, String thisLocale, 855 boolean ignoreDraft, Map<String, String> stack) { 856 Node[] array = getNodeArray(fullyResolvedDoc, LDMLConstants.ALIAS); 857 858 // resolve all the aliases by iterating over 859 // the list of nodes 860 Node[] replacementList = null; 861 Node parent = null; 862 String source = null; 863 String path = null; 864 String type = null; 865 for (int i = 0; i < array.length; i++) { 866 Node node = array[i]; 867 /* 868 * //stop inherited aliases from overwriting valid locale data 869 * //ldml.dtd does not allow alias to have any sibling elements 870 * boolean bFoundSibling = false; 871 * Node n = node.getNextSibling(); 872 * while (n != null) 873 * { 874 * if (n.getNodeType() == Node.ELEMENT_NODE) 875 * { 876 * // System.err.println ("it's an element node " + n.getNodeName () + " " + n.getNodeValue()); 877 * bFoundSibling = true; 878 * break; 879 * } 880 * n = n.getNextSibling(); 881 * } 882 * if (bFoundSibling == true) 883 * continue; 884 */ 885 // initialize the stack for every alias! 886 stack = new HashMap<String, String>(); 887 if (node == null) { 888 System.err.println("list.item(" + i + ") returned null!. The list reports it's length as: " 889 + array.length); 890 continue; 891 } 892 parent = node.getParentNode(); 893 // boolean isDraft = isNodeDraft(node); 894 source = getAttributeValue(node, LDMLConstants.SOURCE); 895 path = getAttributeValue(node, LDMLConstants.PATH); 896 type = getAttributeValue(parent, LDMLConstants.TYPE); 897 if (parent.getParentNode() == null) { 898 // some of the nodes were orphaned by the previous alias resolution .. just continue 899 continue; 900 } 901 if (source != null && path == null) { 902 // this LDML 1.1 style alias parse it 903 path = getAbsoluteXPath(node, type); 904 } 905 String key = "SRC:" + thisLocale + ";XPATH:" + getAbsoluteXPath(node, type); 906 if (stack.get(key) != null) { 907 throw new IllegalStateException("Found circular aliases! " + key); 908 909 } 910 stack.put(key, ""); 911 if (source.equals(LDMLConstants.LOCALE)) { 912 913 Object[] aliasList = getChildNodeListAsArray(getNode(parent, path), false); 914 Object[] childList = getChildNodeListAsArray(parent, true); 915 replacementList = mergeNodeLists(aliasList, childList); 916 } else if (source != null && !source.equals(thisLocale)) { 917 // if source is defined then path should not be 918 // relative 919 if (path.indexOf("..") > 0) { 920 throw new IllegalArgumentException("Cannot parse relative xpath: " + path + 921 " in locale: " + source + 922 " from source locale: " + thisLocale); 923 } 924 // this is a is an absolute XPath 925 Document newDoc = getFullyResolvedLDML(sourceDir, source, false, true, false, ignoreDraft, stack); 926 replacementList = getNodeListAsArray(newDoc, path); 927 } else { 928 // path attribute is referencing another node in this DOM tree 929 replacementList = getNodeListAsArray(parent, path); 930 } 931 if (replacementList != null) { 932 parent.removeChild(node); 933 int listLen = replacementList.length; 934 if (listLen > 1) { 935 // check if the whole locale is aliased 936 // if yes then remove the identity from 937 // the current document! 938 if (path != null && path.equals("//ldml/*")) { 939 Node[] identity = getNodeArray(fullyResolvedDoc, LDMLConstants.IDENTICAL); 940 for (int j = 0; j < identity.length; j++) { 941 parent.removeChild(node); 942 } 943 } else { 944 // remove all the children of the parent node 945 removeChildNodes(parent); 946 } 947 for (int j = 0; j < listLen; j++) { 948 // found an element node in the aliased resource 949 // add to the source 950 Node child = replacementList[j]; 951 Node childToImport = fullyResolvedDoc.importNode(child, true); 952 // if(isDraft==true && childToImport.getNodeType() == Node.ELEMENT_NODE){ 953 // ((Element)childToImport).setAttribute("draft", "true"); 954 // } 955 parent.appendChild(childToImport); 956 } 957 } else { 958 Node replacement = replacementList[0]; 959 // remove all the children of the parent node 960 removeChildNodes(parent); 961 for (Node child = replacement.getFirstChild(); child != null; child = child.getNextSibling()) { 962 // found an element node in the aliased resource 963 // add to the source 964 Node childToImport = fullyResolvedDoc.importNode(child, true); 965 // if(isDraft==true && childToImport.getNodeType() == Node.ELEMENT_NODE){ 966 // ((Element)childToImport).setAttribute("draft", "true"); 967 // } 968 parent.appendChild(childToImport); 969 } 970 } 971 972 } else { 973 throw new IllegalArgumentException("Could not find node for xpath: " + path + 974 " in locale: " + source + 975 " from source locale: " + thisLocale); 976 977 } 978 } 979 return fullyResolvedDoc; 980 } 981 removeChildNodes(Node parent)982 private static void removeChildNodes(Node parent) { 983 Node[] children = toNodeArray(parent.getChildNodes()); 984 for (int j = 0; j < children.length; j++) { 985 parent.removeChild(children[j]); 986 } 987 } 988 989 // TODO add funtions for fetching legitimate children 990 // for ICU isParentDraft(Document fullyResolved, String xpath)991 public boolean isParentDraft(Document fullyResolved, String xpath) { 992 Node node = getNode(fullyResolved, xpath); 993 Node parentNode; 994 while ((parentNode = node.getParentNode()) != null) { 995 String draft = getAttributeValue(parentNode, LDMLConstants.DRAFT); 996 if (draft != null) { 997 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) { 998 return true; 999 } else { 1000 return false; 1001 } 1002 } 1003 } 1004 // the default value is false if none specified 1005 return false; 1006 } 1007 isNodeDraft(Node node)1008 public static boolean isNodeDraft(Node node) { 1009 String draft = getAttributeValue(node, LDMLConstants.DRAFT); 1010 if (draft != null) { 1011 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) { 1012 return true; 1013 } else { 1014 return false; 1015 } 1016 } 1017 return false; 1018 } 1019 isDraft(Node fullyResolved, StringBuffer xpath)1020 public static boolean isDraft(Node fullyResolved, StringBuffer xpath) { 1021 Node current = getNode(fullyResolved, xpath.toString()); 1022 String draft = null; 1023 while (current != null && current.getNodeType() == Node.ELEMENT_NODE) { 1024 draft = getAttributeValue(current, LDMLConstants.DRAFT); 1025 if (draft != null) { 1026 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) { 1027 return true; 1028 } else { 1029 return false; 1030 } 1031 } 1032 current = current.getParentNode(); 1033 } 1034 return false; 1035 } 1036 isSiblingDraft(Node root)1037 public static boolean isSiblingDraft(Node root) { 1038 Node current = root; 1039 String draft = null; 1040 while (current != null && current.getNodeType() == Node.ELEMENT_NODE) { 1041 draft = getAttributeValue(current, LDMLConstants.DRAFT); 1042 if (draft != null) { 1043 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) { 1044 return true; 1045 } else { 1046 return false; 1047 } 1048 } 1049 current = current.getNextSibling(); 1050 } 1051 return false; 1052 } 1053 appendAllAttributes(Node node, StringBuffer xpath)1054 public static void appendAllAttributes(Node node, StringBuffer xpath) { 1055 NamedNodeMap attr = node.getAttributes(); 1056 int len = attr.getLength(); 1057 if (len > 0) { 1058 for (int i = 0; i < len; i++) { 1059 Node item = attr.item(i); 1060 xpath.append("[@"); 1061 xpath.append(item.getNodeName()); 1062 xpath.append("='"); 1063 xpath.append(item.getNodeValue()); 1064 xpath.append("']"); 1065 } 1066 } 1067 } 1068 areChildNodesElements(Node node)1069 private static boolean areChildNodesElements(Node node) { 1070 NodeList list = node.getChildNodes(); 1071 for (int i = 0; i < list.getLength(); i++) { 1072 if (list.item(i).getNodeType() == Node.ELEMENT_NODE) { 1073 return true; 1074 } 1075 } 1076 return false; 1077 } 1078 areSiblingsOfNodeElements(Node node)1079 private static boolean areSiblingsOfNodeElements(Node node) { 1080 NodeList list = node.getParentNode().getChildNodes(); 1081 int count = 0; 1082 for (int i = 0; i < list.getLength(); i++) { 1083 Node item = list.item(i); 1084 if (item.getNodeType() == Node.ELEMENT_NODE) { 1085 count++; 1086 } 1087 // the first child node of type element of <ldml> should be <identity> 1088 // here we figure out if any additional elements are there 1089 if (count > 1) { 1090 return true; 1091 } 1092 } 1093 return false; 1094 } 1095 isLocaleAlias(Document doc)1096 public static boolean isLocaleAlias(Document doc) { 1097 return (getAliasNode(doc) != null); 1098 } 1099 getAliasNode(Document doc)1100 private static Node getAliasNode(Document doc) { 1101 NodeList elements = doc.getElementsByTagName(LDMLConstants.IDENTITY); 1102 if (elements.getLength() == 1) { 1103 Node id = elements.item(0); 1104 Node sib = id; 1105 while ((sib = sib.getNextSibling()) != null) { 1106 if (sib.getNodeType() != Node.ELEMENT_NODE) { 1107 continue; 1108 } 1109 if (sib.getNodeName().equals(LDMLConstants.ALIAS)) { 1110 return sib; 1111 } 1112 } 1113 } else { 1114 System.out.println("Error: elements returned more than 1 identity element!"); 1115 } 1116 return null; 1117 } 1118 1119 /** 1120 * Determines if the whole locale is marked draft. To accomplish this 1121 * the method traverses all leaf nodes to determine if all nodes are marked draft 1122 */ 1123 private static boolean seenElementsOtherThanIdentity = false; 1124 isLocaleDraft(Node node)1125 public static final boolean isLocaleDraft(Node node) { 1126 boolean isDraft = true; 1127 // fast path to check if <ldml> element is draft 1128 if (isNodeDraft(node) == true) { 1129 return true; 1130 } 1131 for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { 1132 if (child.getNodeType() != Node.ELEMENT_NODE) { 1133 continue; 1134 } 1135 String name = child.getNodeName(); 1136 // fast path to check if <ldml> element is draft 1137 if (name.equals(LDMLConstants.LDML) && isNodeDraft(child) == true) { 1138 return true; 1139 } 1140 if (name.equals(LDMLConstants.IDENTITY)) { 1141 seenElementsOtherThanIdentity = areSiblingsOfNodeElements(child); 1142 continue; 1143 } 1144 1145 if (child.hasChildNodes() && areChildNodesElements(child)) { 1146 isDraft = isLocaleDraft(child); 1147 } else { 1148 if (isNodeDraft(child) == false) { 1149 isDraft = false; 1150 } 1151 } 1152 if (isDraft == false) { 1153 break; 1154 } 1155 } 1156 if (!seenElementsOtherThanIdentity) { 1157 return false; 1158 } 1159 return isDraft; 1160 } 1161 1162 /** 1163 * Appends the attribute values that make differentiate 2 siblings 1164 * in LDML 1165 * 1166 * @param node 1167 * @param xpath 1168 * @deprecated - use version that takes StringBuilder instead 1169 */ 1170 @Deprecated appendXPathAttribute(Node node, StringBuffer xpath)1171 public static final void appendXPathAttribute(Node node, StringBuffer xpath) { 1172 appendXPathAttribute(node, xpath, false, false); 1173 } 1174 1175 /** 1176 * @deprecated 1177 */ 1178 @Deprecated appendXPathAttribute(Node node, StringBuffer xpath, boolean ignoreAlt, boolean ignoreDraft)1179 public static void appendXPathAttribute(Node node, StringBuffer xpath, boolean ignoreAlt, boolean ignoreDraft) { 1180 StringBuilder sb = new StringBuilder(xpath.toString()); 1181 appendXPathAttribute(node, sb, ignoreAlt, ignoreDraft); 1182 } 1183 appendXPathAttribute(Node node, StringBuilder xpath)1184 public static final void appendXPathAttribute(Node node, StringBuilder xpath) { 1185 appendXPathAttribute(node, xpath, false, false); 1186 } 1187 appendXPathAttribute(Node node, StringBuilder xpath, boolean ignoreAlt, boolean ignoreDraft)1188 public static void appendXPathAttribute(Node node, StringBuilder xpath, boolean ignoreAlt, boolean ignoreDraft) { 1189 boolean terminate = false; 1190 String val = getAttributeValue(node, LDMLConstants.TYPE); 1191 String and = "][";// " and "; 1192 boolean isStart = true; 1193 String name = node.getNodeName(); 1194 if (val != null && !name.equals(LDMLConstants.DEFAULT) && !name.equals(LDMLConstants.MS)) { 1195 if (!(val.equals("standard") && name.equals(LDMLConstants.PATTERN))) { 1196 1197 if (isStart) { 1198 xpath.append("["); 1199 isStart = false; 1200 } 1201 xpath.append("@type='"); 1202 xpath.append(val); 1203 xpath.append("'"); 1204 terminate = true; 1205 } 1206 } 1207 if (!ignoreAlt) { 1208 val = getAttributeValue(node, LDMLConstants.ALT); 1209 if (val != null) { 1210 if (isStart) { 1211 xpath.append("["); 1212 isStart = false; 1213 } else { 1214 xpath.append(and); 1215 } 1216 xpath.append("@alt='"); 1217 xpath.append(val); 1218 xpath.append("'"); 1219 terminate = true; 1220 } 1221 1222 } 1223 1224 if (!ignoreDraft) { 1225 val = getAttributeValue(node, LDMLConstants.DRAFT); 1226 if (val != null && !name.equals(LDMLConstants.LDML)) { 1227 if (isStart) { 1228 xpath.append("["); 1229 isStart = false; 1230 } else { 1231 xpath.append(and); 1232 } 1233 xpath.append("@draft='"); 1234 xpath.append(val); 1235 xpath.append("'"); 1236 terminate = true; 1237 } 1238 1239 } 1240 1241 val = getAttributeValue(node, LDMLConstants.KEY); 1242 if (val != null) { 1243 if (isStart) { 1244 xpath.append("["); 1245 isStart = false; 1246 } else { 1247 xpath.append(and); 1248 } 1249 xpath.append("@key='"); 1250 xpath.append(val); 1251 xpath.append("'"); 1252 terminate = true; 1253 } 1254 val = getAttributeValue(node, LDMLConstants.REGISTRY); 1255 if (val != null) { 1256 if (isStart) { 1257 xpath.append("["); 1258 isStart = false; 1259 } else { 1260 xpath.append(and); 1261 } 1262 xpath.append("@registry='"); 1263 xpath.append(val); 1264 xpath.append("'"); 1265 terminate = true; 1266 } 1267 val = getAttributeValue(node, LDMLConstants.ID); 1268 if (val != null) { 1269 if (isStart) { 1270 xpath.append("["); 1271 isStart = false; 1272 } else { 1273 xpath.append(and); 1274 } 1275 xpath.append("@id='"); 1276 xpath.append(val); 1277 xpath.append("'"); 1278 terminate = true; 1279 } 1280 if (terminate) { 1281 xpath.append("]"); 1282 } 1283 } 1284 1285 /** 1286 * Ascertains if the children of the given node are element 1287 * nodes. 1288 * 1289 * @param node 1290 * @return 1291 */ areChildrenElementNodes(Node node)1292 public static boolean areChildrenElementNodes(Node node) { 1293 NodeList list = node.getChildNodes(); 1294 for (int i = 0; i < list.getLength(); i++) { 1295 if (list.item(i).getNodeType() == Node.ELEMENT_NODE) { 1296 return true; 1297 } 1298 } 1299 return false; 1300 } 1301 getNodeListAsArray(Node doc, String xpath)1302 public static Node[] getNodeListAsArray(Node doc, String xpath) { 1303 try { 1304 NodeList list = XPathAPI_selectNodeList(doc, xpath); 1305 int length = list.getLength(); 1306 if (length > 0) { 1307 Node[] array = new Node[length]; 1308 for (int i = 0; i < length; i++) { 1309 array[i] = list.item(i); 1310 } 1311 return array; 1312 } 1313 return null; 1314 } catch (TransformerException ex) { 1315 throw new RuntimeException(ex); 1316 } 1317 } 1318 getChildNodeListAsArray(Node parent, boolean exceptAlias)1319 private static Object[] getChildNodeListAsArray(Node parent, boolean exceptAlias) { 1320 1321 NodeList list = parent.getChildNodes(); 1322 int length = list.getLength(); 1323 1324 List<Node> al = new ArrayList<Node>(); 1325 for (int i = 0; i < length; i++) { 1326 Node item = list.item(i); 1327 if (item.getNodeType() != Node.ELEMENT_NODE) { 1328 continue; 1329 } 1330 if (exceptAlias && item.getNodeName().equals(LDMLConstants.ALIAS)) { 1331 continue; 1332 } 1333 al.add(item); 1334 } 1335 return al.toArray(); 1336 1337 } 1338 toNodeArray(NodeList list)1339 public static Node[] toNodeArray(NodeList list) { 1340 int length = list.getLength(); 1341 if (length > 0) { 1342 Node[] array = new Node[length]; 1343 for (int i = 0; i < length; i++) { 1344 array[i] = list.item(i); 1345 } 1346 return array; 1347 } 1348 return null; 1349 } 1350 getElementsByTagName(Document doc, String tagName)1351 public static Node[] getElementsByTagName(Document doc, String tagName) { 1352 try { 1353 NodeList list = doc.getElementsByTagName(tagName); 1354 int length = list.getLength(); 1355 if (length > 0) { 1356 Node[] array = new Node[length]; 1357 for (int i = 0; i < length; i++) { 1358 array[i] = list.item(i); 1359 } 1360 return array; 1361 } 1362 return null; 1363 } catch (Exception ex) { 1364 throw new RuntimeException(ex); 1365 } 1366 } 1367 1368 /** 1369 * Fetches the list of nodes that match the given xpath 1370 * 1371 * @param doc 1372 * @param xpath 1373 * @return 1374 */ getNodeList(Document doc, String xpath)1375 public static NodeList getNodeList(Document doc, String xpath) { 1376 try { 1377 return XPathAPI_selectNodeList(doc, xpath); 1378 1379 } catch (TransformerException ex) { 1380 throw new RuntimeException(ex); 1381 } 1382 } 1383 isAlternate(Node node)1384 public static final boolean isAlternate(Node node) { 1385 NamedNodeMap attributes = node.getAttributes(); 1386 Node attr = attributes.getNamedItem(LDMLConstants.ALT); 1387 if (attr != null) { 1388 return true; 1389 } 1390 return false; 1391 } 1392 getNonAltNodeIfPossible(NodeList list)1393 private static final Node getNonAltNodeIfPossible(NodeList list) { 1394 // A nonalt node is one which .. does not have alternate 1395 // attribute set 1396 Node node = null; 1397 for (int i = 0; i < list.getLength(); i++) { 1398 node = list.item(i); 1399 if (/* !isDraft(node, xpath)&& */!isAlternate(node)) { 1400 return node; 1401 } 1402 } 1403 if (list.getLength() > 0) 1404 return list.item(0); // if all have alt=.... then return the first one 1405 return null; 1406 } 1407 getNonAltNodeLike(Node parent, Node child)1408 public static Node getNonAltNodeLike(Node parent, Node child) { 1409 StringBuffer childXpath = new StringBuffer(child.getNodeName()); 1410 appendXPathAttribute(child, childXpath, true/* ignore alt */, true/* ignore draft */); 1411 String childXPathString = childXpath.toString(); 1412 for (Node other = parent.getFirstChild(); other != null; other = other.getNextSibling()) { 1413 if ((other.getNodeType() != Node.ELEMENT_NODE) || (other == child)) { 1414 continue; 1415 } 1416 StringBuffer otherXpath = new StringBuffer(other.getNodeName()); 1417 appendXPathAttribute(other, otherXpath); 1418 // System.out.println("Compare: " + childXpath + " to " + otherXpath); 1419 if (childXPathString.equals(otherXpath.toString())) { 1420 // System.out.println("Match!"); 1421 return other; 1422 } 1423 } 1424 return null; 1425 } 1426 1427 /** 1428 * Fetches the node from the document that matches the given xpath. 1429 * The context namespace node is required if the xpath contains 1430 * namespace elments 1431 * 1432 * @param doc 1433 * @param xpath 1434 * @param namespaceNode 1435 * @return 1436 */ getNode(Document doc, String xpath, Node namespaceNode)1437 public static Node getNode(Document doc, String xpath, Node namespaceNode) { 1438 try { 1439 NodeList nl = XPathAPI_selectNodeList(doc, xpath, namespaceNode); 1440 int len = nl.getLength(); 1441 // TODO watch for attribute "alt" 1442 if (len > 1) { 1443 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath); 1444 } 1445 if (len == 0) { 1446 return null; 1447 } 1448 return nl.item(0); 1449 1450 } catch (TransformerException ex) { 1451 ex.printStackTrace(); 1452 throw new RuntimeException(ex); 1453 } 1454 } 1455 getNode(Node context, String resToFetch, Node namespaceNode)1456 public static Node getNode(Node context, String resToFetch, Node namespaceNode) { 1457 try { 1458 NodeList nl = XPathAPI_selectNodeList(context, "./" + resToFetch, namespaceNode); 1459 int len = nl.getLength(); 1460 // TODO watch for attribute "alt" 1461 if (len > 1) { 1462 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + resToFetch); 1463 } 1464 if (len == 0) { 1465 return null; 1466 } 1467 return nl.item(0); 1468 1469 } catch (TransformerException ex) { 1470 throw new RuntimeException(ex); 1471 } 1472 } 1473 1474 /** 1475 * Fetches the node from the document which matches the xpath 1476 * 1477 * @param node 1478 * @param xpath 1479 * @return 1480 */ getNode(Node node, String xpath)1481 public static Node getNode(Node node, String xpath) { 1482 try { 1483 NodeList nl = XPathAPI_selectNodeList(node, xpath); 1484 int len = nl.getLength(); 1485 // TODO watch for attribute "alt" 1486 if (len > 1) { 1487 // PN Node best = getNonAltNode(nl); 1488 Node best = getNonAltNodeIfPossible(nl); // PN 1489 if (best != null) { 1490 // System.err.println("Chose best node from " + xpath); 1491 return best; 1492 } 1493 /* else complain */ 1494 String all = ""; 1495 int i; 1496 for (i = 0; i < len; i++) { 1497 all = all + ", " + nl.item(i); 1498 } 1499 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath 1500 + " = " + all); 1501 } 1502 if (len == 0) { 1503 return null; 1504 } 1505 return nl.item(0); 1506 1507 } catch (TransformerException ex) { 1508 throw new RuntimeException(ex); 1509 } 1510 } 1511 getNode(Node node, String xpath, boolean preferDraft, boolean preferAlt)1512 public static Node getNode(Node node, String xpath, boolean preferDraft, boolean preferAlt) { 1513 try { 1514 NodeList nl = XPathAPI_selectNodeList(node, xpath); 1515 return getNode(nl, xpath, preferDraft, preferAlt); 1516 1517 } catch (TransformerException ex) { 1518 throw new RuntimeException(ex); 1519 } 1520 } 1521 getVettedNode(NodeList list, StringBuffer xpath, boolean ignoreDraft)1522 private static Node getVettedNode(NodeList list, StringBuffer xpath, boolean ignoreDraft) { 1523 // A vetted node is one which is not draft and does not have alternate 1524 // attribute set 1525 Node node = null; 1526 for (int i = 0; i < list.getLength(); i++) { 1527 node = list.item(i); 1528 if (isDraft(node, xpath) && !ignoreDraft) { 1529 continue; 1530 } 1531 if (isAlternate(node)) { 1532 continue; 1533 } 1534 return node; 1535 } 1536 return null; 1537 } 1538 getVettedNode(Document fullyResolvedDoc, Node parent, String childName, StringBuffer xpath, boolean ignoreDraft)1539 public static Node getVettedNode(Document fullyResolvedDoc, Node parent, String childName, StringBuffer xpath, 1540 boolean ignoreDraft) { 1541 NodeList list = getNodeList(parent, childName, fullyResolvedDoc, xpath.toString()); 1542 int oldLength = xpath.length(); 1543 Node ret = null; 1544 1545 if (list != null && list.getLength() > 0) { 1546 xpath.append("/"); 1547 xpath.append(childName); 1548 ret = getVettedNode(list, xpath, ignoreDraft); 1549 } 1550 xpath.setLength(oldLength); 1551 return ret; 1552 } 1553 getNode(NodeList nl, String xpath, boolean preferDraft, boolean preferAlt)1554 public static Node getNode(NodeList nl, String xpath, boolean preferDraft, boolean preferAlt) { 1555 int len = nl.getLength(); 1556 // TODO watch for attribute "alt" 1557 if (len > 1) { 1558 Node best = null; 1559 for (int i = 0; i < len; i++) { 1560 Node current = nl.item(i); 1561 if (!preferDraft && !preferAlt) { 1562 if (!isNodeDraft(current) && !isAlternate(current)) { 1563 best = current; 1564 break; 1565 } 1566 continue; 1567 } else if (preferDraft && !preferAlt) { 1568 if (isNodeDraft(current) && !isAlternate(current)) { 1569 best = current; 1570 break; 1571 } 1572 continue; 1573 } else if (!preferDraft && preferAlt) { 1574 if (!isNodeDraft(current) && isAlternate(current)) { 1575 best = current; 1576 break; 1577 } 1578 continue; 1579 } else { 1580 if (isNodeDraft(current) || isAlternate(current)) { 1581 best = current; 1582 break; 1583 } 1584 continue; 1585 } 1586 } 1587 if (best == null && preferDraft == true) { 1588 best = getVettedNode(nl, new StringBuffer(xpath), false); 1589 } 1590 if (best != null) { 1591 return best; 1592 } 1593 /* else complain */ 1594 String all = ""; 1595 int i; 1596 for (i = 0; i < len; i++) { 1597 all = all + ", " + nl.item(i); 1598 } 1599 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath + " = " 1600 + all); 1601 } 1602 if (len == 0) { 1603 return null; 1604 } 1605 return nl.item(0); 1606 1607 } 1608 1609 /** 1610 * 1611 * @param context 1612 * @param resToFetch 1613 * @param fullyResolved 1614 * @param xpath 1615 * @return 1616 */ getNode(Node context, String resToFetch, Document fullyResolved, String xpath)1617 public static Node getNode(Node context, String resToFetch, Document fullyResolved, String xpath) { 1618 String ctx = "./" + resToFetch; 1619 Node node = getNode(context, ctx); 1620 if (node == null && fullyResolved != null) { 1621 // try from fully resolved 1622 String path = xpath + "/" + resToFetch; 1623 node = getNode(fullyResolved, path); 1624 } 1625 return node; 1626 } 1627 1628 /** 1629 * 1630 * @param context 1631 * @param resToFetch 1632 * @return 1633 */ getChildNodes(Node context, String resToFetch)1634 public static NodeList getChildNodes(Node context, String resToFetch) { 1635 String ctx = "./" + resToFetch; 1636 NodeList list = getNodeList(context, ctx); 1637 return list; 1638 } 1639 1640 /** 1641 * Fetches the node from the document that matches the given xpath. 1642 * The context namespace node is required if the xpath contains 1643 * namespace elments 1644 * 1645 * @param doc 1646 * @param xpath 1647 * @param namespaceNode 1648 * @return 1649 */ getNodeList(Document doc, String xpath, Node namespaceNode)1650 public static NodeList getNodeList(Document doc, String xpath, Node namespaceNode) { 1651 try { 1652 NodeList nl = XPathAPI_selectNodeList(doc, xpath, namespaceNode); 1653 if (nl.getLength() == 0) { 1654 return null; 1655 } 1656 return nl; 1657 1658 } catch (TransformerException ex) { 1659 throw new RuntimeException(ex); 1660 } 1661 } 1662 1663 /** 1664 * Fetches the node from the document which matches the xpath 1665 * 1666 * @param node 1667 * @param xpath 1668 * @return 1669 */ getNodeList(Node node, String xpath)1670 public static NodeList getNodeList(Node node, String xpath) { 1671 try { 1672 NodeList nl = XPathAPI_selectNodeList(node, xpath); 1673 int len = nl.getLength(); 1674 if (len == 0) { 1675 return null; 1676 } 1677 return nl; 1678 } catch (TransformerException ex) { 1679 throw new RuntimeException(ex); 1680 } 1681 } 1682 1683 /** 1684 * Fetches node list from the children of the context node. 1685 * 1686 * @param context 1687 * @param resToFetch 1688 * @param fullyResolved 1689 * @param xpath 1690 * @return 1691 */ getNodeList(Node context, String resToFetch, Document fullyResolved, String xpath)1692 public static NodeList getNodeList(Node context, String resToFetch, Document fullyResolved, String xpath) { 1693 String ctx = "./" + resToFetch; 1694 NodeList list = getNodeList(context, ctx); 1695 if ((list == null || list.getLength() > 0) && fullyResolved != null) { 1696 // try from fully resolved 1697 String path = xpath + "/" + resToFetch; 1698 list = getNodeList(fullyResolved, path); 1699 } 1700 return list; 1701 } 1702 1703 /** 1704 * Decide if the node is text, and so must be handled specially 1705 * 1706 * @param n 1707 * @return 1708 */ getAttributeNode(Node sNode, String attribName)1709 public static Node getAttributeNode(Node sNode, String attribName) { 1710 NamedNodeMap attrs = sNode.getAttributes(); 1711 if (attrs != null) { 1712 return attrs.getNamedItem(attribName); 1713 } 1714 return null; 1715 } 1716 1717 /** 1718 * Utility method to fetch the attribute value from the given 1719 * element node 1720 * 1721 * @param sNode 1722 * @param attribName 1723 * @return 1724 */ getAttributeValue(Node sNode, String attribName)1725 public static String getAttributeValue(Node sNode, String attribName) { 1726 String value = null; 1727 NamedNodeMap attrs = sNode.getAttributes(); 1728 if (attrs != null) { 1729 Node attr = attrs.getNamedItem(attribName); 1730 if (attr != null) { 1731 value = attr.getNodeValue(); 1732 } 1733 } 1734 return value; 1735 } 1736 1737 /** 1738 * Utility method to set the attribute value on the given 1739 * element node 1740 * 1741 * @param sNode 1742 * @param attribName 1743 * @param val 1744 */ setAttributeValue(Node sNode, String attribName, String val)1745 public static void setAttributeValue(Node sNode, String attribName, String val) { 1746 1747 Node attr = sNode.getAttributes().getNamedItem(attribName); 1748 if (attr != null) { 1749 attr.setNodeValue(val); 1750 } else { 1751 attr = sNode.getOwnerDocument().createAttribute(attribName); 1752 attr.setNodeValue(val); 1753 sNode.getAttributes().setNamedItem(attr); 1754 } 1755 } 1756 1757 /** 1758 * Utility method to fetch the value of the element node 1759 * 1760 * @param node 1761 * @return 1762 */ getNodeValue(Node node)1763 public static String getNodeValue(Node node) { 1764 for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { 1765 if (child.getNodeType() == Node.TEXT_NODE) { 1766 return child.getNodeValue(); 1767 } 1768 } 1769 return null; 1770 } 1771 1772 /** 1773 * Parse & resolve file level alias 1774 */ parseAndResolveAlias(String filename, String locale, boolean ignoreError)1775 public static Document parseAndResolveAlias(String filename, String locale, boolean ignoreError) 1776 throws RuntimeException { 1777 // Force filerefs to be URI's if needed: note this is independent of any other files 1778 String docURI = filenameToURL(filename); 1779 Document doc = parse(new InputSource(docURI), filename, ignoreError); 1780 NodeList elements = doc.getElementsByTagName(LDMLConstants.IDENTITY); 1781 if (elements.getLength() == 1) { 1782 Node id = elements.item(0); 1783 Node sib = id; 1784 while ((sib = sib.getNextSibling()) != null) { 1785 if (sib.getNodeType() != Node.ELEMENT_NODE) { 1786 continue; 1787 } 1788 if (sib.getNodeName().equals(LDMLConstants.ALIAS)) { 1789 resolveAliases(doc, filename.substring(0, filename.lastIndexOf(File.separator) + 1), locale, false, 1790 null); 1791 } 1792 } 1793 } else { 1794 System.out.println("Error: elements returned more than 1 identity element!"); 1795 } 1796 if (DEBUG) { 1797 try { 1798 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter( 1799 new java.io.FileOutputStream("./" + File.separator + locale 1800 + "_debug_1.xml"), 1801 "UTF-8"); 1802 LDMLUtilities.printDOMTree(doc, new PrintWriter(writer), 1803 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null); 1804 writer.flush(); 1805 } catch (IOException e) { 1806 // throw the exceptionaway .. this is for debugging 1807 } 1808 } 1809 return doc; 1810 } 1811 1812 /** 1813 * Simple worker method to parse filename to a Document. 1814 * 1815 * Attempts XML parse, then HTML parse (when parser available), 1816 * then just parses as text and sticks into a text node. 1817 * 1818 * @param filename 1819 * to parse as a local path 1820 * 1821 * @return Document object with contents of the file; 1822 * otherwise throws an unchecked RuntimeException if there 1823 * is any fatal problem 1824 */ parse(String filename, boolean ignoreError)1825 public static Document parse(String filename, boolean ignoreError) throws RuntimeException { 1826 // Force filerefs to be URI's if needed: note this is independent of any other files 1827 String docURI = filenameToURL(filename); 1828 return parse(new InputSource(docURI), filename, ignoreError); 1829 } 1830 parseAndResolveAliases(String locale, String sourceDir, boolean ignoreError, boolean ignoreDraft)1831 public static Document parseAndResolveAliases(String locale, String sourceDir, boolean ignoreError, 1832 boolean ignoreDraft) { 1833 try { 1834 Document full = parse(sourceDir + File.separator + locale, ignoreError); 1835 if (full != null) { 1836 full = resolveAliases(full, sourceDir, locale, ignoreDraft, null); 1837 } 1838 /* 1839 * Debugging 1840 * 1841 * Node[] list = getNodeArray(full, LDMLConstants.ALIAS); 1842 * if(list.length>0){ 1843 * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length); 1844 * } 1845 */ 1846 return full; 1847 } catch (Exception ex) { 1848 if (!ignoreError) { 1849 ex.printStackTrace(); 1850 throw new RuntimeException(ex); 1851 } 1852 } 1853 return null; 1854 1855 } 1856 getNullErrorHandler(final String filename2, final boolean ignoreError)1857 private static ErrorHandler getNullErrorHandler(final String filename2, final boolean ignoreError) { 1858 // Local class: cheap non-printing ErrorHandler 1859 // This is used to suppress validation warnings 1860 ErrorHandler nullHandler = new ErrorHandler() { 1861 public void warning(SAXParseException e) throws SAXException { 1862 int col = e.getColumnNumber(); 1863 String msg = (filename2 + ":" + e.getLineNumber() 1864 + (col >= 0 ? ":" + col : "") + ": WARNING: " 1865 + e.getMessage()); 1866 1867 System.err.println(msg); 1868 if (!ignoreError) { 1869 throw new RuntimeException(msg); 1870 } 1871 } 1872 1873 public void error(SAXParseException e) throws SAXException { 1874 int col = e.getColumnNumber(); 1875 String msg = (filename2 + ":" + e.getLineNumber() 1876 + (col >= 0 ? ":" + col : "") + ": ERROR: " 1877 + e.getMessage()); 1878 System.err.println(msg); 1879 if (!ignoreError) { 1880 throw new RuntimeException(msg); 1881 } 1882 } 1883 1884 public void fatalError(SAXParseException e) throws SAXException { 1885 throw e; 1886 } 1887 }; 1888 return nullHandler; 1889 } 1890 newDocument()1891 public static Document newDocument() { 1892 return newDocumentBuilder(false).newDocument(); 1893 } 1894 newDocumentBuilder(boolean validating)1895 private static DocumentBuilder newDocumentBuilder(boolean validating) { 1896 try { 1897 DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); 1898 // Always set namespaces on 1899 dfactory.setNamespaceAware(true); 1900 dfactory.setValidating(validating); 1901 dfactory.setIgnoringComments(false); 1902 dfactory.setExpandEntityReferences(true); 1903 // Set other attributes here as needed 1904 // applyAttributes(dfactory, attributes); 1905 1906 DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); 1907 docBuilder.setEntityResolver(new CachingEntityResolver()); 1908 return docBuilder; 1909 } catch (Throwable se) { 1910 System.err.println(": ERROR : trying to create documentBuilder: " + se.getMessage()); 1911 se.printStackTrace(); 1912 throw new RuntimeException(se); 1913 } 1914 } 1915 parse(InputSource docSrc, String filename, boolean ignoreError)1916 public static Document parse(InputSource docSrc, String filename, boolean ignoreError) { 1917 Document doc = null; 1918 try { 1919 // First, attempt to parse as XML (preferred)... 1920 DocumentBuilder docBuilder = newDocumentBuilder(true); 1921 docBuilder.setErrorHandler(getNullErrorHandler(filename, ignoreError)); 1922 doc = docBuilder.parse(docSrc); 1923 } catch (Throwable se) { 1924 // ... if we couldn't parse as XML, attempt parse as HTML... 1925 System.err.println(filename + ": ERROR :" + se.getMessage()); 1926 se.printStackTrace(); 1927 if (!ignoreError) { 1928 throw new RuntimeException(se); 1929 } 1930 } 1931 return doc; 1932 } // end of parse() 1933 1934 /* 1935 * Utility method to translate a String filename to URL. 1936 * 1937 * Note: This method is not necessarily proven to get the 1938 * correct URL for every possible kind of filename; it should 1939 * be improved. It handles the most common cases that we've 1940 * encountered when running Conformance tests on Xalan. 1941 * Also note, this method does not handle other non-file: 1942 * flavors of URLs at all. 1943 * 1944 * If the name is null, return null. 1945 * If the name starts with a common URI scheme (namely the ones 1946 * found in the examples of RFC2396), then simply return the 1947 * name as-is (the assumption is that it's already a URL) 1948 * Otherwise we attempt (cheaply) to convert to a file:/// URL. 1949 */ filenameToURL(String filename)1950 public static String filenameToURL(String filename) { 1951 // null begets null - something like the commutative property 1952 if (null == filename) { 1953 return null; 1954 } 1955 1956 // Don't translate a string that already looks like a URL 1957 if (filename.startsWith("file:") 1958 || filename.startsWith("http:") 1959 || filename.startsWith("ftp:") 1960 || filename.startsWith("gopher:") 1961 || filename.startsWith("mailto:") 1962 || filename.startsWith("news:") 1963 || filename.startsWith("telnet:")) { 1964 return filename; 1965 } 1966 1967 File f = new File(filename); 1968 String tmp = null; 1969 try { 1970 // This normally gives a better path 1971 tmp = f.getCanonicalPath(); 1972 } catch (IOException ioe) { 1973 // But this can be used as a backup, for cases 1974 // where the file does not exist, etc. 1975 tmp = f.getAbsolutePath(); 1976 } 1977 1978 // URLs must explicitly use only forward slashes 1979 if (File.separatorChar == '\\') { 1980 tmp = tmp.replace('\\', '/'); 1981 } 1982 // Note the presumption that it's a file reference 1983 // Ensure we have the correct number of slashes at the 1984 // start: we always want 3 /// if it's absolute 1985 // (which we should have forced above) 1986 if (tmp.startsWith("/")) { 1987 return "file://" + tmp; 1988 } else { 1989 return "file:///" + tmp; 1990 } 1991 } 1992 1993 /** 1994 * Debugging method for printing out the DOM Tree 1995 * Prints the specified node, recursively. 1996 * 1997 * @param node 1998 * @param out 1999 * @throws IOException 2000 */ printDOMTree(Node node, PrintWriter out, String docType, String copyright)2001 public static void printDOMTree(Node node, PrintWriter out, String docType, String copyright) throws IOException { 2002 try { 2003 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 2004 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 2005 transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 2006 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 2007 2008 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 2009 out.print("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); 2010 if (copyright != null) { 2011 out.print(copyright); 2012 } 2013 if (docType != null) { 2014 out.print(docType); 2015 } 2016 2017 // transformer.setParameter(entityName, entityRef ); 2018 // transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, XLIFF_PUBLIC_NAME); 2019 transformer.transform(new DOMSource(node), new StreamResult(out)); 2020 } catch (TransformerException te) { 2021 throw new IOException(te.getMessage()); 2022 } 2023 2024 // out.close(); 2025 } // printDOMTree(Node, PrintWriter) 2026 2027 // Utility functions, HTML and such. 2028 public static String CVSBASE = "http://www.unicode.org/cldr/trac/browser/trunk"; 2029 getCVSLink(String locale)2030 public static final String getCVSLink(String locale) { 2031 return "<a href=\"" + CVSBASE + "/common/main/" + locale + ".xml\">"; 2032 } 2033 getCVSLink(String locale, String version)2034 public static final String getCVSLink(String locale, String version) { 2035 return "<a href=\"" + CVSBASE + "/common/main/" + locale + ".xml?rev=" + 2036 version + "\">"; 2037 2038 } 2039 2040 /** 2041 * Load the revision from CVS or from the Identity element. 2042 * 2043 * @param fileName 2044 * @return 2045 */ loadFileRevision(String fileName)2046 static public String loadFileRevision(String fileName) { 2047 int index = fileName.lastIndexOf(File.separatorChar); 2048 if (index == -1) { 2049 return null; 2050 } 2051 String sourceDir = fileName.substring(0, index); 2052 return loadFileRevision(sourceDir, new File(fileName).getName()); 2053 } 2054 2055 // //ldml[@version="1.7"]/identity/version[@number="$Revision: 13987 $"] 2056 // private static Pattern VERSION_PATTERN = 2057 // PatternCache.get("//ldml[^/]*/identity/version\\[@number=\"[^0-9]*\\([0-9.]+\\).*"); 2058 private static Pattern VERSION_PATTERN = PatternCache.get(".*identity/version.*Revision[: ]*([0-9.]*).*"); 2059 2060 /** 2061 * Load the revision from CVS or from the Identity element. 2062 * 2063 * @param sourceDir 2064 * @param fileName 2065 * @return 2066 */ loadFileRevision(String sourceDir, String fileName)2067 static public String loadFileRevision(String sourceDir, String fileName) { 2068 String aVersion = null; 2069 File entriesFile = new File(sourceDir + File.separatorChar + "CVS", "Entries"); 2070 if (entriesFile.exists() && entriesFile.canRead()) { 2071 try { 2072 BufferedReader r = new BufferedReader(new FileReader(entriesFile.getPath())); 2073 String s; 2074 while ((s = r.readLine()) != null) { 2075 String lookFor = "/" + fileName + "/"; 2076 if (s.startsWith(lookFor)) { 2077 String ver = s.substring(lookFor.length()); 2078 ver = ver.substring(0, ver.indexOf('/')); 2079 aVersion = ver; 2080 } 2081 } 2082 r.close(); 2083 } catch (Throwable th) { 2084 System.err.println(th.toString() + " trying to read CVS Entries file " + entriesFile.getPath()); 2085 return null; 2086 } 2087 } else { 2088 // no CVS, use file ident. 2089 File xmlFile = new File(sourceDir, fileName); 2090 if (!xmlFile.exists()) return null; 2091 final String bVersion[] = { "unknown" }; 2092 try { 2093 XMLFileReader xfr = new XMLFileReader().setHandler(new SimpleHandler() { 2094 private boolean done = false; 2095 2096 // public void handleAttributeDecl(String eName, String aName, String type, String mode, String 2097 // value) { 2098 public void handlePathValue(String p, String v) { 2099 if (!done) { 2100 Matcher m = VERSION_PATTERN.matcher(p); 2101 if (m.matches()) { 2102 // System.err.println("Matches! "+p+" = "+m.group(1)); 2103 bVersion[0] = m.group(1); 2104 done = true; 2105 } 2106 } 2107 } 2108 }); 2109 xfr.read(xmlFile.getPath(), -1, true); 2110 aVersion = bVersion[0]; // copy from input param 2111 } catch (Throwable t) { 2112 t.printStackTrace(); 2113 aVersion = "err"; 2114 System.err.println("Error reading version of " + xmlFile.getAbsolutePath() + ": " + t.toString()); 2115 } 2116 } 2117 // System.err.println("v="+aVersion); 2118 return aVersion; 2119 } 2120 2121 // // Caching Resolution 2122 // private static File getCacheName(String sourceDir, String last, String loc) 2123 // { 2124 // //File xCacheDir = new 2125 // File((CachingEntityResolver.getCacheDir()!=null)?CachingEntityResolver.getCacheDir():"/tmp/cldrres"); 2126 // return new File(sourceDir).getName() + "_" + last + "." + loc; 2127 // } 2128 2129 // Document readMergeCache(String sourceDir, String last, String loc) 2130 // { 2131 // File cacheName = getCacheName(String sourceDir, last, loc); 2132 // System.out.println(" M: " + cacheName); 2133 // File cacheFile = new File(xCacheDir, cacheName + ".xml"); 2134 // if(cacheFile.exists()) { // && is newer than last, loc 2135 // doc = parse(cacheFile.getPath(),ignoreUnavailable); 2136 // } 2137 // if(doc!=null) { 2138 // System.out.println("Cache hit for " + cacheName); 2139 // } 2140 // return doc; 2141 // } 2142 2143 // void writeMergeCache(String sourceDir, String last, String loc, Document full) 2144 // { 2145 // try { 2146 // OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(cacheFile),"UTF-8"); 2147 // PrintWriter pw = new PrintWriter(writer); 2148 // printDOMTree(full,pw); 2149 // long stop = System.currentTimeMillis(); 2150 // long total = (stop-start); 2151 // double s = total/1000.; 2152 // System.out.println(" " + cacheName + " parse time: " + s + "s"); 2153 // pw.println("<!-- " + cacheName + " parse time: " + s + "s -->"); 2154 // writer.close(); 2155 // } catch (Throwable t) { 2156 // System.err.println(t.toString() + " while trying to write cache file " + cacheName); 2157 // } 2158 // } 2159 getFullPath(int fileType, String fName, String dir)2160 public static String getFullPath(int fileType, String fName, String dir) { 2161 String str = null; 2162 int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1/* add 1 to skip past the separator */; 2163 int lastIndex2 = fName.lastIndexOf('.', fName.length()); 2164 if (fileType == TXT) { 2165 if (lastIndex2 == -1) { 2166 fName = fName.trim() + ".txt"; 2167 } else { 2168 if (!fName.substring(lastIndex2).equalsIgnoreCase(".txt")) { 2169 fName = fName.substring(lastIndex1, lastIndex2) + ".txt"; 2170 } 2171 } 2172 if (dir != null && fName != null) { 2173 str = dir + "/" + fName.trim(); 2174 } else { 2175 str = System.getProperty("user.dir") + "/" + fName.trim(); 2176 } 2177 } else if (fileType == XML) { 2178 if (lastIndex2 == -1) { 2179 fName = fName.trim() + ".xml"; 2180 } else { 2181 if (!fName.substring(lastIndex2).equalsIgnoreCase(".xml") 2182 && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")) { 2183 fName = fName.substring(lastIndex1, lastIndex2) + ".xml"; 2184 } 2185 } 2186 if (dir != null && fName != null) { 2187 str = dir + "/" + fName; 2188 } else if (lastIndex1 > 0) { 2189 str = fName; 2190 } else { 2191 str = System.getProperty("user.dir") + "/" + fName; 2192 } 2193 } else { 2194 System.err.println("Invalid file type."); 2195 System.exit(-1); 2196 } 2197 return str; 2198 } 2199 2200 /** 2201 * split an alt= tag into pieces. Any piece can be missing (== null) 2202 * Piece 0: 'alt type'. null means this is the normal (non-alt) item. Possible values are 'alternate', 'colloquial', 2203 * etc. 2204 * Piece 1: 'proposed type'. If non-null, this is a string beginning with 'proposed' and containing arbitrary other 2205 * text. 2206 * 2207 * STRING 0 1 2208 * ------------------------------- 2209 * ""/null null null 2210 * something something null 2211 * something-proposed something proposed 2212 * something-proposed3 something proposed3 2213 * proposed null proposed 2214 * somethingproposed somethingproposed null 2215 * 2216 * 2217 * @param alt 2218 * the alt tag to parse 2219 * @return a 2-element array containing piece 0 and piece 1 2220 * @see formatAlt 2221 */ parseAlt(String alt)2222 public static String[] parseAlt(String alt) { 2223 String[] ret = new String[2]; 2224 if (alt == null) { 2225 ret[0] = null; 2226 ret[1] = null; 2227 } else { 2228 int l = alt.indexOf(LDMLConstants.PROPOSED); 2229 if (l == -1) { /* no PROPOSED */ 2230 ret[0] = alt; // all alt, 2231 ret[1] = null; // no kind 2232 } else if (l == 0) { /* begins with */ 2233 ret[0] = null; // all properties 2234 ret[1] = alt; 2235 } else { 2236 if (alt.charAt(l - 1) != '-') { 2237 throw new InternalError("Expected '-' before " + LDMLConstants.PROPOSED + " in " + alt); 2238 } 2239 ret[0] = alt.substring(0, l - 1); 2240 ret[1] = alt.substring(l); 2241 2242 if (ret[0].length() == 0) { 2243 ret[0] = null; 2244 } 2245 if (ret[1].length() == 0) { 2246 ret[1] = null; 2247 } 2248 } 2249 } 2250 return ret; 2251 } 2252 2253 /** 2254 * Format aan alt string from components. 2255 * 2256 * @param altType 2257 * optional alternate type (i.e. 'alternate' or 'colloquial'). 2258 * @param proposedType 2259 * @see parseAlt 2260 */ formatAlt(String altType, String proposedType)2261 public static String formatAlt(String altType, String proposedType) { 2262 2263 if (((altType == null) || (altType.length() == 0)) && 2264 ((proposedType == null) || (proposedType.length() == 0))) { 2265 return null; 2266 } 2267 2268 if ((proposedType == null) || (proposedType.length() == 0)) { 2269 return altType; // no proposed type: 'alternate' 2270 } else if (!proposedType.startsWith(LDMLConstants.PROPOSED)) { 2271 throw new InternalError("proposedType must begin with " + LDMLConstants.PROPOSED); 2272 } 2273 2274 if ((altType == null) || (altType.length() == 0)) { 2275 return proposedType; // Just a proposed type: "proposed" or "proposed-3" 2276 } else { 2277 return altType + "-" + proposedType; // 'alternate-proposed' 2278 } 2279 } 2280 2281 /** 2282 * Compatibility. 2283 * 2284 * @param node 2285 * @param xpath 2286 * @return 2287 * @throws TransformerException 2288 */ XPathAPI_selectNodeList(Node node, String xpath)2289 private static NodeList XPathAPI_selectNodeList(Node node, String xpath) throws TransformerException { 2290 XPathFactory factory = XPathFactory.newInstance(); 2291 XPath xPath = factory.newXPath(); 2292 setNamespace(xPath, xpath); 2293 try { 2294 XPathExpression xPathExpression = xPath.compile(xpath); 2295 return (NodeList) xPathExpression.evaluate(node, XPathConstants.NODESET); 2296 } catch (XPathExpressionException e) { 2297 throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e); 2298 } 2299 } 2300 XPathAPI_selectNodeList(Document doc, String xpath, Node namespaceNode)2301 private static NodeList XPathAPI_selectNodeList(Document doc, String xpath, 2302 Node namespaceNode) throws TransformerException { 2303 XPathFactory factory = XPathFactory.newInstance(); 2304 XPath xPath = factory.newXPath(); 2305 setNamespace(xPath, xpath); 2306 try { 2307 XPathExpression xPathExpression = xPath.compile(xpath); 2308 return (NodeList) xPathExpression.evaluate(doc, XPathConstants.NODESET); 2309 } catch (XPathExpressionException e) { 2310 throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e); 2311 } 2312 } 2313 XPathAPI_selectNodeList(Node context, String xpath, Node namespaceNode)2314 private static NodeList XPathAPI_selectNodeList(Node context, String xpath, 2315 Node namespaceNode) throws TransformerException { 2316 XPathFactory factory = XPathFactory.newInstance(); 2317 XPath xPath = factory.newXPath(); 2318 setNamespace(xPath, xpath); 2319 try { 2320 XPathExpression xPathExpression = xPath.compile(xpath); 2321 return (NodeList) xPathExpression.evaluate(context, XPathConstants.NODESET); 2322 } catch (XPathExpressionException e) { 2323 throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e); 2324 } 2325 } 2326 XPathAPI_eval(Node context, String string, Node namespaceNode)2327 private static void XPathAPI_eval(Node context, String string, 2328 Node namespaceNode) throws TransformerException { 2329 XPathAPI_selectNodeList(context, string, namespaceNode); 2330 } 2331 XPathAPI_eval(Node context, String string)2332 private static void XPathAPI_eval(Node context, String string) throws TransformerException { 2333 XPathAPI_selectNodeList(context, string); 2334 } 2335 setNamespace(XPath xpath, String string)2336 private static void setNamespace(XPath xpath, String string) { 2337 if (string.contains("special/icu:")) { 2338 xpath.setNamespaceContext(new javax.xml.namespace.NamespaceContext() { 2339 public String getNamespaceURI(String prefix) { 2340 if (prefix.equals("icu")) { 2341 return "http://www.icu-project.org"; 2342 } 2343 return null; 2344 } 2345 2346 public String getPrefix(String namespaceURI) { 2347 return null; 2348 } 2349 2350 public Iterator<Object> getPrefixes(String namespaceURI) { 2351 return null; 2352 } 2353 }); 2354 } 2355 } 2356 } 2357