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