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