1 package org.unicode.cldr.util; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.nio.charset.Charset; 9 import java.util.ArrayList; 10 import java.util.Arrays; 11 import java.util.HashSet; 12 import java.util.List; 13 import java.util.Map; 14 import java.util.Map.Entry; 15 import java.util.Objects; 16 import java.util.Set; 17 import java.util.TreeMap; 18 import java.util.regex.Matcher; 19 import java.util.regex.Pattern; 20 21 import org.unicode.cldr.util.CLDRFile.DraftStatus; 22 import org.unicode.cldr.util.XMLFileReader.AllHandler; 23 import org.xml.sax.Attributes; 24 import org.xml.sax.Locator; 25 import org.xml.sax.SAXException; 26 import org.xml.sax.SAXParseException; 27 28 import com.google.common.cache.CacheBuilder; 29 import com.google.common.cache.CacheLoader; 30 import com.google.common.cache.LoadingCache; 31 import com.google.common.collect.ImmutableSet; 32 import com.ibm.icu.impl.Utility; 33 import com.ibm.icu.text.UnicodeSet; 34 import com.ibm.icu.util.ICUUncheckedIOException; 35 import com.ibm.icu.util.VersionInfo; 36 37 /** 38 * Loading Normalized XMLSource 39 */ 40 public class XMLNormalizingLoader{ 41 42 private static final int CACHE_LIMIT = 700; 43 private static LoadingCache<XMLSourceCacheKey, XMLSource> cache = CacheBuilder.newBuilder() 44 .maximumSize(CACHE_LIMIT) 45 .softValues() // will garbage-collected in LRU manner in response to memory demand 46 .build( 47 new CacheLoader<XMLSourceCacheKey, XMLSource>() { 48 @Override 49 public XMLSource load(XMLSourceCacheKey key) { 50 return makeXMLSource(key); 51 } 52 }); 53 54 private static final boolean LOG_PROGRESS = false; 55 private static final boolean DEBUG = false; 56 enum SupplementalStatus { 57 NEVER_SET, IS_SUMPPLEMENTAL, NOT_SUPPLEMENTAL 58 }; 59 60 private static class XMLSourceCacheKey { 61 private final String localeId; 62 private final Set<File> dirs; 63 private final DraftStatus minimalDraftStatus; 64 private final int hashCode; XMLSourceCacheKey(String localeId, List<File> dirs, DraftStatus minimalDraftStatus)65 public XMLSourceCacheKey(String localeId, List<File> dirs, DraftStatus minimalDraftStatus) { 66 this.localeId = localeId; 67 // Parameter check: the directory/file supplied must be non-null and readable. 68 if (dirs == null || dirs.isEmpty()) { 69 throw new ICUUncheckedIOException("Attempt to create a XMLSourceCacheKey with a null directory, please supply a non-null one."); 70 } 71 ImmutableSet.Builder<File> _dirs = ImmutableSet.builder(); 72 for (File dir : dirs) { 73 if (!dir.canRead()) { 74 throw new ICUUncheckedIOException("The directory specified, " + dir.getPath() + ", cannot be read"); 75 } 76 _dirs.add(dir); 77 } 78 this.dirs = _dirs.build(); 79 this.minimalDraftStatus = minimalDraftStatus; 80 this.hashCode = Objects.hash(this.localeId, this.dirs, this.minimalDraftStatus); 81 } 82 83 @Override hashCode()84 public int hashCode() { 85 return hashCode; 86 } 87 88 @Override equals(Object obj)89 public boolean equals(Object obj) { 90 if (this == obj) { 91 return true; 92 } 93 if (obj == null) { 94 return false; 95 } 96 if (getClass() != obj.getClass()) { 97 return false; 98 } 99 XMLSourceCacheKey other = (XMLSourceCacheKey) obj; 100 if(hashCode != other.hashCode) { 101 return false; 102 } 103 if (!Objects.equals(dirs, other.dirs)) { 104 return false; 105 } 106 if (minimalDraftStatus != other.minimalDraftStatus) { 107 return false; 108 } 109 if (!Objects.equals(localeId, other.localeId)) { 110 return false; 111 } 112 return true; 113 } 114 } 115 getFrozenInstance(String localeId, List<File> dirs, DraftStatus minimalDraftStatus)116 public static XMLSource getFrozenInstance(String localeId, List<File> dirs, DraftStatus minimalDraftStatus) { 117 XMLSourceCacheKey key = new XMLSourceCacheKey(localeId, dirs, minimalDraftStatus); 118 return cache.getUnchecked(key); 119 } 120 makeXMLSource(XMLSourceCacheKey key)121 private static XMLSource makeXMLSource(XMLSourceCacheKey key) { 122 XMLSource source = null; 123 if (key.dirs.size() == 1) { 124 File file = new File(key.dirs.iterator().next(), key.localeId + ".xml"); 125 source = loadXMLFile(file, key.localeId, key.minimalDraftStatus); 126 source.freeze(); 127 return source; 128 } 129 130 // if contains more than one file, make XMLSource from each file and then combine them to a combined XMLSource, 131 // so that can cache single file XMLSource as well as combined XMLSource 132 List<XMLSource> list = new ArrayList<>(); 133 List<File> dirList = new ArrayList<>(); 134 for (File dir: key.dirs) { 135 dirList.clear(); 136 dirList.add(dir); 137 XMLSourceCacheKey singleKey = new XMLSourceCacheKey(key.localeId, dirList, key.minimalDraftStatus); 138 XMLSource singleSource = cache.getUnchecked(singleKey); 139 list.add(singleSource); 140 } 141 142 source = list.get(0).cloneAsThawed(); 143 for (int i = 1; i < list.size(); i++) { 144 XMLSource other = list.get(i); 145 source.putAll(other, 0); // 0 --> merge_keep_mine 146 source.getXpathComments().joinAll(other.getXpathComments()); 147 } 148 source.freeze(); 149 return source; 150 } 151 loadXMLFile(File f, String localeId, DraftStatus minimalDraftStatus)152 public static XMLSource loadXMLFile(File f, String localeId, DraftStatus minimalDraftStatus) { 153 // use try-with-resources statement 154 try ( 155 InputStream fis = new StripUTF8BOMInputStream(new FileInputStream(f)); 156 InputStreamReader reader = new InputStreamReader(fis, Charset.forName("UTF-8")) 157 ) { 158 String fullFileName = f.getCanonicalPath(); 159 XMLSource source = new SimpleXMLSource(localeId); 160 XMLNormalizingHandler XML_HANDLER = new XMLNormalizingHandler(source, minimalDraftStatus); 161 XMLFileReader.read(fullFileName, reader, -1, true, XML_HANDLER); 162 if (XML_HANDLER.supplementalStatus == SupplementalStatus.NEVER_SET) { 163 throw new IllegalArgumentException("root of file must be either ldml or supplementalData"); 164 } 165 source.setNonInheriting(XML_HANDLER.supplementalStatus == SupplementalStatus.NOT_SUPPLEMENTAL); 166 if (XML_HANDLER.overrideCount > 0) { 167 throw new IllegalArgumentException("Internal problems: either data file has duplicate path, or" + 168 " CLDRFile.isDistinguishing() or CLDRFile.isOrdered() need updating: " 169 + XML_HANDLER.overrideCount 170 + "; The exact problems are printed on the console above."); 171 } 172 return source; 173 } catch (IOException e) { 174 throw new ICUUncheckedIOException("Cannot read the file " + f, e); 175 } 176 } 177 178 private static class XMLNormalizingHandler implements AllHandler { 179 private DraftStatus minimalDraftStatus; 180 private static final boolean SHOW_START_END = false; 181 private int commentStackIndex; 182 private boolean justPopped = false; 183 private String lastChars = ""; 184 private StringBuilder currentFullXPathSb = new StringBuilder("/"); 185 private String comment = null; 186 private Map<String, String> attributeOrder; 187 private DtdData dtdData; 188 private XMLSource source; 189 private String lastActiveLeafNode; 190 private String lastLeafNode; 191 private SupplementalStatus supplementalStatus = SupplementalStatus.NEVER_SET; 192 private final static int MAX_DEPTH = 30; // just make deep enough to handle any CLDR file. 193 // orderedCounter, orderedString, and level logically form a single class that allows adding elements, but never removed. 194 private int[] orderedCounter = new int[MAX_DEPTH]; 195 private String[] orderedString = new String[MAX_DEPTH]; 196 private int level = 0; 197 private int overrideCount = 0; 198 // Types which changed from 'type' to 'choice', but not in supplemental data. 199 private static final Set<String> CHANGED_TYPES = new HashSet<>(Arrays.asList(new String[] { 200 "abbreviationFallback", 201 "default", "mapping", "measurementSystem", "preferenceOrdering" })); 202 private static final Pattern DRAFT_PATTERN = PatternCache.get("\\[@draft=\"([^\"]*)\"\\]"); 203 private static final Pattern WHITESPACE_WITH_LF = PatternCache.get("\\s*\\u000a\\s*"); 204 private Matcher draftMatcher = DRAFT_PATTERN.matcher(""); 205 private Matcher whitespaceWithLf = WHITESPACE_WITH_LF.matcher(""); 206 private static final UnicodeSet CONTROLS = new UnicodeSet("[:cc:]").freeze(); 207 private static final UnicodeSet WHITESPACE = new UnicodeSet("[:whitespace:]").freeze(); 208 XMLNormalizingHandler(XMLSource source, DraftStatus minimalDraftStatus)209 XMLNormalizingHandler(XMLSource source, DraftStatus minimalDraftStatus) { 210 this.source = source; 211 this.minimalDraftStatus = minimalDraftStatus; 212 } 213 show(Attributes attributes)214 private String show(Attributes attributes) { 215 if (attributes == null) return "null"; 216 StringBuilder result = new StringBuilder(); 217 for (int i = 0; i < attributes.getLength(); ++i) { 218 String attribute = attributes.getQName(i); 219 String value = attributes.getValue(i); 220 result.append( "[@" + attribute + "=\"" + value + "\"]"); // TODO quote the value?? 221 } 222 return result.toString(); 223 } 224 push(String qName, Attributes attributes)225 private void push(String qName, Attributes attributes) { 226 Log.logln(LOG_PROGRESS, "push\t" + qName + "\t" + show(attributes)); 227 ++level; 228 if (!qName.equals(orderedString[level])) { 229 orderedString[level] = qName; 230 } 231 if (lastChars.length() != 0) { 232 if (WHITESPACE.containsAll(lastChars)) 233 lastChars = ""; 234 else 235 throw new IllegalArgumentException("Must not have mixed content: " + qName + ", " 236 + show(attributes) + ", Content: " + lastChars); 237 } 238 239 currentFullXPathSb.append("/" + qName); 240 if (dtdData.isOrdered(qName)) { 241 currentFullXPathSb.append(orderingAttribute()); 242 } 243 if (attributes.getLength() > 0) { 244 attributeOrder.clear(); 245 for (int i = 0; i < attributes.getLength(); ++i) { 246 String attribute = attributes.getQName(i); 247 String value = attributes.getValue(i); 248 249 if (attribute.equals("cldrVersion") 250 && (qName.equals("version"))) { 251 ((SimpleXMLSource) source).setDtdVersionInfo(VersionInfo.getInstance(value)); 252 } else { 253 putAndFixDeprecatedAttribute(qName, attribute, value); 254 } 255 } 256 for (Entry<String, String> entry : attributeOrder.entrySet()) { 257 String attribute = entry.getKey(); 258 String value = entry.getValue(); 259 String both = "[@" + attribute + "=\"" + value + "\"]"; // TODO quote the value?? 260 currentFullXPathSb.append(both); 261 } 262 } 263 if (comment != null) { 264 String currentFullXPath = currentFullXPathSb.toString(); 265 if (currentFullXPath.equals("//ldml") || currentFullXPath.equals("//supplementalData")) { 266 source.setInitialComment(comment); 267 } else { 268 source.addComment(currentFullXPath, comment, XPathParts.Comments.CommentType.PREBLOCK); 269 } 270 comment = null; 271 } 272 justPopped = false; 273 lastActiveLeafNode = null; 274 Log.logln(LOG_PROGRESS, "currentFullXPath\t" + currentFullXPathSb.toString()); 275 } 276 277 orderingAttribute()278 private String orderingAttribute() { 279 return "[@_q=\"" + (orderedCounter[level]++) + "\"]"; 280 } 281 putAndFixDeprecatedAttribute(String element, String attribute, String value)282 private void putAndFixDeprecatedAttribute(String element, String attribute, String value) { 283 if (attribute.equals("draft")) { 284 if (value.equals("true")) { 285 value = "approved"; 286 } 287 else if (value.equals("false")) { 288 value = "unconfirmed"; 289 } 290 } else if (attribute.equals("type")) { 291 if (CHANGED_TYPES.contains(element) && supplementalStatus!= SupplementalStatus.NOT_SUPPLEMENTAL) { // measurementSystem for example did not 292 // change from 'type' to 'choice'. 293 attribute = "choice"; 294 } 295 } 296 297 attributeOrder.put(attribute, value); 298 } 299 300 /** 301 * Adds a parsed XPath to the CLDRFile. 302 * 303 * @param fullXPath 304 * @param value 305 */ addPath(String fullXPath, String value)306 private void addPath(String fullXPath, String value) { 307 String former = source.getValueAtPath(fullXPath); 308 if (former != null) { 309 String formerPath = source.getFullXPath(fullXPath); 310 if (!former.equals(value) || !fullXPath.equals(formerPath)) { 311 if (!fullXPath.startsWith("//ldml/identity/version") && !fullXPath.startsWith("//ldml/identity/generation")) { 312 warnOnOverride(former, formerPath); 313 } 314 } 315 } 316 value = trimWhitespaceSpecial(value); 317 source.add(fullXPath, value); 318 } 319 pop(String qName)320 private void pop(String qName) { 321 Log.logln(LOG_PROGRESS, "pop\t" + qName); 322 --level; 323 String currentFullXPath = currentFullXPathSb.toString(); 324 if (!lastChars.isEmpty() || justPopped == false) { 325 boolean acceptItem = minimalDraftStatus == DraftStatus.unconfirmed; 326 if (!acceptItem) { 327 if (draftMatcher.reset(currentFullXPath).find()) { 328 DraftStatus foundStatus = DraftStatus.valueOf(draftMatcher.group(1)); 329 if (minimalDraftStatus.compareTo(foundStatus) <= 0) { 330 // what we found is greater than or equal to our status 331 acceptItem = true; 332 } 333 } else { 334 acceptItem = true; // if not found, then the draft status is approved, so it is always ok 335 } 336 } 337 if (acceptItem) { 338 // Change any deprecated orientation attributes into values 339 // for backwards compatibility. 340 boolean skipAdd = false; 341 if (currentFullXPath.startsWith("//ldml/layout/orientation")) { 342 XPathParts parts = XPathParts.getFrozenInstance(currentFullXPath); 343 String value = parts.getAttributeValue(-1, "characters"); 344 if (value != null) { 345 addPath("//ldml/layout/orientation/characterOrder", value); 346 skipAdd = true; 347 } 348 value = parts.getAttributeValue(-1, "lines"); 349 if (value != null) { 350 addPath("//ldml/layout/orientation/lineOrder", value); 351 skipAdd = true; 352 } 353 } 354 if (!skipAdd) { 355 addPath(currentFullXPath, lastChars); 356 } 357 lastLeafNode = lastActiveLeafNode = currentFullXPath; 358 } 359 lastChars = ""; 360 } else { 361 Log.logln(LOG_PROGRESS && lastActiveLeafNode != null, "pop: zeroing last leafNode: " 362 + lastActiveLeafNode); 363 lastActiveLeafNode = null; 364 if (comment != null) { 365 source.addComment(lastLeafNode, comment, XPathParts.Comments.CommentType.POSTBLOCK); 366 comment = null; 367 } 368 } 369 currentFullXPathSb.setLength(0); 370 currentFullXPathSb.append(stripAfter(currentFullXPath, qName)); 371 justPopped = true; 372 } 373 374 /** 375 * Trim leading whitespace if there is a linefeed among them, then the same with trailing. 376 * 377 * @param source 378 * @return 379 */ trimWhitespaceSpecial(String source)380 private String trimWhitespaceSpecial(String source) { 381 if (DEBUG && CONTROLS.containsSome(source)) { 382 System.out.println("*** " + source); 383 } 384 if (!source.contains("\n")) { 385 return source; 386 } 387 source = whitespaceWithLf.reset(source).replaceAll("\n"); 388 return source; 389 } 390 warnOnOverride(String former, String formerPath)391 private void warnOnOverride(String former, String formerPath) { 392 String distinguishing = CLDRFile.getDistinguishingXPath(formerPath, null); 393 System.out.println("\tERROR in " + source.getLocaleID() 394 + ";\toverriding old value <" + former + "> at path " + distinguishing + 395 "\twith\t<" + lastChars + ">" + 396 CldrUtility.LINE_SEPARATOR + "\told fullpath: " + formerPath + 397 CldrUtility.LINE_SEPARATOR + "\tnew fullpath: " + currentFullXPathSb.toString()); 398 overrideCount += 1; 399 } 400 stripAfter(String input, String qName)401 private static String stripAfter(String input, String qName) { 402 int pos = findLastSlash(input); 403 if (qName != null) { 404 // assert input.substring(pos+1).startsWith(qName); 405 if (!input.substring(pos + 1).startsWith(qName)) { 406 throw new IllegalArgumentException("Internal Error: should never get here."); 407 } 408 } 409 return input.substring(0, pos); 410 } 411 findLastSlash(String input)412 private static int findLastSlash(String input) { 413 int braceStack = 0; 414 char inQuote = 0; 415 for (int i = input.length() - 1; i >= 0; --i) { 416 char ch = input.charAt(i); 417 switch (ch) { 418 case '\'': // treat single and double quotes in same way 419 case '"': 420 if (inQuote == 0) { 421 inQuote = ch; 422 } else if (inQuote == ch) { 423 inQuote = 0; // come out of quote 424 } 425 break; 426 case '/': 427 if (inQuote == 0 && braceStack == 0) { 428 return i; 429 } 430 break; 431 case '[': 432 if (inQuote == 0) { 433 --braceStack; 434 } 435 break; 436 case ']': 437 if (inQuote == 0) { 438 ++braceStack; 439 } 440 break; 441 } 442 } 443 return -1; 444 } 445 446 // SAX items we need to catch 447 448 @Override startElement( String uri, String localName, String qName, Attributes attributes)449 public void startElement( 450 String uri, 451 String localName, 452 String qName, 453 Attributes attributes) 454 throws SAXException { 455 Log.logln(LOG_PROGRESS || SHOW_START_END, "startElement uri\t" + uri 456 + "\tlocalName " + localName 457 + "\tqName " + qName 458 + "\tattributes " + show(attributes)); 459 try { 460 if (supplementalStatus == SupplementalStatus.NEVER_SET) { // set by first element 461 attributeOrder = new TreeMap<>( 462 // HACK for ldmlIcu 463 dtdData.dtdType == DtdType.ldml 464 ? CLDRFile.getAttributeOrdering() 465 : dtdData.getAttributeComparator()); 466 supplementalStatus = source.getXMLNormalizingDtdType() == DtdType.ldml ? 467 SupplementalStatus.IS_SUMPPLEMENTAL : SupplementalStatus.NOT_SUPPLEMENTAL; 468 } 469 push(qName, attributes); 470 } catch (RuntimeException e) { 471 e.printStackTrace(); 472 throw e; 473 } 474 } 475 476 @Override endElement(String uri, String localName, String qName)477 public void endElement(String uri, String localName, String qName) 478 throws SAXException { 479 Log.logln(LOG_PROGRESS || SHOW_START_END, "endElement uri\t" + uri + "\tlocalName " + localName 480 + "\tqName " + qName); 481 try { 482 pop(qName); 483 } catch (RuntimeException e) { 484 throw e; 485 } 486 } 487 488 @Override characters(char[] ch, int start, int length)489 public void characters(char[] ch, int start, int length) 490 throws SAXException { 491 try { 492 String value = new String(ch, start, length); 493 Log.logln(LOG_PROGRESS, "characters:\t" + value); 494 // we will strip leading and trailing line separators in another place. 495 // if (value.indexOf(XML_LINESEPARATOR) >= 0) { 496 // value = value.replace(XML_LINESEPARATOR, '\u0020'); 497 // } 498 lastChars += value; 499 justPopped = false; 500 } catch (RuntimeException e) { 501 e.printStackTrace(); 502 throw e; 503 } 504 } 505 506 @Override startDTD(String name, String publicId, String systemId)507 public void startDTD(String name, String publicId, String systemId) throws SAXException { 508 Log.logln(LOG_PROGRESS, "startDTD name: " + name 509 + ", publicId: " + publicId 510 + ", systemId: " + systemId); 511 commentStackIndex++; 512 source.setXMLNormalizingDtdType(DtdType.valueOf(name)); 513 dtdData = DtdData.getInstance(source.getXMLNormalizingDtdType()); 514 } 515 516 @Override endDTD()517 public void endDTD() throws SAXException { 518 Log.logln(LOG_PROGRESS, "endDTD"); 519 commentStackIndex--; 520 } 521 522 @Override comment(char[] ch, int start, int length)523 public void comment(char[] ch, int start, int length) throws SAXException { 524 final String string = new String(ch, start, length); 525 Log.logln(LOG_PROGRESS, commentStackIndex + " comment " + string); 526 try { 527 if (commentStackIndex != 0) return; 528 String comment0 = trimWhitespaceSpecial(string).trim(); 529 if (lastActiveLeafNode != null) { 530 source.addComment(lastActiveLeafNode, comment0, XPathParts.Comments.CommentType.LINE); 531 } else { 532 comment = (comment == null ? comment0 : comment + XPathParts.NEWLINE + comment0); 533 } 534 } catch (RuntimeException e) { 535 e.printStackTrace(); 536 throw e; 537 } 538 } 539 540 @Override ignorableWhitespace(char[] ch, int start, int length)541 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 542 if (LOG_PROGRESS) 543 Log.logln(LOG_PROGRESS, 544 "ignorableWhitespace length: " + length + ": " + Utility.hex(new String(ch, start, length))); 545 for (int i = start; i < start + length; ++i) { 546 if (ch[i] == '\n') { 547 Log.logln(LOG_PROGRESS && lastActiveLeafNode != null, "\\n: zeroing last leafNode: " 548 + lastActiveLeafNode); 549 lastActiveLeafNode = null; 550 break; 551 } 552 } 553 } 554 555 @Override startDocument()556 public void startDocument() throws SAXException { 557 Log.logln(LOG_PROGRESS, "startDocument"); 558 commentStackIndex = 0; // initialize 559 } 560 561 @Override endDocument()562 public void endDocument() throws SAXException { 563 Log.logln(LOG_PROGRESS, "endDocument"); 564 try { 565 if (comment != null) { 566 source.addComment(null, comment, XPathParts.Comments.CommentType.LINE); 567 } 568 } catch (RuntimeException e) { 569 e.printStackTrace(); 570 throw e; 571 } 572 } 573 574 // ==== The following are just for debugging ===== 575 576 @Override elementDecl(String name, String model)577 public void elementDecl(String name, String model) throws SAXException { 578 Log.logln(LOG_PROGRESS, "Attribute\t" + name + "\t" + model); 579 } 580 581 @Override attributeDecl(String eName, String aName, String type, String mode, String value)582 public void attributeDecl(String eName, String aName, String type, String mode, String value) 583 throws SAXException { 584 Log.logln(LOG_PROGRESS, "Attribute\t" + eName + "\t" + aName + "\t" + type + "\t" + mode + "\t" + value); 585 } 586 587 @Override internalEntityDecl(String name, String value)588 public void internalEntityDecl(String name, String value) throws SAXException { 589 Log.logln(LOG_PROGRESS, "Internal Entity\t" + name + "\t" + value); 590 } 591 592 @Override externalEntityDecl(String name, String publicId, String systemId)593 public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException { 594 Log.logln(LOG_PROGRESS, "Internal Entity\t" + name + "\t" + publicId + "\t" + systemId); 595 } 596 597 @Override processingInstruction(String target, String data)598 public void processingInstruction(String target, String data) 599 throws SAXException { 600 Log.logln(LOG_PROGRESS, "processingInstruction: " + target + ", " + data); 601 } 602 603 @Override skippedEntity(String name)604 public void skippedEntity(String name) 605 throws SAXException { 606 Log.logln(LOG_PROGRESS, "skippedEntity: " + name); 607 } 608 609 @Override setDocumentLocator(Locator locator)610 public void setDocumentLocator(Locator locator) { 611 Log.logln(LOG_PROGRESS, "setDocumentLocator Locator " + locator); 612 } 613 614 @Override startPrefixMapping(String prefix, String uri)615 public void startPrefixMapping(String prefix, String uri) throws SAXException { 616 Log.logln(LOG_PROGRESS, "startPrefixMapping prefix: " + prefix + 617 ", uri: " + uri); 618 } 619 620 @Override endPrefixMapping(String prefix)621 public void endPrefixMapping(String prefix) throws SAXException { 622 Log.logln(LOG_PROGRESS, "endPrefixMapping prefix: " + prefix); 623 } 624 625 @Override startEntity(String name)626 public void startEntity(String name) throws SAXException { 627 Log.logln(LOG_PROGRESS, "startEntity name: " + name); 628 } 629 630 @Override endEntity(String name)631 public void endEntity(String name) throws SAXException { 632 Log.logln(LOG_PROGRESS, "endEntity name: " + name); 633 } 634 635 @Override startCDATA()636 public void startCDATA() throws SAXException { 637 Log.logln(LOG_PROGRESS, "startCDATA"); 638 } 639 640 @Override endCDATA()641 public void endCDATA() throws SAXException { 642 Log.logln(LOG_PROGRESS, "endCDATA"); 643 } 644 645 /* 646 * (non-Javadoc) 647 * 648 * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) 649 */ 650 @Override error(SAXParseException exception)651 public void error(SAXParseException exception) throws SAXException { 652 Log.logln(LOG_PROGRESS || true, "error: " + XMLFileReader.showSAX(exception)); 653 throw exception; 654 } 655 656 /* 657 * (non-Javadoc) 658 * 659 * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) 660 */ 661 @Override fatalError(SAXParseException exception)662 public void fatalError(SAXParseException exception) throws SAXException { 663 Log.logln(LOG_PROGRESS, "fatalError: " + XMLFileReader.showSAX(exception)); 664 throw exception; 665 } 666 667 /* 668 * (non-Javadoc) 669 * 670 * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) 671 */ 672 @Override warning(SAXParseException exception)673 public void warning(SAXParseException exception) throws SAXException { 674 Log.logln(LOG_PROGRESS, "warning: " + XMLFileReader.showSAX(exception)); 675 throw exception; 676 } 677 } 678 } 679