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