xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/XMLNormalizingLoader.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
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