| package org.unicode.cldr.unittest; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.EnumSet; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| |
| import org.unicode.cldr.test.DisplayAndInputProcessor; |
| import org.unicode.cldr.tool.CldrVersion; |
| import org.unicode.cldr.tool.LikelySubtags; |
| import org.unicode.cldr.util.Builder; |
| import org.unicode.cldr.util.CLDRConfig; |
| import org.unicode.cldr.util.CLDRFile; |
| import org.unicode.cldr.util.CLDRFile.DraftStatus; |
| import org.unicode.cldr.util.CLDRFile.Status; |
| import org.unicode.cldr.util.CLDRFile.WinningChoice; |
| import org.unicode.cldr.util.CLDRPaths; |
| import org.unicode.cldr.util.ChainedMap; |
| import org.unicode.cldr.util.ChainedMap.M4; |
| import org.unicode.cldr.util.CharacterFallbacks; |
| import org.unicode.cldr.util.CldrUtility; |
| import org.unicode.cldr.util.Counter; |
| import org.unicode.cldr.util.DiscreteComparator; |
| import org.unicode.cldr.util.DiscreteComparator.Ordering; |
| import org.unicode.cldr.util.DtdData; |
| import org.unicode.cldr.util.DtdData.Attribute; |
| import org.unicode.cldr.util.DtdData.Element; |
| import org.unicode.cldr.util.DtdData.ElementType; |
| import org.unicode.cldr.util.DtdType; |
| import org.unicode.cldr.util.ElementAttributeInfo; |
| import org.unicode.cldr.util.Factory; |
| import org.unicode.cldr.util.InputStreamFactory; |
| import org.unicode.cldr.util.LanguageTagParser; |
| import org.unicode.cldr.util.Level; |
| import org.unicode.cldr.util.LocaleIDParser; |
| import org.unicode.cldr.util.Pair; |
| import org.unicode.cldr.util.PathHeader; |
| import org.unicode.cldr.util.SupplementalDataInfo; |
| import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo; |
| import org.unicode.cldr.util.SupplementalDataInfo.PluralType; |
| import org.unicode.cldr.util.XMLFileReader; |
| import org.unicode.cldr.util.XPathParts; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.XMLReader; |
| |
| import com.google.common.base.Objects; |
| import com.google.common.collect.ImmutableMultimap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.TreeMultimap; |
| import com.ibm.icu.dev.util.CollectionUtilities; |
| import com.ibm.icu.impl.Relation; |
| import com.ibm.icu.impl.Row; |
| import com.ibm.icu.impl.Row.R2; |
| import com.ibm.icu.impl.Row.R3; |
| import com.ibm.icu.impl.Utility; |
| import com.ibm.icu.lang.UCharacter; |
| import com.ibm.icu.text.Collator; |
| import com.ibm.icu.text.DecimalFormat; |
| import com.ibm.icu.text.Normalizer; |
| import com.ibm.icu.text.NumberFormat; |
| import com.ibm.icu.text.UTF16; |
| import com.ibm.icu.text.UnicodeSet; |
| import com.ibm.icu.text.UnicodeSetIterator; |
| import com.ibm.icu.util.Currency; |
| import com.ibm.icu.util.ULocale; |
| |
| public class TestBasic extends TestFmwkPlus { |
| |
| private static final boolean DEBUG = false; |
| |
| static CLDRConfig testInfo = CLDRConfig.getInstance(); |
| |
| private static final SupplementalDataInfo SUPPLEMENTAL_DATA_INFO = testInfo |
| .getSupplementalDataInfo(); |
| |
| private static final ImmutableSet<Pair<String, String>> knownElementExceptions = ImmutableSet.of( |
| Pair.of("ldml", "usesMetazone"), |
| Pair.of("ldmlICU", "usesMetazone")); |
| |
| private static final ImmutableSet<Pair<String, String>> knownAttributeExceptions = ImmutableSet.of( |
| Pair.of("ldml", "version"), |
| Pair.of("supplementalData", "version"), |
| Pair.of("ldmlICU", "version"), |
| Pair.of("layout", "standard"), |
| Pair.of("currency", "id"), // for v1.1.1 |
| Pair.of("monthNames", "type"), // for v1.1.1 |
| Pair.of("alias", "type") // for v1.1.1 |
| ); |
| |
| private static final ImmutableSet<Pair<String, String>> knownChildExceptions = ImmutableSet.of( |
| Pair.of("abbreviationFallback", "special"), |
| Pair.of("inList", "special"), |
| Pair.of("preferenceOrdering", "special")); |
| |
| /** |
| * Simple test that loads each file in the cldr directory, thus verifying |
| * that the DTD works, and also checks that the PrettyPaths work. |
| * |
| * @author markdavis |
| */ |
| |
| public static void main(String[] args) { |
| new TestBasic().run(args); |
| } |
| |
| private static final ImmutableSet<String> skipAttributes = ImmutableSet.of( |
| "alt", "draft", "references"); |
| |
| private final ImmutableSet<String> eightPointLocales = ImmutableSet.of( |
| "ar", "ca", "cs", "da", "de", "el", "es", "fi", "fr", "he", "hi", "hr", "hu", "id", |
| "it", "ja", "ko", "lt", "lv", "nb", "nl", "pl", "pt", "pt_PT", "ro", "ru", "sk", "sl", "sr", "sv", |
| "th", "tr", "uk", "vi", "zh", "zh_Hant"); |
| |
| // private final boolean showForceZoom = Utility.getProperty("forcezoom", |
| // false); |
| |
| private final boolean resolved = CldrUtility.getProperty("resolved", false); |
| |
| private final Exception[] internalException = new Exception[1]; |
| |
| public void TestDtds() throws IOException { |
| Relation<Row.R2<DtdType, String>, String> foundAttributes = Relation |
| .of(new TreeMap<Row.R2<DtdType, String>, Set<String>>(), |
| TreeSet.class); |
| final CLDRConfig config = CLDRConfig.getInstance(); |
| final File basedir = config.getCldrBaseDirectory(); |
| List<TimingInfo> data = new ArrayList<>(); |
| |
| for (String subdir : config.getCLDRDataDirectories()) { |
| checkDtds(new File(basedir, subdir), 0, foundAttributes, data); |
| } |
| if (foundAttributes.size() > 0) { |
| showFoundElements(foundAttributes); |
| } |
| if (isVerbose()) { |
| long totalBytes = 0; |
| long totalNanos = 0; |
| for (TimingInfo i : data) { |
| long length = i.file.length(); |
| totalBytes += length; |
| totalNanos += i.nanos; |
| logln(i.nanos + "\t" + length + "\t" + i.file); |
| } |
| logln(totalNanos + "\t" + totalBytes); |
| } |
| } |
| |
| private void checkDtds(File directoryFile, int level, |
| Relation<R2<DtdType, String>, String> foundAttributes, |
| List<TimingInfo> data) throws IOException { |
| boolean deepCheck = getInclusion() >= 10; |
| File[] listFiles = directoryFile.listFiles(); |
| String canonicalPath = directoryFile.getCanonicalPath(); |
| String indent = Utility.repeat("\t", level); |
| if (listFiles == null) { |
| throw new IllegalArgumentException(indent + "Empty directory: " |
| + canonicalPath); |
| } |
| logln("Checking files for DTD errors in: " + indent + canonicalPath); |
| for (File fileName : listFiles) { |
| String name = fileName.getName(); |
| if (CLDRConfig.isJunkFile(name)) { |
| continue; |
| } else if (fileName.isDirectory()) { |
| checkDtds(fileName, level + 1, foundAttributes, data); |
| } else if (name.endsWith(".xml")) { |
| data.add(check(fileName)); |
| if (deepCheck // takes too long to do all the time |
| ) { |
| CLDRFile cldrfile = CLDRFile.loadFromFile(fileName, "temp", |
| DraftStatus.unconfirmed); |
| for (String xpath : cldrfile) { |
| String fullPath = cldrfile.getFullXPath(xpath); |
| if (fullPath == null) { |
| fullPath = cldrfile.getFullXPath(xpath); |
| assertNotNull("", fullPath); |
| continue; |
| } |
| XPathParts parts = XPathParts |
| .getFrozenInstance(fullPath); |
| DtdType type = parts.getDtdData().dtdType; |
| for (int i = 0; i < parts.size(); ++i) { |
| String element = parts.getElement(i); |
| R2<DtdType, String> typeElement = Row.of(type, |
| element); |
| if (parts.getAttributeCount(i) == 0) { |
| foundAttributes.put(typeElement, "NONE"); |
| } else { |
| for (String attribute : parts |
| .getAttributeKeys(i)) { |
| foundAttributes.put(typeElement, attribute); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void showFoundElements( |
| Relation<Row.R2<DtdType, String>, String> foundAttributes) { |
| Relation<Row.R2<DtdType, String>, String> theoryAttributes = Relation |
| .of(new TreeMap<Row.R2<DtdType, String>, Set<String>>(), |
| TreeSet.class); |
| for (DtdType type : DtdType.values()) { |
| DtdData dtdData = DtdData.getInstance(type); |
| for (Element element : dtdData.getElementFromName().values()) { |
| String name = element.getName(); |
| Set<Attribute> attributes = element.getAttributes().keySet(); |
| R2<DtdType, String> typeElement = Row.of(type, name); |
| if (attributes.isEmpty()) { |
| theoryAttributes.put(typeElement, "NONE"); |
| } else { |
| for (Attribute attribute : attributes) { |
| theoryAttributes.put(typeElement, attribute.name); |
| } |
| } |
| } |
| } |
| Relation<String, R3<Boolean, DtdType, String>> attributesToTypeElementUsed = Relation |
| .of(new TreeMap<String, Set<R3<Boolean, DtdType, String>>>(), |
| LinkedHashSet.class); |
| |
| for (Entry<R2<DtdType, String>, Set<String>> s : theoryAttributes |
| .keyValuesSet()) { |
| R2<DtdType, String> typeElement = s.getKey(); |
| Set<String> theoryAttributeSet = s.getValue(); |
| DtdType type = typeElement.get0(); |
| String element = typeElement.get1(); |
| if (element.equals("ANY") || element.equals("#PCDATA")) { |
| continue; |
| } |
| boolean deprecatedElement = SUPPLEMENTAL_DATA_INFO.isDeprecated( |
| type, element, "*", "*"); |
| String header = type + "\t" + element + "\t" |
| + (deprecatedElement ? "X" : "") + "\t"; |
| Set<String> usedAttributes = foundAttributes.get(typeElement); |
| Set<String> unusedAttributes = new LinkedHashSet<String>( |
| theoryAttributeSet); |
| if (usedAttributes == null) { |
| logln(header |
| + "<NOT-FOUND>\t\t" |
| + siftDeprecated(type, element, unusedAttributes, |
| attributesToTypeElementUsed, false)); |
| continue; |
| } |
| unusedAttributes.removeAll(usedAttributes); |
| logln(header |
| + siftDeprecated(type, element, usedAttributes, |
| attributesToTypeElementUsed, true) |
| + "\t" |
| + siftDeprecated(type, element, unusedAttributes, |
| attributesToTypeElementUsed, false)); |
| } |
| |
| logln("Undeprecated Attributes\t"); |
| for (Entry<String, R3<Boolean, DtdType, String>> s : attributesToTypeElementUsed |
| .keyValueSet()) { |
| R3<Boolean, DtdType, String> typeElementUsed = s.getValue(); |
| logln(s.getKey() + "\t" + typeElementUsed.get0() |
| + "\t" + typeElementUsed.get1() + "\t" |
| + typeElementUsed.get2()); |
| } |
| } |
| |
| private String siftDeprecated( |
| DtdType type, |
| String element, |
| Set<String> attributeSet, |
| Relation<String, R3<Boolean, DtdType, String>> attributesToTypeElementUsed, |
| boolean used) { |
| StringBuilder b = new StringBuilder(); |
| StringBuilder bdep = new StringBuilder(); |
| for (String attribute : attributeSet) { |
| String attributeName = "«" |
| + attribute |
| + (!"NONE".equals(attribute) && CLDRFile.isDistinguishing(type, element, attribute) ? "*" |
| : "") |
| + "»"; |
| if (!"NONE".equals(attribute) && SUPPLEMENTAL_DATA_INFO.isDeprecated(type, element, attribute, |
| "*")) { |
| if (bdep.length() != 0) { |
| bdep.append(" "); |
| } |
| bdep.append(attributeName); |
| } else { |
| if (b.length() != 0) { |
| b.append(" "); |
| } |
| b.append(attributeName); |
| if (!"NONE".equals(attribute)) { |
| attributesToTypeElementUsed.put(attribute, |
| Row.of(used, type, element)); |
| } |
| } |
| } |
| return b.toString() + "\t" + bdep.toString(); |
| } |
| |
| class MyErrorHandler implements ErrorHandler { |
| public void error(SAXParseException exception) throws SAXException { |
| errln("error: " + XMLFileReader.showSAX(exception)); |
| throw exception; |
| } |
| |
| public void fatalError(SAXParseException exception) throws SAXException { |
| errln("fatalError: " + XMLFileReader.showSAX(exception)); |
| throw exception; |
| } |
| |
| public void warning(SAXParseException exception) throws SAXException { |
| errln("warning: " + XMLFileReader.showSAX(exception)); |
| throw exception; |
| } |
| } |
| |
| private class TimingInfo { |
| File file; |
| long nanos; |
| } |
| |
| public TimingInfo check(File systemID) { |
| long start = System.nanoTime(); |
| try (InputStream fis = InputStreamFactory.createInputStream(systemID)) { |
| // FileInputStream fis = new FileInputStream(systemID); |
| XMLReader xmlReader = XMLFileReader.createXMLReader(true); |
| xmlReader.setErrorHandler(new MyErrorHandler()); |
| InputSource is = new InputSource(fis); |
| is.setSystemId(systemID.toString()); |
| xmlReader.parse(is); |
| // fis.close(); |
| } catch (SAXException | IOException e) { |
| errln("\t" + "Can't read " + systemID + "\t" + e.getClass() + "\t" |
| + e.getMessage()); |
| } |
| // catch (SAXParseException e) { |
| // errln("\t" + "Can't read " + systemID + "\t" + e.getClass() + "\t" + |
| // e.getMessage()); |
| // } catch (IOException e) { |
| // errln("\t" + "Can't read " + systemID + "\t" + e.getClass() + "\t" + |
| // e.getMessage()); |
| // } |
| TimingInfo timingInfo = new TimingInfo(); |
| timingInfo.nanos = System.nanoTime() - start; |
| timingInfo.file = systemID; |
| return timingInfo; |
| } |
| |
| public void TestCurrencyFallback() { |
| XPathParts parts = new XPathParts(); |
| Factory cldrFactory = testInfo.getCldrFactory(); |
| Set<String> currencies = testInfo.getStandardCodes().getAvailableCodes( |
| "currency"); |
| |
| final UnicodeSet CHARACTERS_THAT_SHOULD_HAVE_FALLBACKS = (UnicodeSet) new UnicodeSet( |
| "[[:sc:]-[\\u0000-\\u00FF]]").freeze(); |
| |
| CharacterFallbacks fallbacks = CharacterFallbacks.make(); |
| |
| for (String locale : cldrFactory.getAvailable()) { |
| CLDRFile file = testInfo.getCLDRFile(locale, false); |
| if (file.isNonInheriting()) |
| continue; |
| |
| final UnicodeSet OK_CURRENCY_FALLBACK = (UnicodeSet) new UnicodeSet( |
| "[\\u0000-\\u00FF]").addAll(safeExemplars(file, "")) |
| .addAll(safeExemplars(file, "auxiliary")) |
| // .addAll(safeExemplars(file, "currencySymbol")) |
| .freeze(); |
| UnicodeSet badSoFar = new UnicodeSet(); |
| |
| for (Iterator<String> it = file.iterator(); it.hasNext();) { |
| String path = it.next(); |
| if (path.endsWith("/alias")) { |
| continue; |
| } |
| String value = file.getStringValue(path); |
| |
| // check for special characters |
| |
| if (CHARACTERS_THAT_SHOULD_HAVE_FALLBACKS.containsSome(value)) { |
| |
| parts.set(path); |
| if (!parts.getElement(-1).equals("symbol")) { |
| continue; |
| } |
| // We don't care about fallbacks for narrow currency symbols |
| if ("narrow".equals(parts.getAttributeValue(-1, "alt"))) { |
| continue; |
| } |
| String currencyType = parts.getAttributeValue(-2, "type"); |
| |
| UnicodeSet fishy = new UnicodeSet().addAll(value) |
| .retainAll(CHARACTERS_THAT_SHOULD_HAVE_FALLBACKS) |
| .removeAll(badSoFar); |
| for (UnicodeSetIterator it2 = new UnicodeSetIterator(fishy); it2 |
| .next();) { |
| final int fishyCodepoint = it2.codepoint; |
| List<String> fallbackList = fallbacks |
| .getSubstitutes(fishyCodepoint); |
| |
| String nfkc = Normalizer.normalize(fishyCodepoint, |
| Normalizer.NFKC); |
| if (!nfkc.equals(UTF16.valueOf(fishyCodepoint))) { |
| if (fallbackList == null) { |
| fallbackList = new ArrayList<String>(); |
| } else { |
| fallbackList = new ArrayList<String>( |
| fallbackList); // writable |
| } |
| fallbackList.add(nfkc); |
| } |
| // later test for all Latin-1 |
| if (fallbackList == null) { |
| errln("Locale:\t" + locale |
| + ";\tCharacter with no fallback:\t" |
| + it2.getString() + "\t" |
| + UCharacter.getName(fishyCodepoint)); |
| badSoFar.add(fishyCodepoint); |
| } else { |
| String fallback = null; |
| for (String fb : fallbackList) { |
| if (OK_CURRENCY_FALLBACK.containsAll(fb)) { |
| if (!fb.equals(currencyType) |
| && currencies.contains(fb)) { |
| errln("Locale:\t" |
| + locale |
| + ";\tCurrency:\t" |
| + currencyType |
| + ";\tFallback converts to different code!:\t" |
| + fb |
| + "\t" |
| + it2.getString() |
| + "\t" |
| + UCharacter |
| .getName(fishyCodepoint)); |
| } |
| if (fallback == null) { |
| fallback = fb; |
| } |
| } |
| } |
| if (fallback == null) { |
| errln("Locale:\t" |
| + locale |
| + ";\tCharacter with no good fallback (exemplars+Latin1):\t" |
| + it2.getString() + "\t" |
| + UCharacter.getName(fishyCodepoint)); |
| badSoFar.add(fishyCodepoint); |
| } else { |
| logln("Locale:\t" + locale |
| + ";\tCharacter with good fallback:\t" |
| + it2.getString() + " " |
| + UCharacter.getName(fishyCodepoint) |
| + " => " + fallback); |
| // badSoFar.add(fishyCodepoint); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void TestAbstractPaths() { |
| Factory cldrFactory = testInfo.getCldrFactory(); |
| CLDRFile english = testInfo.getEnglish(); |
| Map<String, Counter<Level>> abstactPaths = new TreeMap<String, Counter<Level>>(); |
| RegexTransform abstractPathTransform = new RegexTransform( |
| RegexTransform.Processing.ONE_PASS).add("//ldml/", "") |
| .add("\\[@alt=\"[^\"]*\"\\]", "").add("=\"[^\"]*\"", "=\"*\"") |
| .add("([^]])\\[", "$1\t[").add("([^]])/", "$1\t/") |
| .add("/", "\t"); |
| |
| for (String locale : getInclusion() <= 5 ? eightPointLocales : cldrFactory.getAvailable()) { |
| CLDRFile file = testInfo.getCLDRFile(locale, resolved); |
| if (file.isNonInheriting()) |
| continue; |
| logln(locale + "\t-\t" + english.getName(locale)); |
| |
| for (Iterator<String> it = file.iterator(); it.hasNext();) { |
| String path = it.next(); |
| if (path.endsWith("/alias")) { |
| continue; |
| } |
| // collect abstracted paths |
| String abstractPath = abstractPathTransform.transform(path); |
| Level level = SUPPLEMENTAL_DATA_INFO.getCoverageLevel(path, |
| locale); |
| if (level == Level.OPTIONAL) { |
| level = Level.COMPREHENSIVE; |
| } |
| Counter<Level> row = abstactPaths.get(abstractPath); |
| if (row == null) { |
| abstactPaths.put(abstractPath, row = new Counter<Level>()); |
| } |
| row.add(level, 1); |
| } |
| } |
| logln(CldrUtility.LINE_SEPARATOR + "Abstract Paths"); |
| for (Entry<String, Counter<Level>> pathInfo : abstactPaths.entrySet()) { |
| String path = pathInfo.getKey(); |
| Counter<Level> counter = pathInfo.getValue(); |
| logln(counter.getTotal() + "\t" + getCoverage(counter) + "\t" |
| + path); |
| } |
| } |
| |
| private CharSequence getCoverage(Counter<Level> counter) { |
| StringBuilder result = new StringBuilder(); |
| boolean first = true; |
| for (Level level : counter.getKeysetSortedByKey()) { |
| if (first) { |
| first = false; |
| } else { |
| result.append(' '); |
| } |
| result.append("L").append(level.ordinal()).append("=") |
| .append(counter.get(level)); |
| } |
| return result; |
| } |
| |
| // public void TestCLDRFileCache() { |
| // long start = System.nanoTime(); |
| // Factory cldrFactory = testInfo.getCldrFactory(); |
| // String unusualLocale = "hi"; |
| // CLDRFile file = cldrFactory.make(unusualLocale, true); |
| // long afterOne = System.nanoTime(); |
| // logln("First: " + (afterOne-start)); |
| // CLDRFile file2 = cldrFactory.make(unusualLocale, true); |
| // long afterTwo = System.nanoTime(); |
| // logln("Second: " + (afterTwo-afterOne)); |
| // } |
| // |
| public void TestPaths() { |
| Relation<String, String> distinguishing = Relation.of( |
| new TreeMap<String, Set<String>>(), TreeSet.class); |
| Relation<String, String> nonDistinguishing = Relation.of( |
| new TreeMap<String, Set<String>>(), TreeSet.class); |
| XPathParts parts = new XPathParts(); |
| Factory cldrFactory = testInfo.getCldrFactory(); |
| CLDRFile english = testInfo.getEnglish(); |
| |
| Relation<String, String> pathToLocale = Relation.of( |
| new TreeMap<String, Set<String>>(CLDRFile |
| .getComparator(DtdType.ldml)), |
| TreeSet.class, null); |
| Set<String> localesToTest = getInclusion() <= 5 ? eightPointLocales : cldrFactory.getAvailable(); |
| for (String locale : localesToTest) { |
| CLDRFile file = testInfo.getCLDRFile(locale, resolved); |
| DtdType dtdType = null; |
| if (file.isNonInheriting()) |
| continue; |
| DisplayAndInputProcessor displayAndInputProcessor = new DisplayAndInputProcessor( |
| file, false); |
| |
| logln(locale + "\t-\t" + english.getName(locale)); |
| |
| for (Iterator<String> it = file.iterator(); it.hasNext();) { |
| String path = it.next(); |
| if (dtdType == null) { |
| dtdType = DtdType.fromPath(path); |
| } |
| |
| if (path.endsWith("/alias")) { |
| continue; |
| } |
| String value = file.getStringValue(path); |
| if (value == null) { |
| throw new IllegalArgumentException(locale |
| + "\tError: in null value at " + path); |
| } |
| |
| String displayValue = displayAndInputProcessor |
| .processForDisplay(path, value); |
| if (!displayValue.equals(value)) { |
| logln("\t" |
| + locale |
| + "\tdisplayAndInputProcessor changes display value <" |
| + value + ">\t=>\t<" + displayValue + ">\t\t" |
| + path); |
| } |
| String inputValue = displayAndInputProcessor.processInput(path, |
| value, internalException); |
| if (internalException[0] != null) { |
| errln("\t" + locale |
| + "\tdisplayAndInputProcessor internal error <" |
| + value + ">\t=>\t<" + inputValue + ">\t\t" + path); |
| internalException[0].printStackTrace(System.out); |
| } |
| if (isVerbose() && !inputValue.equals(value)) { |
| displayAndInputProcessor.processInput(path, value, |
| internalException); // for |
| // debugging |
| logln("\t" |
| + locale |
| + "\tdisplayAndInputProcessor changes input value <" |
| + value + ">\t=>\t<" + inputValue + ">\t\t" + path); |
| } |
| |
| pathToLocale.put(path, locale); |
| |
| // also check for non-distinguishing attributes |
| if (path.contains("/identity")) |
| continue; |
| |
| String fullPath = file.getFullXPath(path); |
| parts.set(fullPath); |
| for (int i = 0; i < parts.size(); ++i) { |
| if (parts.getAttributeCount(i) == 0) |
| continue; |
| String element = parts.getElement(i); |
| for (String attribute : parts.getAttributeKeys(i)) { |
| if (skipAttributes.contains(attribute)) |
| continue; |
| if (CLDRFile.isDistinguishing(dtdType, element, attribute)) { |
| distinguishing.put(element, attribute); |
| } else { |
| nonDistinguishing.put(element, attribute); |
| } |
| } |
| } |
| } |
| } |
| |
| if (isVerbose()) { |
| System.out.format("Distinguishing Elements: %s" |
| + CldrUtility.LINE_SEPARATOR, distinguishing); |
| System.out.format("Nondistinguishing Elements: %s" |
| + CldrUtility.LINE_SEPARATOR, nonDistinguishing); |
| System.out.format("Skipped %s" + CldrUtility.LINE_SEPARATOR, |
| skipAttributes); |
| } |
| } |
| |
| /** |
| * The verbose output shows the results of 1..3 \u00a4 signs. |
| */ |
| public void checkCurrency() { |
| Map<String, Set<R2<String, Integer>>> results = new TreeMap<String, Set<R2<String, Integer>>>( |
| Collator.getInstance(ULocale.ENGLISH)); |
| for (ULocale locale : ULocale.getAvailableLocales()) { |
| if (locale.getCountry().length() != 0) { |
| continue; |
| } |
| for (int i = 1; i < 4; ++i) { |
| NumberFormat format = getCurrencyInstance(locale, i); |
| for (Currency c : new Currency[] { Currency.getInstance("USD"), |
| Currency.getInstance("EUR"), |
| Currency.getInstance("INR") }) { |
| format.setCurrency(c); |
| final String formatted = format.format(12345.67); |
| Set<R2<String, Integer>> set = results.get(formatted); |
| if (set == null) { |
| results.put(formatted, |
| set = new TreeSet<R2<String, Integer>>()); |
| } |
| set.add(Row.of(locale.toString(), Integer.valueOf(i))); |
| } |
| } |
| } |
| for (String formatted : results.keySet()) { |
| logln(formatted + "\t" + results.get(formatted)); |
| } |
| } |
| |
| private static NumberFormat getCurrencyInstance(ULocale locale, int type) { |
| NumberFormat format = NumberFormat.getCurrencyInstance(locale); |
| if (type > 1) { |
| DecimalFormat format2 = (DecimalFormat) format; |
| String pattern = format2.toPattern(); |
| String replacement = "\u00a4\u00a4"; |
| for (int i = 2; i < type; ++i) { |
| replacement += "\u00a4"; |
| } |
| pattern = pattern.replace("\u00a4", replacement); |
| format2.applyPattern(pattern); |
| } |
| return format; |
| } |
| |
| private UnicodeSet safeExemplars(CLDRFile file, String string) { |
| final UnicodeSet result = file.getExemplarSet(string, |
| WinningChoice.NORMAL); |
| return result != null ? result : new UnicodeSet(); |
| } |
| |
| public void TestAPath() { |
| // <month type="1">1</month> |
| String path = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/months/monthContext[@type=\"format\"]/monthWidth[@type=\"abbreviated\"]/month[@type=\"1\"]"; |
| CLDRFile root = testInfo.getRoot(); |
| logln("path: " + path); |
| String fullpath = root.getFullXPath(path); |
| logln("fullpath: " + fullpath); |
| String value = root.getStringValue(path); |
| logln("value: " + value); |
| Status status = new Status(); |
| String source = root.getSourceLocaleID(path, status); |
| logln("locale: " + source); |
| logln("status: " + status); |
| } |
| |
| public void TestDefaultContents() { |
| Set<String> defaultContents = Inheritance.defaultContents; |
| Multimap<String, String> parentToChildren = Inheritance.parentToChildren; |
| |
| if (DEBUG) { |
| Inheritance.showChain("", "", "root"); |
| } |
| |
| for (String locale : defaultContents) { |
| CLDRFile cldrFile; |
| try { |
| cldrFile = testInfo.getCLDRFile(locale, false); |
| } catch (RuntimeException e) { |
| logln("Can't open default content file:\t" + locale); |
| continue; |
| } |
| // we check that the default content locale is always empty |
| for (Iterator<String> it = cldrFile.iterator(); it.hasNext();) { |
| String path = it.next(); |
| if (path.contains("/identity")) { |
| continue; |
| } |
| errln("Default content file not empty:\t" + locale); |
| showDifferences(locale); |
| break; |
| } |
| } |
| |
| // check that if a locale has any children, that exactly one of them is |
| // the default content. Ignore locales with variants |
| |
| for (Entry<String, Collection<String>> localeAndKids : parentToChildren.asMap().entrySet()) { |
| String locale = localeAndKids.getKey(); |
| if (locale.equals("root")) { |
| continue; |
| } |
| |
| Collection<String> rawChildren = localeAndKids.getValue(); |
| |
| // remove variant children |
| Set<String> children = new LinkedHashSet<>(); |
| for (String child : rawChildren) { |
| if (new LocaleIDParser().set(child).getVariants().length == 0) { |
| children.add(child); |
| } |
| } |
| if (children.isEmpty()) { |
| continue; |
| } |
| |
| Set<String> defaultContentChildren = new LinkedHashSet<String>(children); |
| defaultContentChildren.retainAll(defaultContents); |
| if (defaultContentChildren.size() == 1) { |
| continue; |
| // If we're already down to the region level then it's OK not to have |
| // default contents. |
| } else if (! new LocaleIDParser().set(locale).getRegion().isEmpty()) { |
| continue; |
| } else if (defaultContentChildren.isEmpty()) { |
| Object possible = highestShared(locale, children); |
| errln("Locale has children but is missing default contents locale: " |
| + locale + ", children: " + children + "; possible fixes for children:\n" + possible); |
| } else { |
| errln("Locale has too many defaultContent locales!!: " |
| + locale + ", defaultContents: " |
| + defaultContentChildren); |
| } |
| } |
| |
| // check that each default content locale is likely-subtag equivalent to |
| // its parent. |
| |
| for (String locale : defaultContents) { |
| String maxLocale = LikelySubtags.maximize(locale, likelyData); |
| String localeParent = LocaleIDParser.getParent(locale); |
| String maxLocaleParent = LikelySubtags.maximize(localeParent, |
| likelyData); |
| if (locale.equals("ar_001")) { |
| logln("Known exception to likelyMax(locale=" + locale + ")" |
| + " == " + "likelyMax(defaultContent=" + localeParent |
| + ")"); |
| continue; |
| } |
| assertEquals("likelyMax(locale=" + locale + ")" + " == " |
| + "likelyMax(defaultContent=" + localeParent + ")", |
| maxLocaleParent, maxLocale); |
| } |
| |
| } |
| |
| private String highestShared(String parent, Set<String> children) { |
| M4<PathHeader, String, String, Boolean> data = ChainedMap.of(new TreeMap<PathHeader, Object>(), new TreeMap<String, Object>(), |
| new TreeMap<String, Object>(), Boolean.class); |
| CLDRFile parentFile = testInfo.getCLDRFile(parent, true); |
| PathHeader.Factory phf = PathHeader.getFactory(testInfo.getEnglish()); |
| for (String child : children) { |
| CLDRFile cldrFile = testInfo.getCLDRFile(child, false); |
| for (String path : cldrFile) { |
| if (path.contains("/identity")) { |
| continue; |
| } |
| if (path.contains("provisional") || path.contains("unconfirmed")) { |
| continue; |
| } |
| String value = cldrFile.getStringValue(path); |
| // double-check |
| String parentValue = parentFile.getStringValue(path); |
| if (value.equals(parentValue)) { |
| continue; |
| } |
| PathHeader ph = phf.fromPath(path); |
| data.put(ph, value, child, Boolean.TRUE); |
| data.put(ph, parentValue == null ? "∅∅∅" : parentValue, child, Boolean.TRUE); |
| } |
| } |
| StringBuilder result = new StringBuilder(); |
| for (Entry<PathHeader, Map<String, Map<String, Boolean>>> entry : data) { |
| for (Entry<String, Map<String, Boolean>> item : entry.getValue().entrySet()) { |
| result.append("\n") |
| .append(entry.getKey()) |
| .append("\t") |
| .append(item.getKey() + "\t" + item.getValue().keySet()); |
| } |
| } |
| return result.toString(); |
| } |
| |
| public static class Inheritance { |
| public static final Set<String> defaultContents = SUPPLEMENTAL_DATA_INFO |
| .getDefaultContentLocales(); |
| public static final Multimap<String, String> parentToChildren; |
| |
| static { |
| Multimap<String, String> _parentToChildren = TreeMultimap.create(); |
| for (String child : testInfo.getCldrFactory().getAvailable()) { |
| if (child.equals("root")) { |
| continue; |
| } |
| String localeParent = LocaleIDParser.getParent(child); |
| _parentToChildren.put(localeParent, child); |
| } |
| parentToChildren = ImmutableMultimap.copyOf(_parentToChildren); |
| } |
| |
| public static void showChain(String prefix, String gparent, String current) { |
| Collection<String> children = parentToChildren.get(current); |
| if (children == null) { |
| throw new IllegalArgumentException(); |
| } |
| prefix += current + (defaultContents.contains(current) ? "*" : "") |
| + (isLikelyEquivalent(gparent, current) ? "~" : "") + "\t"; |
| |
| // find leaves |
| Set<String> parents = new LinkedHashSet<>(children); |
| parents.retainAll(parentToChildren.keySet()); |
| Set<String> leaves = new LinkedHashSet<>(children); |
| leaves.removeAll(parentToChildren.keySet()); |
| if (!leaves.isEmpty()) { |
| List<String> presentation = new ArrayList<>(); |
| boolean gotDc = false; |
| for (String s : leaves) { |
| String shown = s; |
| if (isLikelyEquivalent(current, s)) { |
| shown += "~"; |
| } |
| if (defaultContents.contains(s)) { |
| gotDc = true; |
| shown += "*"; |
| } |
| if (!shown.equals(s)) { |
| presentation.add(0, shown); |
| } else { |
| presentation.add(shown); |
| } |
| } |
| if (!gotDc) { |
| int debug = 0; |
| } |
| if (leaves.size() == 1) { |
| System.out.println(prefix + CollectionUtilities.join(presentation, " ")); |
| } else { |
| System.out.println(prefix + "{" + CollectionUtilities.join(presentation, " ") + "}"); |
| } |
| } |
| for (String parent : parents) { |
| showChain(prefix, current, parent); |
| } |
| } |
| |
| static boolean isLikelyEquivalent(String locale1, String locale2) { |
| if (locale1.equals(locale2)) { |
| return true; |
| } |
| try { |
| String maxLocale1 = LikelySubtags.maximize(locale1, likelyData); |
| String maxLocale2 = LikelySubtags.maximize(locale2, likelyData); |
| return maxLocale1 != null && Objects.equal(maxLocale1, maxLocale2); |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| } |
| |
| static final Map<String, String> likelyData = SUPPLEMENTAL_DATA_INFO |
| .getLikelySubtags(); |
| |
| public void TestLikelySubtagsComplete() { |
| LanguageTagParser ltp = new LanguageTagParser(); |
| for (String locale : testInfo.getCldrFactory().getAvailable()) { |
| if (locale.equals("root")) { |
| continue; |
| } |
| String maxLocale = LikelySubtags.maximize(locale, likelyData); |
| if (maxLocale == null) { |
| errln("Locale missing likely subtag: " + locale); |
| continue; |
| } |
| ltp.set(maxLocale); |
| if (ltp.getLanguage().isEmpty() || ltp.getScript().isEmpty() |
| || ltp.getRegion().isEmpty()) { |
| errln("Locale has defective likely subtag: " + locale + " => " |
| + maxLocale); |
| } |
| } |
| } |
| |
| private void showDifferences(String locale) { |
| CLDRFile cldrFile = testInfo.getCLDRFile(locale, false); |
| final String localeParent = LocaleIDParser.getParent(locale); |
| CLDRFile parentFile = testInfo.getCLDRFile(localeParent, true); |
| int funnyCount = 0; |
| for (Iterator<String> it = cldrFile.iterator("", |
| cldrFile.getComparator()); it.hasNext();) { |
| String path = it.next(); |
| if (path.contains("/identity")) { |
| continue; |
| } |
| final String fullXPath = cldrFile.getFullXPath(path); |
| if (fullXPath.contains("[@draft=\"unconfirmed\"]") |
| || fullXPath.contains("[@draft=\"provisional\"]")) { |
| funnyCount++; |
| continue; |
| } |
| logln("\tpath:\t" + path); |
| logln("\t\t" + locale + " value:\t<" |
| + cldrFile.getStringValue(path) + ">"); |
| final String parentFullPath = parentFile.getFullXPath(path); |
| logln("\t\t" + localeParent + " value:\t<" |
| + parentFile.getStringValue(path) + ">"); |
| logln("\t\t" + locale + " fullpath:\t" + fullXPath); |
| logln("\t\t" + localeParent + " fullpath:\t" + parentFullPath); |
| } |
| logln("\tCount of non-approved:\t" + funnyCount); |
| } |
| |
| enum MissingType { |
| plurals, main_exemplars, no_main, collation, index_exemplars, punct_exemplars |
| } |
| |
| public void TestCoreData() { |
| Set<String> availableLanguages = testInfo.getCldrFactory() |
| .getAvailableLanguages(); |
| PluralInfo rootRules = SUPPLEMENTAL_DATA_INFO.getPlurals( |
| PluralType.cardinal, "root"); |
| EnumSet<MissingType> errors = EnumSet.of(MissingType.collation); |
| EnumSet<MissingType> warnings = EnumSet.of(MissingType.collation, |
| MissingType.index_exemplars, MissingType.punct_exemplars); |
| |
| Set<String> collations = new HashSet<String>(); |
| |
| // collect collation info |
| Factory collationFactory = Factory.make(CLDRPaths.COLLATION_DIRECTORY, |
| ".*", DraftStatus.contributed); |
| for (String localeID : collationFactory.getAvailable()) { |
| // if (localeID.equals("root")) { |
| // CLDRFile cldrFile = collationFactory.make(localeID, false, |
| // DraftStatus.contributed); |
| // for (String path : cldrFile) { |
| // if (path.startsWith("//ldml/collations")) { |
| // String fullPath = cldrFile.getFullXPath(path); |
| // String valid = parts.set(fullPath).getAttributeValue(1, |
| // "validSubLocales"); |
| // for (String validSub : valid.trim().split("\\s+")) { |
| // if (isTopLevel(validSub)) { |
| // collations.add(validSub); |
| // } |
| // } |
| // break; // done with root |
| // } |
| // } |
| // } else |
| if (isTopLevel(localeID)) { |
| collations.add(localeID); |
| } |
| } |
| logln(collations.toString()); |
| |
| Set<String> allLanguages = Builder.with(new TreeSet<String>()) |
| .addAll(collations).addAll(availableLanguages).freeze(); |
| |
| for (String localeID : allLanguages) { |
| if (localeID.equals("root")) { |
| continue; // skip script locales |
| } |
| if (!isTopLevel(localeID)) { |
| continue; |
| } |
| |
| errors.clear(); |
| warnings.clear(); |
| |
| String name = "Locale:" + localeID + " (" |
| + testInfo.getEnglish().getName(localeID) + ")"; |
| |
| if (!collations.contains(localeID)) { |
| warnings.add(MissingType.collation); |
| logln(name + " is missing " + MissingType.collation.toString()); |
| } |
| |
| try { |
| CLDRFile cldrFile = testInfo.getCldrFactory().make(localeID, |
| false, DraftStatus.contributed); |
| |
| String wholeFileAlias = cldrFile.getStringValue("//ldml/alias"); |
| if (wholeFileAlias != null) { |
| logln("Whole-file alias:" + name); |
| continue; |
| } |
| |
| PluralInfo pluralInfo = SUPPLEMENTAL_DATA_INFO.getPlurals( |
| PluralType.cardinal, localeID); |
| if (pluralInfo == rootRules) { |
| logln(name + " is missing " |
| + MissingType.plurals.toString()); |
| warnings.add(MissingType.plurals); |
| } |
| UnicodeSet main = cldrFile.getExemplarSet("", |
| WinningChoice.WINNING); |
| if (main == null || main.isEmpty()) { |
| errln(" " + name + " is missing " |
| + MissingType.main_exemplars.toString()); |
| errors.add(MissingType.main_exemplars); |
| } |
| UnicodeSet index = cldrFile.getExemplarSet("index", |
| WinningChoice.WINNING); |
| if (index == null || index.isEmpty()) { |
| logln(name + " is missing " |
| + MissingType.index_exemplars.toString()); |
| warnings.add(MissingType.index_exemplars); |
| } |
| UnicodeSet punctuation = cldrFile.getExemplarSet("punctuation", |
| WinningChoice.WINNING); |
| if (punctuation == null || punctuation.isEmpty()) { |
| logln(name + " is missing " |
| + MissingType.punct_exemplars.toString()); |
| warnings.add(MissingType.punct_exemplars); |
| } |
| } catch (Exception e) { |
| errln(" " + name + " is missing main locale data."); |
| errors.add(MissingType.no_main); |
| } |
| |
| // report errors |
| |
| if (errors.isEmpty() && warnings.isEmpty()) { |
| logln(name + ": No problems..."); |
| } |
| } |
| } |
| |
| private boolean isTopLevel(String localeID) { |
| return "root".equals(LocaleIDParser.getParent(localeID)); |
| } |
| |
| /** |
| * Tests that every dtd item is connected from root |
| */ |
| public void TestDtdCompleteness() { |
| for (DtdType type : DtdType.values()) { |
| DtdData dtdData = DtdData.getInstance(type); |
| Set<Element> descendents = new LinkedHashSet<Element>(); |
| dtdData.getDescendents(dtdData.ROOT, descendents); |
| Set<Element> elements = dtdData.getElements(); |
| if (!elements.equals(descendents)) { |
| for (Element e : elements) { |
| if (!descendents.contains(e) && !e.equals(dtdData.PCDATA) |
| && !e.equals(dtdData.ANY)) { |
| errln(type + ": Element " + e |
| + " not contained in descendents of ROOT."); |
| } |
| } |
| for (Element e : descendents) { |
| if (!elements.contains(e)) { |
| errln(type + ": Element " + e |
| + ", descendent of ROOT, not in elements."); |
| } |
| } |
| } |
| LinkedHashSet<Element> all = new LinkedHashSet<Element>(descendents); |
| all.addAll(elements); |
| Set<Attribute> attributes = dtdData.getAttributes(); |
| for (Attribute a : attributes) { |
| if (!elements.contains(a.element)) { |
| errln(type + ": Attribute " + a + " isn't for any element."); |
| } |
| } |
| } |
| } |
| |
| public void TestBasicDTDCompatibility() { |
| |
| if (logKnownIssue("cldrbug:11583", "Comment out test until last release data is available for unit tests")) { |
| return; |
| } |
| |
| final String oldCommon = CldrVersion.v22_1.getBaseDirectory() + "/common"; |
| |
| // set up exceptions |
| Set<String> changedToEmpty = new HashSet<String>( |
| Arrays.asList(new String[] { "version", "languageCoverage", |
| "scriptCoverage", "territoryCoverage", |
| "currencyCoverage", "timezoneCoverage", |
| "skipDefaultLocale" })); |
| Set<String> PCDATA = new HashSet<String>(); |
| PCDATA.add("PCDATA"); |
| Set<String> EMPTY = new HashSet<String>(); |
| EMPTY.add("EMPTY"); |
| Set<String> VERSION = new HashSet<String>(); |
| VERSION.add("version"); |
| |
| // test all DTDs |
| for (DtdType dtd : DtdType.values()) { |
| try { |
| ElementAttributeInfo oldDtd = ElementAttributeInfo.getInstance( |
| oldCommon, dtd); |
| ElementAttributeInfo newDtd = ElementAttributeInfo |
| .getInstance(dtd); |
| |
| if (oldDtd == newDtd) { |
| continue; |
| } |
| Relation<String, String> oldElement2Children = oldDtd |
| .getElement2Children(); |
| Relation<String, String> newElement2Children = newDtd |
| .getElement2Children(); |
| |
| Relation<String, String> oldElement2Attributes = oldDtd |
| .getElement2Attributes(); |
| Relation<String, String> newElement2Attributes = newDtd |
| .getElement2Attributes(); |
| |
| for (String element : oldElement2Children.keySet()) { |
| Set<String> oldChildren = oldElement2Children |
| .getAll(element); |
| Set<String> newChildren = newElement2Children |
| .getAll(element); |
| if (newChildren == null) { |
| if (!knownElementExceptions.contains(Pair.of(dtd.toString(), element))) { |
| errln("Old " + dtd + " contains element not in new: <" |
| + element + ">"); |
| } |
| continue; |
| } |
| Set<String> funny = containsInOrder(newChildren, |
| oldChildren); |
| if (funny != null) { |
| if (changedToEmpty.contains(element) |
| && oldChildren.equals(PCDATA) |
| && newChildren.equals(EMPTY)) { |
| // ok, skip |
| } else { |
| errln("Old " + dtd + " element <" + element |
| + "> has children Missing/Misordered:\t" |
| + funny + "\n\t\tOld:\t" + oldChildren |
| + "\n\t\tNew:\t" + newChildren); |
| } |
| } |
| |
| Set<String> oldAttributes = oldElement2Attributes |
| .getAll(element); |
| if (oldAttributes == null) { |
| oldAttributes = Collections.emptySet(); |
| } |
| Set<String> newAttributes = newElement2Attributes |
| .getAll(element); |
| if (newAttributes == null) { |
| newAttributes = Collections.emptySet(); |
| } |
| if (!newAttributes.containsAll(oldAttributes)) { |
| LinkedHashSet<String> missing = new LinkedHashSet<String>( |
| oldAttributes); |
| missing.removeAll(newAttributes); |
| if (element.equals(dtd.toString()) |
| && missing.equals(VERSION)) { |
| // ok, skip |
| } else { |
| errln("Old " + dtd + " element <" + element |
| + "> has attributes Missing:\t" + missing |
| + "\n\t\tOld:\t" + oldAttributes |
| + "\n\t\tNew:\t" + newAttributes); |
| } |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| errln("Failure with " + dtd); |
| } |
| } |
| } |
| |
| private <T> Set<T> containsInOrder(Set<T> superset, Set<T> subset) { |
| if (!superset.containsAll(subset)) { |
| LinkedHashSet<T> missing = new LinkedHashSet<T>(subset); |
| missing.removeAll(superset); |
| return missing; |
| } |
| // ok, we know that they are subsets, try order |
| Set<T> result = null; |
| DiscreteComparator<T> comp = new DiscreteComparator.Builder<T>( |
| Ordering.ARBITRARY).add(superset).get(); |
| T last = null; |
| for (T item : subset) { |
| if (last != null) { |
| int order = comp.compare(last, item); |
| if (order != -1) { |
| if (result == null) { |
| result = new HashSet<T>(); |
| result.add(last); |
| result.add(item); |
| } |
| } |
| } |
| last = item; |
| } |
| return result; |
| } |
| |
| public void TestDtdCompatibility() { |
| |
| for (DtdType type : DtdType.values()) { |
| DtdData dtdData = DtdData.getInstance(type); |
| Map<String, Element> currentElementFromName = dtdData |
| .getElementFromName(); |
| |
| // current has no orphan |
| Set<Element> orphans = new LinkedHashSet<Element>(dtdData |
| .getElementFromName().values()); |
| orphans.remove(dtdData.ROOT); |
| orphans.remove(dtdData.PCDATA); |
| orphans.remove(dtdData.ANY); |
| Set<String> elementsWithoutAlt = new TreeSet<String>(); |
| Set<String> elementsWithoutDraft = new TreeSet<String>(); |
| Set<String> elementsWithoutAlias = new TreeSet<String>(); |
| Set<String> elementsWithoutSpecial = new TreeSet<String>(); |
| |
| for (Element element : dtdData.getElementFromName().values()) { |
| Set<Element> children = element.getChildren().keySet(); |
| orphans.removeAll(children); |
| if (type == DtdType.ldml |
| && !SUPPLEMENTAL_DATA_INFO.isDeprecated(type, |
| element.name, "*", "*")) { |
| if (element.getType() == ElementType.PCDATA) { |
| if (element.getAttributeNamed("alt") == null) { |
| elementsWithoutAlt.add(element.name); |
| } |
| if (element.getAttributeNamed("draft") == null) { |
| elementsWithoutDraft.add(element.name); |
| } |
| } else { |
| if (children.size() != 0 && !"alias".equals(element.name)) { |
| if (element.getChildNamed("alias") == null) { |
| elementsWithoutAlias.add(element.name); |
| } |
| if (element.getChildNamed("special") == null) { |
| elementsWithoutSpecial.add(element.name); |
| } |
| } |
| } |
| } |
| } |
| assertEquals(type + " DTD Must not have orphan elements", |
| Collections.EMPTY_SET, orphans); |
| assertEquals(type |
| + " DTD elements with PCDATA must have 'alt' attributes", |
| Collections.EMPTY_SET, elementsWithoutAlt); |
| assertEquals(type |
| + " DTD elements with PCDATA must have 'draft' attributes", |
| Collections.EMPTY_SET, elementsWithoutDraft); |
| assertEquals(type |
| + " DTD elements with children must have 'alias' elements", |
| Collections.EMPTY_SET, elementsWithoutAlias); |
| assertEquals( |
| type |
| + " DTD elements with children must have 'special' elements", |
| Collections.EMPTY_SET, elementsWithoutSpecial); |
| |
| if (logKnownIssue("cldrbug:11583", "Comment out test until last release data is available for unit tests")) { |
| return; |
| } |
| |
| for (CldrVersion version : CldrVersion.CLDR_VERSIONS_DESCENDING) { |
| if (version == CldrVersion.unknown || version == CldrVersion.trunk) { |
| continue; |
| } |
| DtdData dtdDataOld; |
| try { |
| dtdDataOld = DtdData.getInstance(type, version.toString()); |
| } catch (IllegalArgumentException e) { |
| boolean tooOld = false; |
| switch (type) { |
| case ldmlBCP47: |
| case ldmlICU: |
| tooOld = version.isOlderThan(CldrVersion.v1_7_2); |
| break; |
| case keyboard: |
| case platform: |
| tooOld = version.isOlderThan(CldrVersion.v22_1); |
| break; |
| default: |
| break; |
| } |
| if (tooOld) { |
| continue; |
| } else { |
| errln(version + ": " + e.getClass().getSimpleName() + ", " + e.getMessage()); |
| continue; |
| } |
| } |
| // verify that if E is in dtdDataOld, then it is in dtdData, and |
| // has at least the same children and attributes |
| for (Entry<String, Element> entry : dtdDataOld |
| .getElementFromName().entrySet()) { |
| Element oldElement = entry.getValue(); |
| Element newElement = currentElementFromName.get(entry |
| .getKey()); |
| if (knownElementExceptions.contains(Pair.of(type.toString(), oldElement.getName()))) { |
| continue; |
| } |
| if (assertNotNull(type |
| + " DTD for trunk must be superset of v" + version |
| + ", and must contain «" + oldElement.getName() |
| + "»", newElement)) { |
| // TODO Check order also |
| for (Element oldChild : oldElement.getChildren() |
| .keySet()) { |
| if (oldChild == null) { |
| continue; |
| } |
| Element newChild = newElement |
| .getChildNamed(oldChild.getName()); |
| |
| if (knownChildExceptions.contains(Pair.of(newElement.getName(), oldChild.getName()))) { |
| continue; |
| } |
| assertNotNull( |
| type + " DTD - Trunk children of «" |
| + newElement.getName() |
| + "» must be superset of v" |
| + version + ", and must contain «" |
| + oldChild.getName() + "»", |
| newChild); |
| } |
| for (Attribute oldAttribute : oldElement |
| .getAttributes().keySet()) { |
| Attribute newAttribute = newElement |
| .getAttributeNamed(oldAttribute.getName()); |
| |
| if (knownAttributeExceptions.contains(Pair.of(newElement.getName(), oldAttribute.getName()))) { |
| continue; |
| } |
| assertNotNull( |
| type + " DTD - Trunk attributes of «" |
| + newElement.getName() |
| + "» must be superset of v" |
| + version + ", and must contain «" |
| + oldAttribute.getName() + "»", |
| newAttribute); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Compare each path to each other path for every single file in CLDR |
| */ |
| public void TestDtdComparison() { |
| // try some simple paths for regression |
| |
| sortPaths( |
| DtdData.getInstance(DtdType.ldml).getDtdComparator(null), |
| "//ldml/dates/calendars/calendar[@type=\"generic\"]/dateTimeFormats/dateTimeFormatLength[@type=\"full\"]/dateTimeFormat[@type=\"standard\"]/pattern[@type=\"standard\"]", |
| "//ldml/dates/calendars/calendar[@type=\"generic\"]/dateTimeFormats"); |
| |
| sortPaths( |
| DtdData.getInstance(DtdType.supplementalData).getDtdComparator( |
| null), |
| "//supplementalData/territoryContainment/group[@type=\"419\"][@contains=\"013 029 005\"][@grouping=\"true\"]", |
| "//supplementalData/territoryContainment/group[@type=\"003\"][@contains=\"021 013 029\"][@grouping=\"true\"]"); |
| |
| //checkDtdComparatorForResource("TestBasic_ja.xml", DtdType.ldmlICU); |
| } |
| |
| public void TestDtdComparisonsAll() { |
| if (getInclusion() <= 5) { // Only run this test in exhaustive mode. |
| return; |
| } |
| for (File file : CLDRConfig.getInstance().getAllCLDRFilesEndingWith(".xml")) { |
| checkDtdComparatorFor(file, null); |
| } |
| } |
| |
| public void checkDtdComparatorForResource(String fileToRead, |
| DtdType overrideDtdType) { |
| MyHandler myHandler = new MyHandler(overrideDtdType); |
| XMLFileReader xfr = new XMLFileReader().setHandler(myHandler); |
| try { |
| myHandler.fileName = fileToRead; |
| xfr.read(myHandler.fileName, TestBasic.class, -1, true); |
| logln(myHandler.fileName); |
| } catch (Exception e) { |
| Throwable t = e; |
| StringBuilder b = new StringBuilder(); |
| String indent = ""; |
| while (t != null) { |
| b.append(indent).append(t.getMessage()); |
| indent = indent.isEmpty() ? "\n\t\t" : indent + "\t"; |
| t = t.getCause(); |
| } |
| errln(b.toString()); |
| return; |
| } |
| DtdData dtdData = DtdData.getInstance(myHandler.dtdType); |
| sortPaths(dtdData.getDtdComparator(null), myHandler.data); |
| } |
| |
| public void checkDtdComparatorFor(File fileToRead, DtdType overrideDtdType) { |
| MyHandler myHandler = new MyHandler(overrideDtdType); |
| XMLFileReader xfr = new XMLFileReader().setHandler(myHandler); |
| try { |
| myHandler.fileName = fileToRead.getCanonicalPath(); |
| xfr.read(myHandler.fileName, -1, true); |
| logln(myHandler.fileName); |
| } catch (Exception e) { |
| Throwable t = e; |
| StringBuilder b = new StringBuilder(); |
| String indent = ""; |
| while (t != null) { |
| b.append(indent).append(t.getMessage()); |
| indent = indent.isEmpty() ? "\n\t\t" : indent + "\t"; |
| t = t.getCause(); |
| } |
| errln(b.toString()); |
| return; |
| } |
| DtdData dtdData = DtdData.getInstance(myHandler.dtdType); |
| sortPaths(dtdData.getDtdComparator(null), myHandler.data); |
| } |
| |
| static class MyHandler extends XMLFileReader.SimpleHandler { |
| private String fileName; |
| private DtdType dtdType; |
| private final Set<String> data = new LinkedHashSet<>(); |
| |
| public MyHandler(DtdType overrideDtdType) { |
| dtdType = overrideDtdType; |
| } |
| |
| public void handlePathValue(String path, String value) { |
| if (dtdType == null) { |
| try { |
| dtdType = DtdType.fromPath(path); |
| } catch (Exception e) { |
| throw new IllegalArgumentException( |
| "Can't read " + fileName, e); |
| } |
| } |
| data.add(path); |
| } |
| } |
| |
| public void sortPaths(Comparator<String> dc, Collection<String> paths) { |
| String[] array = paths.toArray(new String[paths.size()]); |
| sortPaths(dc, array); |
| } |
| |
| public void sortPaths(Comparator<String> dc, String... array) { |
| Arrays.sort(array, 0, array.length, dc); |
| } |
| // public void TestNewDtdData() moved to TestDtdData |
| } |