• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ******************************************************************************
3  * Copyright (C) 2004-2013, International Business Machines Corporation and   *
4  * others. All Rights Reserved.                                               *
5  ******************************************************************************
6  */
7 package org.unicode.cldr.tool;
8 
9 import java.io.BufferedReader;
10 import java.io.File;
11 import java.io.IOException;
12 import java.io.PrintWriter;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import java.util.TreeMap;
26 import java.util.TreeSet;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 
30 import org.unicode.cldr.draft.FileUtilities;
31 import org.unicode.cldr.util.CLDRConfig;
32 import org.unicode.cldr.util.CLDRFile;
33 import org.unicode.cldr.util.CLDRPaths;
34 import org.unicode.cldr.util.CldrUtility;
35 import org.unicode.cldr.util.Factory;
36 import org.unicode.cldr.util.Iso639Data;
37 import org.unicode.cldr.util.IsoCurrencyParser;
38 import org.unicode.cldr.util.IsoCurrencyParser.Data;
39 import org.unicode.cldr.util.IsoRegionData;
40 import org.unicode.cldr.util.Level;
41 import org.unicode.cldr.util.Log;
42 import org.unicode.cldr.util.Organization;
43 import org.unicode.cldr.util.Pair;
44 import org.unicode.cldr.util.PatternCache;
45 import org.unicode.cldr.util.StandardCodes;
46 import org.unicode.cldr.util.SupplementalDataInfo;
47 import org.unicode.cldr.util.Tabber;
48 import org.unicode.cldr.util.TimezoneFormatter;
49 import org.unicode.cldr.util.UnicodeSetPrettyPrinter;
50 import org.unicode.cldr.util.XPathParts;
51 import org.unicode.cldr.util.props.ICUPropertyFactory;
52 
53 import com.google.common.base.Joiner;
54 import com.google.common.collect.ImmutableMap;
55 import com.ibm.icu.dev.util.UnicodeMap;
56 import com.ibm.icu.dev.util.UnicodeMapIterator;
57 import com.ibm.icu.impl.Relation;
58 import com.ibm.icu.impl.Row;
59 import com.ibm.icu.impl.Row.R2;
60 import com.ibm.icu.impl.Row.R3;
61 import com.ibm.icu.text.Collator;
62 import com.ibm.icu.text.NumberFormat;
63 import com.ibm.icu.text.RuleBasedCollator;
64 import com.ibm.icu.text.Transform;
65 import com.ibm.icu.text.UnicodeSet;
66 import com.ibm.icu.util.ULocale;
67 
68 /**
69  * Simple program to count the amount of data in CLDR. Internal Use.
70  */
71 public class CountItems {
72 
73     private static final Collator ROOT_PRIMARY_COLLATOR = Collator.getInstance(ULocale.ROOT)
74         .setStrength2(Collator.PRIMARY);
75 
76     static final String needsTranslationString = "America/Buenos_Aires " // America/Rio_Branco
77         + " America/Manaus America/Belem "
78         + " America/Campo_Grande America/Sao_Paulo "
79         + " Australia/Perth Australia/Darwin Australia/Brisbane Australia/Adelaide Australia/Sydney Australia/Hobart "
80         + " America/Vancouver America/Edmonton America/Regina America/Winnipeg America/Toronto America/Halifax America/St_Johns "
81         + " Asia/Jakarta "
82         + " America/Tijuana America/Hermosillo America/Chihuahua America/Mexico_City "
83         + " Europe/Moscow Europe/Kaliningrad Europe/Moscow Asia/Yekaterinburg Asia/Novosibirsk Asia/Yakutsk Asia/Vladivostok"
84         + " Pacific/Honolulu America/Indiana/Indianapolis America/Anchorage "
85         + " America/Los_Angeles America/Phoenix America/Denver America/Chicago America/Indianapolis"
86         + " America/New_York";
87 
88     static final ImmutableMap<String, String> country_map = ImmutableMap.<String, String>builder()
89         .put("AQ", "http://www.worldtimezone.com/time-antarctica24.php")
90         .put("AR", "http://www.worldtimezone.com/time-south-america24.php")
91         .put("AU", "http://www.worldtimezone.com/time-australia24.php")
92         .put("BR", "http://www.worldtimezone.com/time-south-america24.php")
93         .put("CA", "http://www.worldtimezone.com/time-canada24.php")
94         .put("CD", "http://www.worldtimezone.com/time-africa24.php")
95         .put("CL", "http://www.worldtimezone.com/time-south-america24.php")
96         .put("CN", "http://www.worldtimezone.com/time-cis24.php")
97         .put("EC", "http://www.worldtimezone.com/time-south-america24.php")
98         .put("ES", "http://www.worldtimezone.com/time-europe24.php")
99         .put("FM", "http://www.worldtimezone.com/time-oceania24.php")
100         .put("GL", "http://www.worldtimezone.com/index24.php")
101         .put("ID", "http://www.worldtimezone.com/time-asia24.php")
102         .put("KI", "http://www.worldtimezone.com/time-oceania24.php")
103         .put("KZ", "http://www.worldtimezone.com/time-cis24.php")
104         .put("MH", "http://www.worldtimezone.com/time-oceania24.php")
105         .put("MN", "http://www.worldtimezone.com/time-cis24.php")
106         .put("MX", "http://www.worldtimezone.com/index24.php")
107         .put("MY", "http://www.worldtimezone.com/time-asia24.php")
108         .put("NZ", "http://www.worldtimezone.com/time-oceania24.php")
109         .put("PF", "http://www.worldtimezone.com/time-oceania24.php")
110         .put("PT", "http://www.worldtimezone.com/time-europe24.php")
111         .put("RU", "http://www.worldtimezone.com/time-russia24.php")
112         .put("SJ", "http://www.worldtimezone.com/index24.php")
113         .put("UA", "http://www.worldtimezone.com/time-cis24.php")
114         .put("UM", "http://www.worldtimezone.com/time-oceania24.php")
115         .put("US", "http://www.worldtimezone.com/time-usa24.php")
116         .put("UZ", "http://www.worldtimezone.com/time-cis24.php")
117         .build();
118 
119     /**
120      * Count the data.
121      *
122      * @throws IOException
123      */
main(String[] args)124     public static void main(String[] args) throws Exception {
125         double deltaTime = System.currentTimeMillis();
126         try {
127             String methodName = System.getProperty("method");
128             if (methodName != null) {
129                 CldrUtility.callMethod(methodName, CountItems.class);
130             } else {
131                 ShowZoneEquivalences.getZoneEquivalences();
132             }
133         } finally {
134             deltaTime = System.currentTimeMillis() - deltaTime;
135             System.out.println("Elapsed: " + deltaTime / 1000.0 + " seconds");
136             System.out.println("Done");
137         }
138     }
139 
subheader(PrintWriter out, Tabber tabber)140     static void subheader(PrintWriter out, Tabber tabber) {
141         // out.println("<tr><td colspan='6' class='gap'>&nbsp;</td></tr>");
142         out.println(tabber.process("Cnty" + "\t" + "Grp" + "\t" + "ZoneID" + "\t"
143             + "Formatted ID" + "\t" + "MaxOffset" + "\t" + "MinOffset"));
144     }
145 
146     /**
147      *
148      */
getPatternBlocks()149     private static void getPatternBlocks() {
150         UnicodeSet patterns = new UnicodeSet("[:pattern_syntax:]");
151         UnicodeSet unassigned = new UnicodeSet("[:unassigned:]");
152         UnicodeSet punassigned = new UnicodeSet(patterns).retainAll(unassigned);
153         UnicodeMap<String> blocks = ICUPropertyFactory.make().getProperty("block")
154             .getUnicodeMap();
155         blocks.setMissing("<Reserved-Block>");
156         // blocks.composeWith(new UnicodeMap().putAll(new UnicodeSet(patterns).retainAll(unassigned),"<reserved>"),
157         // new UnicodeMap.Composer() {
158         // public Object compose(int codePoint, Object a, Object b) {
159         // if (a == null) {
160         // return b;
161         // }
162         // if (b == null) {
163         // return a;
164         // }
165         // return a.toString() + " " + b.toString();
166         // }});
167         for (UnicodeMapIterator<String> it = new UnicodeMapIterator<>(blocks); it
168             .nextRange();) {
169             UnicodeSet range = new UnicodeSet(it.codepoint, it.codepointEnd);
170             boolean hasPat = range.containsSome(patterns);
171             String prefix = !hasPat ? "Not-Syntax"
172                 : !range.containsSome(unassigned) ? "Closed" : !range
173                     .containsSome(punassigned) ? "Closed2" : "Open";
174 
175             boolean show = (prefix.equals("Open") || prefix.equals("Closed2"));
176 
177             if (show)
178                 System.out.println();
179             System.out.println(prefix + "\t" + range + "\t" + it.value);
180             if (show) {
181                 System.out.println(new UnicodeMap<String>().putAll(unassigned, "<reserved>")
182                     .putAll(punassigned, "<reserved-for-syntax>").setMissing(
183                         "<assigned>")
184                     .putAll(range.complement(), null));
185             }
186         }
187     }
188 
189     /**
190      * @throws IOException
191      *
192      */
showExemplars()193     private static void showExemplars() throws IOException {
194         PrintWriter out = FileUtilities.openUTF8Writer(CLDRPaths.GEN_DIRECTORY, "fixed_exemplars.txt");
195         Factory cldrFactory = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
196         Set<String> locales = cldrFactory.getAvailable();
197         for (Iterator<String> it = locales.iterator(); it.hasNext();) {
198             System.out.print('.');
199             String locale = it.next();
200             CLDRFile cldrfile = cldrFactory.make(locale, false);
201             String v = cldrfile
202                 .getStringValue("//ldml/characters/exemplarCharacters");
203             if (v == null)
204                 continue;
205             UnicodeSet exemplars = new UnicodeSet(v);
206             if (exemplars.size() != 0 && exemplars.size() < 500) {
207                 Collator col = Collator.getInstance(new ULocale(locale));
208                 Collator spaceCol = Collator.getInstance(new ULocale(locale));
209                 spaceCol.setStrength(Collator.PRIMARY);
210                 out.println(locale + ":\t\u200E" + v + '\u200E');
211                 String fixed = new UnicodeSetPrettyPrinter()
212                     .setOrdering(col != null ? col : Collator.getInstance(ULocale.ROOT))
213                     .setSpaceComparator(spaceCol != null ? spaceCol : ROOT_PRIMARY_COLLATOR)
214                     .setCompressRanges(true)
215                     .format(exemplars);
216                 out.println(" =>\t\u200E" + fixed + '\u200E');
217 
218                 verifyEquality(exemplars, new UnicodeSet(fixed));
219                 out.flush();
220             }
221         }
222         out.close();
223     }
224 
225     /**
226      *
227      */
verifyEquality(UnicodeSet exemplars, UnicodeSet others)228     private static void verifyEquality(UnicodeSet exemplars, UnicodeSet others) {
229         if (others.equals(exemplars))
230             return;
231         System.out.println("FAIL\ta-b\t"
232             + new UnicodeSet(exemplars).removeAll(others));
233         System.out.println("\tb-a\t" + new UnicodeSet(others).removeAll(exemplars));
234     }
235 
236     /**
237      *
238      */
generateSupplementalCurrencyItems()239     public static void generateSupplementalCurrencyItems() {
240         IsoCurrencyParser isoCurrencyParser = IsoCurrencyParser.getInstance();
241         Relation<String, Data> codeList = isoCurrencyParser.getCodeList();
242         Map<String, String> numericTocurrencyCode = new TreeMap<>();
243         StringBuffer list = new StringBuffer();
244 
245         for (Iterator<String> it = codeList.keySet().iterator(); it.hasNext();) {
246             String currencyCode = it.next();
247             int numericCode = -1;
248             Set<Data> dataSet = codeList.getAll(currencyCode);
249             boolean first = true;
250             for (Data data : dataSet) {
251                 if (first) {
252                     first = false;
253                 }
254                 numericCode = data.getNumericCode();
255             }
256 
257             String strNumCode = "" + numericCode;
258             String otherCode = numericTocurrencyCode.get(strNumCode);
259             if (otherCode != null) {
260                 System.out.println("Warning: duplicate code " + otherCode +
261                     "for " + numericCode);
262             }
263             numericTocurrencyCode.put(strNumCode, currencyCode);
264             if (list.length() != 0)
265                 list.append(" ");
266             String currencyLine = "<currencyCodes type=" + "\"" + currencyCode +
267                 "\"" + " numeric=" + "\"" + numericCode + "\"/>";
268             list.append(currencyLine);
269             System.out.println(currencyLine);
270 
271         }
272         System.out.println();
273 
274     }
275 
276     /**
277      *
278      */
generateCurrencyItems()279     public static void generateCurrencyItems() {
280         IsoCurrencyParser isoCurrencyParser = IsoCurrencyParser.getInstance();
281         Relation<String, Data> codeList = isoCurrencyParser.getCodeList();
282         StringBuffer list = new StringBuffer();
283         for (Iterator<String> it = codeList.keySet().iterator(); it.hasNext();) {
284             // String lastField = (String) it.next();
285             // String zone = (String) fullMap.get(lastField);
286             String currencyCode = it.next();
287             Set<Data> dataSet = codeList.getAll(currencyCode);
288             boolean first = true;
289             for (Data data : dataSet) {
290                 if (first) {
291                     System.out.print(currencyCode);
292                     first = false;
293                 }
294                 System.out.println("\t" + data);
295             }
296 
297             if (list.length() != 0)
298                 list.append(" ");
299             list.append(currencyCode);
300 
301         }
302         System.out.println();
303         String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t\t";
304         // "((?:[-+_A-Za-z0-9]+[/])+[A-Za-z0-9])[-+_A-Za-z0-9]*"
305         String broken = CldrUtility.breakLines(list.toString(), sep, PatternCache.get(
306             "([A-Z])[A-Z][A-Z]").matcher(""), 80);
307         assert (list.toString().equals(broken.replace(sep, " ")));
308         //System.out.println("\t\t\t<variable id=\"$currency\" type=\"choice\">"
309         //    + broken + CldrUtility.LINE_SEPARATOR + "\t\t\t</variable>");
310         Set<String> isoTextFileCodes = StandardCodes.make().getAvailableCodes(
311             "currency");
312         Set<String> temp = new TreeSet<>(codeList.keySet());
313         temp.removeAll(isoTextFileCodes);
314         if (temp.size() != 0) {
315             throw new IllegalArgumentException("Missing from ISO4217.txt file: " + temp);
316         }
317     }
318 
genSupplementalZoneData()319     public static void genSupplementalZoneData() throws IOException {
320         genSupplementalZoneData(false);
321     }
322 
genSupplementalZoneData(boolean skipUnaliased)323     public static void genSupplementalZoneData(boolean skipUnaliased)
324         throws IOException {
325         RuleBasedCollator col = (RuleBasedCollator) Collator.getInstance();
326         col.setNumericCollation(true);
327         StandardCodes sc = StandardCodes.make();
328         Map<String, String> zone_country = sc.getZoneToCounty();
329         Map<String, Set<String>> country_zone = sc.getCountryToZoneSet();
330         Factory cldrFactory = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
331         CLDRFile english = cldrFactory.make("en", true);
332 
333         writeZonePrettyPath(col, zone_country, english);
334         writeMetazonePrettyPath();
335 
336         Map<String, String> old_new = sc.getZoneLinkold_new();
337         Map<String, Set<String>> new_old = new TreeMap<>();
338 
339         for (Iterator<String> it = old_new.keySet().iterator(); it.hasNext();) {
340             String old = it.next();
341             String newOne = old_new.get(old);
342             Set<String> oldSet = new_old.get(newOne);
343             if (oldSet == null)
344                 new_old.put(newOne, oldSet = new TreeSet<>());
345             oldSet.add(old);
346         }
347         Map<String, String> fullMap = new TreeMap<>(col);
348         for (Iterator<String> it = zone_country.keySet().iterator(); it.hasNext();) {
349             String zone = it.next();
350             String defaultName = TimezoneFormatter.getFallbackName(zone);
351             Object already = fullMap.get(defaultName);
352             if (already != null)
353                 System.out.println("CONFLICT: " + already + ", " + zone);
354             fullMap.put(defaultName, zone);
355         }
356         // fullSet.addAll(zone_country.keySet());
357         // fullSet.addAll(new_old.keySet());
358 
359         System.out
360             .println("<!-- Generated by org.unicode.cldr.tool.CountItems -->");
361         System.out.println("<supplementalData>");
362         System.out.println("\t<timezoneData>");
363         System.out.println();
364 
365         Set<String> multizone = new TreeSet<>();
366         for (Iterator<String> it = country_zone.keySet().iterator(); it.hasNext();) {
367             String country = it.next();
368             Set<String> zones = country_zone.get(country);
369             if (zones != null && zones.size() != 1)
370                 multizone.add(country);
371         }
372 
373         System.out.println("\t\t<zoneFormatting multizone=\""
374             + toString(multizone, " ") + "\"" + " tzidVersion=\""
375             + sc.getZoneVersion() + "\"" + ">");
376 
377         Set<String> orderedSet = new TreeSet<>(col);
378         orderedSet.addAll(zone_country.keySet());
379         orderedSet.addAll(sc.getDeprecatedZoneIDs());
380         StringBuffer tzid = new StringBuffer();
381 
382         for (Iterator<String> it = orderedSet.iterator(); it.hasNext();) {
383             // String lastField = (String) it.next();
384             // String zone = (String) fullMap.get(lastField);
385             String zone = it.next();
386             if (tzid.length() != 0)
387                 tzid.append(' ');
388             tzid.append(zone);
389 
390             String country = zone_country.get(zone);
391             if (country == null)
392                 continue; // skip deprecated
393 
394             Set<String> aliases = new_old.get(zone);
395             if (aliases != null) {
396                 aliases = new TreeSet<>(aliases);
397                 aliases.remove(zone);
398             }
399             if (skipUnaliased)
400                 if (aliases == null || aliases.size() == 0)
401                     continue;
402 
403             System.out.println("\t\t\t<zoneItem"
404                 + " type=\""
405                 + zone
406                 + "\""
407                 + " territory=\""
408                 + country
409                 + "\""
410                 + (aliases != null && aliases.size() > 0 ? " aliases=\""
411                     + toString(aliases, " ") + "\"" : "")
412                 + "/>");
413         }
414 
415         System.out.println("\t\t</zoneFormatting>");
416         System.out.println();
417         System.out.println("\t</timezoneData>");
418         System.out.println("</supplementalData>");
419         System.out.println();
420         String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t\t";
421         // "((?:[-+_A-Za-z0-9]+[/])+[A-Za-z0-9])[-+_A-Za-z0-9]*"
422         String broken = CldrUtility.breakLines(tzid, sep, PatternCache.get(
423             "((?:[-+_A-Za-z0-9]+[/])+[-+_A-Za-z0-9])[-+_A-Za-z0-9]*").matcher(""),
424             80);
425         assert (tzid.toString().equals(broken.replace(sep, " ")));
426         System.out.println("\t\t\t<variable id=\"$tzid\" type=\"choice\">" + broken
427             + CldrUtility.LINE_SEPARATOR + "\t\t\t</variable>");
428     }
429 
writeMetazonePrettyPath()430     public static void writeMetazonePrettyPath() {
431         CLDRConfig testInfo = ToolConfig.getToolInstance();
432         Map<String, Map<String, String>> map = testInfo.getSupplementalDataInfo().getMetazoneToRegionToZone();
433         Map zoneToCountry = testInfo.getStandardCodes().getZoneToCounty();
434         Set<Pair<String, String>> results = new TreeSet<>();
435         Map<String, String> countryToContinent = getCountryToContinent(testInfo.getSupplementalDataInfo(),
436             testInfo.getEnglish());
437 
438         for (String metazone : map.keySet()) {
439             Map<String, String> regionToZone = map.get(metazone);
440             String zone = regionToZone.get("001");
441             if (zone == null) {
442                 throw new IllegalArgumentException("Missing 001 for metazone " + metazone);
443             }
444             String continent = zone.split("/")[0];
445 
446             final Object country = zoneToCountry.get(zone);
447             results.add(new Pair<>(continent + "\t" + country + "\t" + countryToContinent.get(country)
448                 + "\t" + metazone, metazone));
449         }
450         for (Pair<String, String> line : results) {
451             System.out.println("'" + line.getSecond() + "'\t>\t'\t" + line.getFirst() + "\t'");
452         }
453     }
454 
getCountryToContinent(SupplementalDataInfo supplementalDataInfo, CLDRFile english)455     private static Map<String, String> getCountryToContinent(SupplementalDataInfo supplementalDataInfo, CLDRFile english) {
456         Relation<String, String> countryToContinent = Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
457         Set<String> continents = new HashSet<>(Arrays.asList("002", "019", "142", "150", "009"));
458         // note: we don't need more than 3 levels
459         for (String continent : continents) {
460             final Set<String> subcontinents = supplementalDataInfo.getContained(continent);
461             countryToContinent.putAll(subcontinents, continent);
462             for (String subcontinent : subcontinents) {
463                 if (subcontinent.equals("EU")) continue;
464                 final Set<String> countries = supplementalDataInfo.getContained(subcontinent);
465                 countryToContinent.putAll(countries, continent);
466             }
467         }
468         // convert to map
469         Map<String, String> results = new TreeMap<>();
470         for (String item : countryToContinent.keySet()) {
471             final Set<String> containees = countryToContinent.getAll(item);
472             if (containees.size() != 1) {
473                 throw new IllegalArgumentException(item + "\t" + containees);
474             }
475             results.put(item, english.getName(CLDRFile.TERRITORY_NAME, containees.iterator().next()));
476         }
477         return results;
478     }
479 
writeZonePrettyPath(RuleBasedCollator col, Map<String, String> zone_country, CLDRFile english)480     private static void writeZonePrettyPath(RuleBasedCollator col, Map<String, String> zone_country,
481         CLDRFile english) throws IOException {
482         System.out.println("Writing zonePrettyPath");
483         Set<String> masked = new HashSet<>();
484         Map<String, String> zoneNew_Old = new TreeMap<>(col);
485         String lastZone = "XXX";
486         for (String zone : new TreeSet<>(zone_country.keySet())) {
487             String[] parts = zone.split("/");
488             String newPrefix = zone_country.get(zone); // english.getName("tzid", zone_country.get(zone),
489             // false).replace(' ', '_');
490             if (newPrefix.equals("001")) {
491                 newPrefix = "ZZ";
492             }
493             parts[0] = newPrefix;
494             String newName;
495             if (parts.length > 2) {
496                 System.out.println("\tMultifield: " + zone);
497                 if (parts.length == 3 && parts[1].equals("Argentina")) {
498                     newName = parts[0] + "/" + parts[1];
499                 } else {
500                     newName = CldrUtility.join(parts, "/");
501                 }
502             } else {
503                 newName = CldrUtility.join(parts, "/");
504             }
505             zoneNew_Old.put(newName, zone);
506             if (zone.startsWith(lastZone)) {
507                 masked.add(zone); // find "masked items" and do them first.
508             } else {
509                 lastZone = zone;
510             }
511         }
512 
513         Log.setLog(CLDRPaths.GEN_DIRECTORY + "/supplemental/prettyPathZone.txt");
514         String lastCountry = "";
515         for (int i = 0; i < 2; ++i) {
516             Set<String> orderedList = zoneNew_Old.keySet();
517             if (i == 0) {
518                 Log
519                     .println("# Short IDs for zone names: country code + last part of TZID");
520                 Log
521                     .println("# First are items that would be masked, and are moved forwards and sorted in reverse order");
522                 Log.println();
523                 //Comparator c;
524                 Set<String> temp = new TreeSet<>(new ReverseComparator<>(col));
525                 temp.addAll(orderedList);
526                 orderedList = temp;
527             } else {
528                 Log.println();
529                 Log.println("# Normal items, sorted by country code");
530                 Log.println();
531             }
532 
533             // do masked items first
534 
535             for (String newName : orderedList) {
536                 String oldName = zoneNew_Old.get(newName);
537                 if (masked.contains(oldName) != (i == 0)) {
538                     continue;
539                 }
540                 String newCountry = newName.split("/")[0];
541                 if (!newCountry.equals(lastCountry)) {
542                     Log.println("# " + newCountry + "\t"
543                         + english.getName("territory", newCountry));
544                     lastCountry = newCountry;
545                 }
546                 Log.println("\t'" + oldName + "'\t>\t'" + newName + "';");
547             }
548         }
549         Log.close();
550         System.out.println("Done Writing zonePrettyPath");
551     }
552 
553     public static class ReverseComparator<T> implements Comparator<T> {
554         Comparator<T> other;
555 
ReverseComparator(Comparator<T> other)556         public ReverseComparator(Comparator<T> other) {
557             this.other = other;
558         }
559 
560         @Override
compare(T o1, T o2)561         public int compare(T o1, T o2) {
562             return other.compare(o2, o1);
563         }
564     }
565 
getSubtagVariables2()566     public static void getSubtagVariables2() throws IOException {
567         Log.setLogNoBOM(CLDRPaths.GEN_DIRECTORY + "/supplemental", "supplementalMetadata.xml");
568         BufferedReader oldFile = FileUtilities.openUTF8Reader(CLDRPaths.SUPPLEMENTAL_DIRECTORY, "supplementalMetadata.xml");
569         CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s*<!-- start of data generated with CountItems.*"),
570             Log.getLog(), true);
571 
572         Map<String, String> variableSubstitutions = getVariables(VariableType.partial);
573         for (Entry<String, String> type : variableSubstitutions.entrySet()) {
574             Log.println(type.getValue());
575         }
576 
577         // String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t";
578         // String broken = CldrUtility.breakLines(CldrUtility.join(defaultLocaleContent," "), sep,
579         // PatternCache.get("(\\S)\\S*").matcher(""), 80);
580         //
581         // Log.println("\t\t<defaultContent locales=\"" + broken + "\"");
582         // Log.println("\t\t/>");
583 
584         // Log.println("</supplementalData>");
585         CldrUtility.copyUpTo(oldFile, PatternCache.get("\\s<!-- end of data generated by CountItems.*"), null, true);
586         CldrUtility.copyUpTo(oldFile, null, Log.getLog(), true);
587 
588         Log.close();
589         oldFile.close();
590     }
591 
592     static final SupplementalDataInfo supplementalData = SupplementalDataInfo
593         .getInstance(CLDRPaths.SUPPLEMENTAL_DIRECTORY);
594     static final StandardCodes sc = StandardCodes.make();
595 
getSubtagVariables()596     public static void getSubtagVariables() {
597 //        This section no longer necessary, as it has been replaced by the new attributeValueValidity.xml
598 //
599 //        System.out.println("Validity variables");
600 //        System.out.println("Cut/paste into supplementalMetadata.xml under the line");
601 //        System.out.println("<!-- start of data generated with CountItems tool ...");
602 //        Map<String, String> variableSubstitutions = getVariables(VariableType.partial);
603 
604 //        for (Entry<String, String> type : variableSubstitutions.entrySet()) {
605 //            System.out.println(type.getValue());
606 //        }
607 //        System.out.println("<!-- end of Validity variables generated with CountItems tool ...");
608 //        System.out.println();
609         System.out.println("Language aliases");
610         System.out.println("Cut/paste into supplementalMetadata.xml under the line");
611         System.out.println("<!-- start of data generated with CountItems tool ...");
612 
613         Map<String, Map<String, String>> languageReplacement = StandardCodes.getLStreg().get("language");
614         Map<String, Map<String, R2<List<String>, String>>> localeAliasInfo = supplementalData.getLocaleAliasInfo();
615         Map<String, R2<List<String>, String>> languageAliasInfo = localeAliasInfo.get("language");
616 
617         Set<String> available = Iso639Data.getAvailable();
618         // <languageAlias type="aju" replacement="jrb"/> <!-- Moroccan Judeo-Arabic ⇒ Judeo-Arabic -->
619         Set<String> bad3letter = new HashSet<>();
620         for (String lang : available) {
621             if (lang.length() != 2) continue;
622             String target = lang;
623             Map<String, String> lstregData = languageReplacement.get(lang);
624             if (lstregData == null) {
625                 throw new IllegalArgumentException("illegal language code");
626             } else {
627                 String replacement = lstregData.get("Preferred-Value");
628                 if (replacement != null) {
629                     target = replacement;
630                 }
631             }
632             String alpha3 = Iso639Data.toAlpha3(lang);
633             bad3letter.add(alpha3);
634             String targetAliased;
635             if (languageAliasInfo.containsKey(target)) {
636                 targetAliased = Joiner.on(" ").join(languageAliasInfo.get(target).get0());
637             } else {
638                 targetAliased = target;
639             }
640             System.out.println("\t\t\t<languageAlias type=\"" + alpha3 + "\" replacement=\"" + targetAliased
641                 + "\" reason=\"overlong\"/> <!-- " +
642                 Iso639Data.getNames(target) + " -->");
643         }
644         System.out.println("\t\t\t<!-- Bibliographic -->");
645         TreeMap<String, String> sorted = new TreeMap<>();
646         for (String hasBiblio : Iso639Data.hasBiblio3()) {
647             String biblio = Iso639Data.toBiblio3(hasBiblio);
648             sorted.put(biblio, hasBiblio);
649         }
650         for (Entry<String, String> entry : sorted.entrySet()) {
651             String biblio = entry.getKey();
652             String hasBiblio = entry.getValue();
653             System.out.println("\t\t\t<languageAlias type=\"" + biblio + "\" replacement=\"" + hasBiblio
654                 + "\" reason=\"bibliographic\"/> <!-- " +
655                 Iso639Data.getNames(hasBiblio) + " -->");
656         }
657         System.out.println("<!-- end of Language alises generated with CountItems tool ...");
658 
659         Set<String> encompassed = Iso639Data.getEncompassed();
660         Set<String> macros = Iso639Data.getMacros();
661         Map<String, String> encompassed_macro = new HashMap<>();
662         for (Entry<String, R2<List<String>, String>> typeAndData : languageAliasInfo.entrySet()) {
663             String type = typeAndData.getKey();
664             R2<List<String>, String> data = typeAndData.getValue();
665             List<String> replacements = data.get0();
666             if (!encompassed.contains(type)) continue;
667             if (replacements == null || replacements.size() != 1) continue;
668             String replacement = replacements.get(0);
669             if (macros.contains(replacement)) {
670                 // we have a match, encompassed => replacement
671                 encompassed_macro.put(type, replacement);
672             }
673         }
674         Set<String> missing = new TreeSet<>();
675         missing.addAll(macros);
676         missing.remove("no");
677         missing.remove("sh");
678 
679         missing.removeAll(encompassed_macro.values());
680         if (missing.size() != 0) {
681             for (String missingMacro : missing) {
682                 System.err.println("ERROR: Missing <languageAlias type=\"" + "???" + "\" replacement=\"" + missingMacro
683                     + "\"/> <!-- ??? => " +
684                     Iso639Data.getNames(missingMacro) + " -->");
685                 System.out.println("\tOptions for ???:");
686                 for (String enc : Iso639Data.getEncompassedForMacro(missingMacro)) {
687                     System.out.println("\t" + enc + "\t// " + Iso639Data.getNames(enc));
688                 }
689             }
690         }
691         // verify that every macro language has a encompassed mapping to it
692         // and remember those codes
693 
694         // verify that nobody contains a bad code
695 
696         for (Entry<String, R2<List<String>, String>> typeAndData : languageAliasInfo.entrySet()) {
697             String type = typeAndData.getKey();
698             List<String> replacements = typeAndData.getValue().get0();
699             if (replacements == null) continue;
700             for (String replacement : replacements) {
701                 if (bad3letter.contains(replacement)) {
702                     System.err.println("ERROR: Replacement(s) for type=\"" + type +
703                         "\" contains " + replacement + ", which should be: " + Iso639Data.fromAlpha3(replacement));
704                 }
705             }
706         }
707 
708         // get the bad ISO codes
709 
710         Factory cldrFactory = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
711         CLDRFile english = cldrFactory.make("en", true);
712 
713         Set<String> territories = new TreeSet<>();
714         Relation<String, String> containers = supplementalData.getTerritoryToContained();
715         for (String region : sc.getAvailableCodes("territory")) {
716             if (containers.containsKey(region)) continue;
717             territories.add(region);
718         }
719         System.out.println();
720         System.out.println("Territory aliases");
721         System.out.println("Cut/paste into supplementalMetadata.xml under the line");
722         System.out.println("<!-- start of data generated with CountItems tool ...");
723         //final Map<String, R2<List<String>, String>> territoryAliasInfo = localeAliasInfo.get("territory");
724 
725         addRegions(english, territories, "alpha3", "EA,EU,IC".split(","), new Transform<String, String>() {
726             @Override
727             public String transform(String region) {
728                 return IsoRegionData.get_alpha3(region);
729             }
730         });
731         addRegions(english, territories, "numeric", "AC,CP,DG,EA,EU,IC,TA".split(","), new Transform<String, String>() {
732             @Override
733             public String transform(String region) {
734                 return IsoRegionData.getNumeric(region);
735             }
736         });
737         System.out.println("<!-- end of Territory alises generated with CountItems tool ...");
738         System.out.println();
739         System.out.println("Deprecated codes check (informational)");
740         // check that all deprecated codes are in fact deprecated
741         Map<String, Map<String, Map<String, String>>> fullData = StandardCodes.getLStreg();
742 
743         checkCodes("language", sc, localeAliasInfo, fullData);
744         checkCodes("script", sc, localeAliasInfo, fullData);
745         checkCodes("territory", sc, localeAliasInfo, fullData);
746         System.out.println("End of Deprecated codes check...");
747 
748         // generate mapping equivalences
749         // { "aar", "aar", "aa" }, // Afar
750         // b, t, bcp47
751         System.out.println();
752         System.out.println("Mapping equivalences - (Informational only...)");
753         System.out.println("{ bib , tech , bcp47 }");
754 
755         Set<R3<String, String, String>> rows = new TreeSet<>();
756         for (String lang : Iso639Data.getAvailable()) {
757             String bib = Iso639Data.toBiblio3(lang);
758             String tech = Iso639Data.toAlpha3(lang);
759             R3<String, String, String> row = Row.of(tech, bib, lang);
760             rows.add(row);
761         }
762         for (R3<String, String, String> row : rows) {
763             String tech = row.get0();
764             String bib = row.get1();
765             String lang = row.get2();
766             String name = Iso639Data.getNames(lang).iterator().next(); // english.getName(lang);
767             if ((bib != null && !lang.equals(bib)) || (tech != null && !lang.equals(tech))) {
768                 System.out.println("  { \"" + bib + "\", \"" + tech + "\", \"" + lang + "\" },  // " + name);
769             }
770         }
771         System.out.println("End of Mapping equivalences...");
772 
773         // generate the codeMappings
774         // <codeMappings>
775         // <territoryCodes type="CS" numeric="891" alpha3="SCG" fips10="YI"/>
776 
777         System.out.println();
778         System.out.println("Code Mappings");
779         System.out.println("Cut/paste into supplementaData.xml under the line");
780         System.out.println("<!-- start of data generated with CountItems tool ...");
781         List<String> warnings = new ArrayList<>();
782         territories.add("QO");
783         territories.add("EU");
784         // territories.add("MF");
785         //Map<String, R2<List<String>, String>> territoryAliases = supplementalData.getLocaleAliasInfo().get("territory");
786         Relation<String, String> numeric2region = Relation.of(new HashMap<String, Set<String>>(), TreeSet.class);
787         Relation<String, String> alpha32region = Relation.of(new HashMap<String, Set<String>>(), TreeSet.class);
788         for (String region : territories) {
789             String numeric = IsoRegionData.getNumeric(region);
790             String alpha3 = IsoRegionData.get_alpha3(region);
791             numeric2region.put(numeric, region);
792             alpha32region.put(alpha3, region);
793         }
794 
795         System.out.println("    <codeMappings>");
796 
797         for (String region : territories) {
798             String numeric = IsoRegionData.getNumeric(region);
799             String alpha3 = IsoRegionData.get_alpha3(region);
800             String fips10 = IsoRegionData.get_fips10(region);
801             System.out.println("        <territoryCodes"
802                 + " type=\"" + region + "\""
803                 + (numeric == null ? "" : " numeric=\"" + numeric + "\"")
804                 + (alpha3 == null ? "" : " alpha3=\"" + alpha3 + "\"")
805                 + (fips10 == null || fips10.equals(region) ? "" : " fips10=\"" + fips10 + "\"")
806                 + "/>");
807         }
808         System.out.println("    </codeMappings>");
809         System.out.println("<!-- end of Code Mappings generated with CountItems tool ...");
810         System.out.println(Joiner.on(CldrUtility.LINE_SEPARATOR).join(warnings));
811     }
812 
813     enum VariableType {
814         full, partial
815     }
816 
getVariables(VariableType variableType)817     public static Map<String, String> getVariables(VariableType variableType) {
818         String sep = CldrUtility.LINE_SEPARATOR + "\t\t\t\t";
819         Map<String, String> variableSubstitutions = new LinkedHashMap<>();
820         for (String type : new String[] { "legacy", "territory", "script", "variant" }) {
821             Set<String> i;
822             i = (variableType == VariableType.full || type.equals("legacy")) ? sc.getAvailableCodes(type) : sc.getGoodAvailableCodes(type);
823             addVariable(variableSubstitutions, type, i, sep);
824         }
825 
826         Relation<String, String> bcp47Keys = supplementalData.getBcp47Keys();
827         Relation<R2<String, String>, String> aliases = supplementalData.getBcp47Aliases();
828         for (String key : bcp47Keys.keySet()) {
829             Set<String> keyAliases = aliases.getAll(Row.of(key, ""));
830             Set<String> rawsubtypes = bcp47Keys.getAll(key);
831             TreeSet<String> subtypes = new TreeSet<>();
832             for (String subtype : rawsubtypes) {
833                 Set<String> keySubtypeAliases = aliases.getAll(Row.of(key, subtype));
834                 if (keySubtypeAliases != null) {
835                     subtypes.addAll(keySubtypeAliases);
836                 }
837             }
838             subtypes.addAll(rawsubtypes);
839             String alias = (keyAliases == null ? key : keyAliases.iterator().next()) + "_XXX";
840             addVariable(variableSubstitutions, alias, subtypes, sep);
841         }
842         return variableSubstitutions;
843     }
844 
845     private static final Pattern BreakerPattern = PatternCache.get("([-_A-Za-z0-9])[-/+_A-Za-z0-9]*");
846 
addVariable(Map<String, String> variableSubstitutions, String type, Set<String> sinput, String sep)847     private static void addVariable(Map<String, String> variableSubstitutions, String type, Set<String> sinput,
848         String sep) {
849         TreeSet<String> s = new TreeSet<>(ROOT_PRIMARY_COLLATOR);
850         s.addAll(sinput);
851 
852         StringBuffer b = new StringBuffer();
853         for (String code : s) {
854             if (b.length() != 0)
855                 b.append(' ');
856             b.append(code);
857         }
858         // "((?:[-+_A-Za-z0-9]+[/])+[A-Za-z0-9])[-+_A-Za-z0-9]*"
859         String broken = CldrUtility.breakLines(b, sep, BreakerPattern.matcher(""), 80);
860         assert (b.toString().equals(broken.replace(sep, " ")));
861         variableSubstitutions.put(type, "\t\t\t<variable id=\"$" + type
862             + "\" type=\"choice\">" + broken + CldrUtility.LINE_SEPARATOR + "\t\t\t</variable>");
863     }
864 
checkCodes(String type, StandardCodes sc, Map<String, Map<String, R2<List<String>, String>>> localeAliasInfo, Map<String, Map<String, Map<String, String>>> fullData)865     private static void checkCodes(String type, StandardCodes sc,
866         Map<String, Map<String, R2<List<String>, String>>> localeAliasInfo,
867         Map<String, Map<String, Map<String, String>>> fullData) {
868         Map<String, Map<String, String>> typeData = fullData.get("territory".equals(type) ? "region" : type);
869         Map<String, R2<List<String>, String>> aliasInfo = localeAliasInfo.get(type);
870         for (String code : sc.getAvailableCodes(type)) {
871             Map<String, String> subdata = typeData.get(code);
872             String deprecated = subdata.get("Deprecated");
873             if (deprecated == null) continue;
874             String replacement = subdata.get("Preferred-Value");
875             R2<List<String>, String> supplementalReplacements = aliasInfo.get(code);
876             if (supplementalReplacements == null) {
877                 System.out.println("Deprecated in LSTR, but not in supplementalData: " + type + "\t" + code + "\t"
878                     + replacement);
879             }
880         }
881     }
882 
addRegions(CLDRFile english, Set<String> availableCodes, String codeType, String[] exceptions, Transform<String, String> trans)883     private static void addRegions(CLDRFile english, Set<String> availableCodes, String codeType, String[] exceptions,
884         Transform<String, String> trans) {
885         Set<String> missingRegions = new TreeSet<>();
886         Set<String> exceptionSet = new HashSet<>(Arrays.asList(exceptions));
887         List<String> duplicateDestroyer = new ArrayList<>();
888         for (String region : availableCodes) {
889 
890             if (exceptionSet.contains(region)) continue;
891             String alpha3 = trans.transform(region);
892             if (alpha3 == null) {
893                 missingRegions.add(region);
894                 continue;
895             }
896             Map<String, R2<List<String>, String>> territoryAliasInfo = supplementalData.getLocaleAliasInfo().get("territory");
897             String result;
898             if (territoryAliasInfo.containsKey(region)) {
899                 result = Joiner.on(" ").join(territoryAliasInfo.get(region).get0());
900             } else {
901                 result = region;
902             }
903             String name = english.getName(CLDRFile.TERRITORY_NAME, result);
904             if (!(duplicateDestroyer.contains(alpha3 + result + name))) {
905                 duplicateDestroyer.add(alpha3 + result + name);
906                 System.out.println("\t\t\t<territoryAlias type=\"" + alpha3 + "\" replacement=\"" + result
907                     + "\" reason=\"overlong\"/> <!-- " + name + " -->");
908             }
909         }
910         for (String region : missingRegions) {
911             String name = english.getName(CLDRFile.TERRITORY_NAME, region);
912             System.err.println("ERROR: Missing " + codeType + " code for " + region + "\t" + name);
913         }
914     }
915 
916     /**
917      *
918      */
toString(Collection aliases, String separator)919     private static String toString(Collection aliases, String separator) {
920         StringBuffer result = new StringBuffer();
921         boolean first = true;
922         for (Iterator<Object> it = aliases.iterator(); it.hasNext();) {
923             Object item = it.next();
924             if (first)
925                 first = false;
926             else
927                 result.append(separator);
928             result.append(item);
929         }
930         return result.toString();
931     }
932 
showZoneInfo()933     public static void showZoneInfo() throws IOException {
934         StandardCodes sc = StandardCodes.make();
935         Map<String, String> m = sc.getZoneLinkold_new();
936         int i = 0;
937         System.out.println("/* Generated by org.unicode.cldr.tool.CountItems */");
938         System.out.println();
939         i = 0;
940         System.out.println("/* zoneID, canonical zoneID */");
941         for (Iterator<String> it = m.keySet().iterator(); it.hasNext();) {
942             String old = it.next();
943             String newOne = m.get(old);
944             System.out.println("{\"" + old + "\", \"" + newOne + "\"},");
945             ++i;
946         }
947         System.out.println("/* Total: " + i + " */");
948 
949         System.out.println();
950         i = 0;
951         System.out.println("/* All canonical zoneIDs */");
952         for (Iterator<String> it = sc.getZoneData().keySet().iterator(); it.hasNext();) {
953             String old = it.next();
954             System.out.println("\"" + old + "\",");
955             ++i;
956         }
957         System.out.println("/* Total: " + i + " */");
958 
959         Factory mainCldrFactory = Factory.make(CLDRPaths.COMMON_DIRECTORY + "main"
960             + File.separator, ".*");
961         CLDRFile desiredLocaleFile = mainCldrFactory.make("root", true);
962         String temp = desiredLocaleFile
963             .getFullXPath("//ldml/dates/timeZoneNames/singleCountries");
964         XPathParts parts = XPathParts.getFrozenInstance(temp);
965         String singleCountriesList = parts.findAttributes("singleCountries").get("list");
966         Set<String> singleCountriesSet = new TreeSet<>(CldrUtility.splitList(singleCountriesList, ' '));
967 
968         Map<String, String> zone_countries = StandardCodes.make().getZoneToCounty();
969         Map<String, Set<String>> countries_zoneSet = StandardCodes.make().getCountryToZoneSet();
970         System.out.println();
971         i = 0;
972         System.out.println("/* zoneID, country, isSingle */");
973         for (Iterator<String> it = zone_countries.keySet().iterator(); it.hasNext();) {
974             String old = it.next();
975             String newOne = zone_countries.get(old);
976             Set<String> s = countries_zoneSet.get(newOne);
977             String isSingle = (s != null && s.size() == 1 || singleCountriesSet
978                 .contains(old)) ? "T" : "F";
979             System.out.println("{\"" + old + "\", \"" + newOne + "\", \"" + isSingle
980                 + "\"},");
981             ++i;
982         }
983         System.out.println("/* Total: " + i + " */");
984 
985         if (true)
986             return;
987 
988         Factory cldrFactory = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
989         Map<Organization, Map<String, Level>> platform_locale_status = StandardCodes.make().getLocaleTypes();
990         Map<String, Level> onlyLocales = platform_locale_status.get(Organization.ibm);
991         Set<String> locales = onlyLocales.keySet();
992         CLDRFile english = cldrFactory.make("en", true);
993         for (Iterator<String> it = locales.iterator(); it.hasNext();) {
994             String locale = it.next();
995             System.out.println(locale + "\t" + english.getName(locale) + "\t"
996                 + onlyLocales.get(locale));
997         }
998     }
999 
1000     static final NumberFormat decimal = NumberFormat.getNumberInstance();
1001     static {
1002         decimal.setGroupingUsed(true);
1003     }
1004 
countItems()1005     public static void countItems() {
1006         // CLDRKey.main(new String[]{"-mde.*"});
1007         String dir = CldrUtility.getProperty("source", CLDRPaths.MAIN_DIRECTORY);
1008         Factory cldrFactory = Factory.make(dir, ".*");
1009         countItems(cldrFactory, false);
1010     }
1011 
1012     /**
1013      * @param cldrFactory
1014      * @param resolved
1015      */
countItems(Factory cldrFactory, boolean resolved)1016     private static int countItems(Factory cldrFactory, boolean resolved) {
1017         int count = 0;
1018         int resolvedCount = 0;
1019         Set<String> locales = cldrFactory.getAvailable();
1020         Set<String> keys = new HashSet<>();
1021         Set<String> values = new HashSet<>();
1022         Set<String> fullpaths = new HashSet<>();
1023         Matcher alt = CLDRFile.ALT_PROPOSED_PATTERN.matcher("");
1024 
1025         Set<String> temp = new HashSet<>();
1026         for (Iterator<String> it = locales.iterator(); it.hasNext();) {
1027             String locale = it.next();
1028             if (CLDRFile.isSupplementalName(locale))
1029                 continue;
1030             CLDRFile item = cldrFactory.make(locale, false);
1031 
1032             temp.clear();
1033             for (Iterator<String> it2 = item.iterator(); it2.hasNext();) {
1034                 String path = it2.next();
1035                 if (alt.reset(path).matches()) {
1036                     continue;
1037                 }
1038                 temp.add(path);
1039                 keys.add(path);
1040                 values.add(item.getStringValue(path));
1041                 fullpaths.add(item.getFullXPath(path));
1042             }
1043             int current = temp.size();
1044 
1045             CLDRFile itemResolved = cldrFactory.make(locale, true);
1046             temp.clear();
1047             itemResolved.forEach(temp::add);
1048             int resolvedCurrent = temp.size();
1049 
1050             System.out.println(locale + "\tPlain:\t" + current + "\tResolved:\t"
1051                 + resolvedCurrent + "\tUnique Paths:\t" + keys.size()
1052                 + "\tUnique Values:\t" + values.size() + "\tUnique Full Paths:\t"
1053                 + fullpaths.size());
1054             count += current;
1055             resolvedCount += resolvedCurrent;
1056         }
1057         System.out.println("Total Items\t" + decimal.format(count));
1058         System.out
1059             .println("Total Resolved Items\t" + decimal.format(resolvedCount));
1060         System.out.println("Unique Paths\t" + decimal.format(keys.size()));
1061         System.out.println("Unique Values\t" + decimal.format(values.size()));
1062         System.out
1063             .println("Unique Full Paths\t" + decimal.format(fullpaths.size()));
1064         return count;
1065     }
1066 
1067 }
1068