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