• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import com.google.common.cache.CacheBuilder;
4 import com.google.common.cache.CacheLoader;
5 import com.google.common.cache.LoadingCache;
6 import com.google.common.collect.ImmutableSet;
7 import com.ibm.icu.dev.test.TestFmwk;
8 import com.ibm.icu.dev.test.TestLog;
9 import com.ibm.icu.text.Collator;
10 import com.ibm.icu.text.RuleBasedCollator;
11 import com.ibm.icu.util.ULocale;
12 import com.ibm.icu.util.VersionInfo;
13 import java.io.File;
14 import java.io.FilenameFilter;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Comparator;
18 import java.util.HashSet;
19 import java.util.LinkedHashSet;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.Set;
25 import java.util.concurrent.ConcurrentHashMap;
26 import org.unicode.cldr.test.CheckCLDR.Phase;
27 
28 /**
29  * Basic information about the CLDR environment. Use CLDRConfig.getInstance() to create your
30  * instance.
31  *
32  * <p>Special notes: - Within the Survey Tool, a special subclass of this class named CLDRConfigImpl
33  * is used instead, which see. - Within unit tests, -DCLDR_ENVIRONMENT=UNITTEST is set, which
34  * prevents the use of CLDRConfigImpl
35  */
36 public class CLDRConfig extends Properties {
37     public static boolean SKIP_SEED = System.getProperty("CLDR_SKIP_SEED") != null;
38     private static final long serialVersionUID = -2605254975303398336L;
39     public static boolean DEBUG = false;
40     /** This is the special implementation which will be used, i.e. CLDRConfigImpl */
41     public static final String SUBCLASS = CLDRConfig.class.getName() + "Impl";
42 
43     /** What environment is CLDR in? */
44     public enum Environment {
45         LOCAL, // < == unknown.
46         SMOKETEST, // staging (SurveyTool) area
47         PRODUCTION, // production (SurveyTool) server!
48         UNITTEST // unit test setting
49     }
50 
51     public static final class CLDRConfigHelper {
make()52         private static CLDRConfig make() {
53             CLDRConfig instance = null;
54             final String env = System.getProperty("CLDR_ENVIRONMENT");
55             if (env != null && env.equals(Environment.UNITTEST.name())) {
56                 // For unittests, skip the following
57                 if (DEBUG) {
58                     System.err.println("-DCLDR_ENVIRONMENT=" + env + " - not loading " + SUBCLASS);
59                 }
60             } else {
61                 // This is the branch for SurveyTool
62                 try {
63                     // System.err.println("Attempting to new up a " + SUBCLASS);
64                     instance = (CLDRConfig) (Class.forName(SUBCLASS).newInstance());
65 
66                     if (instance != null) {
67                         System.err.println(
68                                 "Using CLDRConfig: "
69                                         + instance.toString()
70                                         + " - "
71                                         + instance.getClass().getName());
72                     } else {
73                         if (DEBUG) {
74                             // Probably occurred because ( config.getEnvironment() ==
75                             // Environment.UNITTEST )
76                             // see CLDRConfigImpl
77                             System.err.println(
78                                     "Note: CLDRConfig Subclass "
79                                             + SUBCLASS
80                                             + ".newInstance() returned NULL "
81                                             + "( this is OK if we aren't inside the SurveyTool's web server )");
82                         }
83                     }
84                 } catch (ClassNotFoundException e) {
85                     // Expected - when not under cldr-apps, this class doesn't exist.
86                 } catch (InstantiationException | IllegalAccessException e) {
87                     // TODO: log a useful message
88                 }
89             }
90             if (instance == null) {
91                 // this is the "normal" branch for tools and such
92                 instance = new CLDRConfig();
93                 CldrUtility.checkValidDirectory(
94                         instance.getProperty(CldrUtility.DIR_KEY),
95                         "You have to set -DCLDR_DIR=<validdirectory>");
96             }
97             return instance;
98         }
99 
100         static final CLDRConfig SINGLETON = make();
101     }
102 
103     /**
104      * Main getter for the singleton CLDRConfig.
105      *
106      * @return
107      */
getInstance()108     public static CLDRConfig getInstance() {
109         return CLDRConfigHelper.SINGLETON;
110     }
111 
112     String initStack = null;
113 
CLDRConfig()114     protected CLDRConfig() {
115         initStack = StackTracker.currentStack();
116     }
117 
118     /**
119      * This returns the stacktrace of the first caller to getInstance(), for debugging.
120      *
121      * @return
122      */
getInitStack()123     public String getInitStack() {
124         return initStack;
125     }
126 
127     private Phase phase = null; // default
128 
129     private LoadingCache<String, CLDRFile> cldrFileResolvedCache =
130             CacheBuilder.newBuilder()
131                     .maximumSize(200)
132                     .build(
133                             new CacheLoader<String, CLDRFile>() {
134                                 @Override
135                                 public CLDRFile load(String locale) {
136                                     return getFullCldrFactory().make(locale, true);
137                                 }
138                             });
139 
140     // Unresolved CLDRFiles are smaller than resolved, so we can cache more of them safely.
141     private LoadingCache<String, CLDRFile> cldrFileUnresolvedCache =
142             CacheBuilder.newBuilder()
143                     .maximumSize(1000)
144                     .build(
145                             new CacheLoader<String, CLDRFile>() {
146                                 @Override
147                                 public CLDRFile load(String locale) {
148                                     return getFullCldrFactory().make(locale, false);
149                                 }
150                             });
151     private TestLog testLog = null;
152 
153     // base level
setTestLog(TestLog log)154     public TestLog setTestLog(TestLog log) {
155         testLog = log;
156         return log;
157     }
158 
159     // for calling "run"
setTestLog(TestFmwk log)160     public TestFmwk setTestLog(TestFmwk log) {
161         testLog = log;
162         return log;
163     }
164 
logln(String msg)165     protected void logln(String msg) {
166         if (testLog != null) {
167             testLog.logln(msg);
168         } else {
169             System.out.println(msg);
170             System.out.flush();
171         }
172     }
173 
174     private static final class SupplementalDataInfoHelper {
175         static final SupplementalDataInfo SINGLETON =
176                 SupplementalDataInfo.getInstance(CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY);
177     }
178 
getSupplementalDataInfo()179     public SupplementalDataInfo getSupplementalDataInfo() {
180         // Note: overridden in subclass.
181         return SupplementalDataInfoHelper.SINGLETON;
182     }
183 
184     private static final class CoverageInfoHelper {
185         static final CoverageInfo SINGLETON =
186                 new CoverageInfo(getInstance().getSupplementalDataInfo());
187     }
188 
getCoverageInfo()189     public final CoverageInfo getCoverageInfo() {
190         return CoverageInfoHelper.SINGLETON;
191     }
192 
193     private static final class CldrFactoryHelper {
194         static final Factory SINGLETON = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
195     }
196 
getCldrFactory()197     public final Factory getCldrFactory() {
198         return CldrFactoryHelper.SINGLETON;
199     }
200 
201     private static final class ExemplarsFactoryHelper {
202         static final Factory SINGLETON = Factory.make(CLDRPaths.EXEMPLARS_DIRECTORY, ".*");
203     }
204 
getExemplarsFactory()205     public final Factory getExemplarsFactory() {
206         return ExemplarsFactoryHelper.SINGLETON;
207     }
208 
209     private static final class CollationFactoryHelper {
210         static final Factory SINGLETON =
211                 Factory.make(CLDRPaths.COLLATION_DIRECTORY, ".*")
212                         .setIgnoreExplicitParentLocale(true);
213         static final File[] COLLATION_PATHS = {
214             new File(CLDRPaths.COLLATION_DIRECTORY),
215             SKIP_SEED ? null : new File(CLDRPaths.SEED_COLLATION_DIRECTORY)
216         };
217         static final Factory ALL_SINGLETON =
218                 SimpleFactory.make(COLLATION_PATHS, ".*").setIgnoreExplicitParentLocale(true);
219     }
220 
getCollationFactory()221     public final Factory getCollationFactory() {
222         return CollationFactoryHelper.SINGLETON;
223     }
224 
225     /** Factory for all collation files, not just common */
getAllCollationFactory()226     public final Factory getAllCollationFactory() {
227         CollationFactoryHelper.ALL_SINGLETON.setIgnoreExplicitParentLocale(true);
228         return CollationFactoryHelper.ALL_SINGLETON;
229     }
230 
231     private static final class RBNFFactoryHelper {
232         static final Factory SINGLETON = Factory.make(CLDRPaths.RBNF_DIRECTORY, ".*");
233     }
234 
getRBNFFactory()235     public final Factory getRBNFFactory() {
236         return RBNFFactoryHelper.SINGLETON;
237     }
238 
239     private static final class AnnotationsFactoryHelper {
240         private static final File[] paths = {
241             new File(CLDRPaths.ANNOTATIONS_DIRECTORY),
242             SKIP_SEED ? null : new File(CLDRPaths.SEED_ANNOTATIONS_DIRECTORY)
243         };
244         static final Factory SINGLETON = SimpleFactory.make(paths, ".*");
245     }
246 
getAnnotationsFactory()247     public Factory getAnnotationsFactory() {
248         return AnnotationsFactoryHelper.SINGLETON;
249     }
250 
251     private static final class SubdivisionsFactoryHelper {
252         static final Factory SINGLETON = Factory.make(CLDRPaths.SUBDIVISIONS_DIRECTORY, ".*");
253     }
254 
getSubdivisionFactory()255     public final Factory getSubdivisionFactory() {
256         return SubdivisionsFactoryHelper.SINGLETON;
257     }
258 
259     private static final class MainAndAnnotationsFactoryHelper {
260         private static final File[] paths = {
261             new File(CLDRPaths.MAIN_DIRECTORY), new File(CLDRPaths.ANNOTATIONS_DIRECTORY)
262         };
263         static final Factory SINGLETON = SimpleFactory.make(paths, ".*");
264     }
265 
getMainAndAnnotationsFactory()266     public final Factory getMainAndAnnotationsFactory() {
267         return MainAndAnnotationsFactoryHelper.SINGLETON;
268     }
269 
270     private static final class CommonSeedExemplarsFactoryHelper {
271         static final Factory SINGLETON =
272                 SimpleFactory.make(
273                         getInstance().addStandardSubdirectories(CLDR_DATA_DIRECTORIES), ".*");
274     }
275 
getCommonSeedExemplarsFactory()276     public final Factory getCommonSeedExemplarsFactory() {
277         return CommonSeedExemplarsFactoryHelper.SINGLETON;
278     }
279 
280     private static final class CommonAndSeedAndMainAndAnnotationsFactoryHelper {
281         private static final File[] paths = {
282             new File(CLDRPaths.MAIN_DIRECTORY),
283             new File(CLDRPaths.ANNOTATIONS_DIRECTORY),
284             SKIP_SEED ? null : new File(CLDRPaths.SEED_DIRECTORY),
285             SKIP_SEED ? null : new File(CLDRPaths.SEED_ANNOTATIONS_DIRECTORY)
286         };
287         static final Factory SINGLETON = SimpleFactory.make(paths, ".*");
288     }
289 
getCommonAndSeedAndMainAndAnnotationsFactory()290     public final Factory getCommonAndSeedAndMainAndAnnotationsFactory() {
291         return CommonAndSeedAndMainAndAnnotationsFactoryHelper.SINGLETON;
292     }
293 
294     private static final class FullCldrFactoryHelper {
295         private static final File[] paths = {
296             new File(CLDRPaths.MAIN_DIRECTORY),
297             SKIP_SEED ? null : new File(CLDRPaths.SEED_DIRECTORY)
298         };
299         static final Factory SINGLETON = SimpleFactory.make(paths, ".*");
300     }
301 
getFullCldrFactory()302     public final Factory getFullCldrFactory() {
303         return FullCldrFactoryHelper.SINGLETON;
304     }
305 
306     private static final class SupplementalFactoryHelper {
307         static final Factory SINGLETON =
308                 Factory.make(CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY, ".*");
309     }
310 
getSupplementalFactory()311     public final Factory getSupplementalFactory() {
312         return SupplementalFactoryHelper.SINGLETON;
313     }
314 
getEnglish()315     public CLDRFile getEnglish() {
316         return getCLDRFile("en", true);
317     }
318 
getCLDRFile(String locale, boolean resolved)319     public CLDRFile getCLDRFile(String locale, boolean resolved) {
320         return resolved
321                 ? cldrFileResolvedCache.getUnchecked(locale)
322                 : cldrFileUnresolvedCache.getUnchecked(locale);
323     }
324 
getRoot()325     public CLDRFile getRoot() {
326         return getCLDRFile(LocaleNames.ROOT, true);
327     }
328 
329     private static final class CollatorRootHelper {
330         static final RuleBasedCollator SINGLETON = make();
331 
make()332         private static final RuleBasedCollator make() {
333             RuleBasedCollator colRoot;
334 
335             CLDRFile root = getInstance().getCollationFactory().make(LocaleNames.ROOT, false);
336             String rules =
337                     root.getStringValue(
338                             "//ldml/collations/collation[@type=\"emoji\"][@visibility=\"external\"]/cr");
339             try {
340                 colRoot = new RuleBasedCollator(rules);
341             } catch (Exception e) {
342                 colRoot = (RuleBasedCollator) getInstance().getCollator();
343                 return colRoot;
344             }
345             colRoot.setStrength(Collator.IDENTICAL);
346             colRoot.setNumericCollation(true);
347             colRoot.freeze();
348             return colRoot;
349         }
350     }
351 
getCollatorRoot()352     public final Collator getCollatorRoot() {
353         return CollatorRootHelper.SINGLETON;
354     }
355 
356     @SuppressWarnings("unchecked")
getComparatorRoot()357     public final Comparator<String> getComparatorRoot() {
358         return (Comparator) (getCollatorRoot());
359     }
360 
361     private static final class CollatorHelper {
362         static final Collator EMOJI_COLLATOR = makeEmojiCollator();
363 
makeEmojiCollator()364         private static final Collator makeEmojiCollator() {
365             final RuleBasedCollator col =
366                     (RuleBasedCollator)
367                             Collator.getInstance(ULocale.forLanguageTag("en-u-co-emoji"));
368             col.setStrength(Collator.IDENTICAL);
369             col.setNumericCollation(true);
370             col.freeze();
371             return col;
372         }
373 
374         static final Collator ROOT_NUMERIC = makeRootNumeric();
375 
makeRootNumeric()376         private static final Collator makeRootNumeric() {
377             RuleBasedCollator _ROOT_COL = (RuleBasedCollator) Collator.getInstance(ULocale.ENGLISH);
378             _ROOT_COL.setNumericCollation(true);
379             _ROOT_COL.freeze();
380             return _ROOT_COL;
381         }
382     }
383 
getCollator()384     public Collator getCollator() {
385         return CollatorHelper.EMOJI_COLLATOR;
386     }
387 
getRootNumeric()388     public Collator getRootNumeric() {
389         return CollatorHelper.ROOT_NUMERIC;
390     }
391 
getPhase()392     public synchronized Phase getPhase() {
393         if (phase == null) {
394             if (getEnvironment() == Environment.UNITTEST) {
395                 phase = Phase.BUILD;
396             } else {
397                 phase = Phase.SUBMISSION;
398             }
399         }
400         return phase;
401     }
402 
403     /**
404      * @returns the phase for extended submission locales. Defaults to same as main phase. {@link
405      *     SubmissionLocales#isOpenForExtendedSubmission}
406      */
getExtendedPhase()407     public Phase getExtendedPhase() {
408         // by default, same as main phase.
409         return getPhase();
410     }
411 
412     @Override
getProperty(String key, String d)413     public String getProperty(String key, String d) {
414         String result = getProperty(key);
415         if (result == null) return d;
416         return result;
417     }
418 
419     private Set<String> shown = new HashSet<>();
420 
421     private Map<String, String> localSet = null;
422 
423     @Override
get(Object key)424     public String get(Object key) {
425         return getProperty(key.toString());
426     }
427 
428     @Override
getProperty(String key)429     public String getProperty(String key) {
430         String result = null;
431         if (localSet != null) {
432             result = localSet.get(key);
433         }
434         if (result == null) {
435             result = System.getProperty(key);
436         }
437         if (result == null) {
438             result = System.getProperty(key.toUpperCase(Locale.ENGLISH));
439         }
440         if (result == null) {
441             result = System.getProperty(key.toLowerCase(Locale.ENGLISH));
442         }
443         if (result == null) {
444             result = System.getenv(key);
445         }
446         if (DEBUG && !shown.contains(key)) {
447             logln("-D" + key + "=" + result);
448             shown.add(key);
449         }
450         return result;
451     }
452 
453     private Environment curEnvironment = null;
454 
getEnvironment()455     public Environment getEnvironment() {
456         if (curEnvironment == null) {
457             String envString = getProperty("CLDR_ENVIRONMENT");
458             if (envString != null) {
459                 curEnvironment = Environment.valueOf(envString.trim());
460             }
461             if (curEnvironment == null) {
462                 curEnvironment = getDefaultEnvironment();
463             }
464         }
465         return curEnvironment;
466     }
467 
468     /**
469      * If no environment is defined, what is the default?
470      *
471      * @return
472      */
getDefaultEnvironment()473     protected Environment getDefaultEnvironment() {
474         return Environment.LOCAL;
475     }
476 
setEnvironment(Environment environment)477     public void setEnvironment(Environment environment) {
478         curEnvironment = environment;
479     }
480 
481     /**
482      * For test use only. Will throw an exception in non test environments.
483      *
484      * @param k
485      * @param v
486      * @return
487      */
488     @Override
setProperty(String k, String v)489     public Object setProperty(String k, String v) {
490         if (getEnvironment() != Environment.UNITTEST) {
491             throw new InternalError("setProperty() only valid in UNITTEST Environment.");
492         }
493         if (localSet == null) {
494             localSet = new ConcurrentHashMap<>();
495         }
496         shown.remove(k); // show it again with -D
497         return localSet.put(k, v);
498     }
499 
500     @Override
put(Object k, Object v)501     public Object put(Object k, Object v) {
502         return setProperty(k.toString(), v.toString());
503     }
504 
505     /**
506      * Return true if the value indicates 'true'
507      *
508      * @param k key
509      * @param defVal default value
510      * @return
511      */
getProperty(String k, boolean defVal)512     public boolean getProperty(String k, boolean defVal) {
513         String val = getProperty(k, defVal ? "true" : null);
514         if (val == null) {
515             return false;
516         } else {
517             val = val.trim().toLowerCase();
518             return (val.equals("true") || val.equals("t") || val.equals("yes") || val.equals("y"));
519         }
520     }
521 
522     /**
523      * Return a numeric property
524      *
525      * @param k key
526      * @param defVal default value
527      * @return
528      */
getProperty(String k, int defVal)529     public int getProperty(String k, int defVal) {
530         String val = getProperty(k, Integer.toString(defVal));
531         if (val == null) {
532             return defVal;
533         } else {
534             try {
535                 return Integer.parseInt(val);
536             } catch (NumberFormatException nfe) {
537                 return defVal;
538             }
539         }
540     }
541 
542     private static class FileWrapper {
543         private File cldrDir = null;
544 
FileWrapper()545         private FileWrapper() {
546             String dir = getInstance().getProperty(CldrUtility.DIR_KEY, null);
547             if (dir != null) {
548                 cldrDir = new File(dir);
549             } else {
550                 cldrDir = null;
551             }
552         }
553 
getCldrDir()554         public File getCldrDir() {
555             return this.cldrDir;
556         }
557         // singleton
558         private static FileWrapper fileWrapperInstance = new FileWrapper();
559 
getFileWrapperInstance()560         public static FileWrapper getFileWrapperInstance() {
561             return fileWrapperInstance;
562         }
563     }
564 
getCldrBaseDirectory()565     public File getCldrBaseDirectory() {
566         return FileWrapper.getFileWrapperInstance().getCldrDir();
567     }
568 
569     /**
570      * Get all CLDR XML files in the CLDR base directory.
571      *
572      * @return
573      */
getAllCLDRFilesEndingWith(final String suffix)574     public Set<File> getAllCLDRFilesEndingWith(final String suffix) {
575         FilenameFilter filter =
576                 new FilenameFilter() {
577                     @Override
578                     public boolean accept(File dir, String name) {
579                         return name.endsWith(suffix)
580                                 && !isJunkFile(name); // skip junk and backup files
581                     }
582                 };
583         final File dir = getCldrBaseDirectory();
584         Set<File> list;
585         list = getCLDRFilesMatching(filter, dir);
586         return list;
587     }
588 
589     /**
590      * Return all CLDR data files matching this filter
591      *
592      * @param filter matching filter
593      * @param baseDir base directory, see {@link #getCldrBaseDirectory()}
594      * @return set of files
595      */
getCLDRFilesMatching(FilenameFilter filter, final File baseDir)596     public Set<File> getCLDRFilesMatching(FilenameFilter filter, final File baseDir) {
597         Set<File> list;
598         list = new LinkedHashSet<>();
599         for (String subdir : getCLDRDataDirectories()) {
600             getFilesRecursively(new File(baseDir, subdir), filter, list);
601         }
602         return list;
603     }
604 
605     /** TODO: better place for these constants? */
606     private static final String COMMON_DIR = "common";
607     /** TODO: better place for these constants? */
608     private static final String EXEMPLARS_DIR = "exemplars";
609     /** TODO: better place for these constants? */
610     private static final String SEED_DIR = "seed";
611     /** TODO: better place for these constants? */
612     private static final String KEYBOARDS_DIR = "keyboards";
613 
614     private static final String MAIN_DIR = "main";
615     private static final String ANNOTATIONS_DIR = "annotations";
616     private static final String SUBDIVISIONS_DIR = "subdivisions";
617 
618     /** TODO: better place for these constants? */
619     private static final String CLDR_DATA_DIRECTORIES[] = {
620         COMMON_DIR, SEED_DIR, KEYBOARDS_DIR, EXEMPLARS_DIR
621     };
622 
623     private static final ImmutableSet<String> STANDARD_SUBDIRS =
624             ImmutableSet.of(MAIN_DIR, ANNOTATIONS_DIR, SUBDIVISIONS_DIR);
625 
626     /**
627      * Get a list of CLDR directories containing actual data
628      *
629      * @return an iterable containing the names of all CLDR data subdirectories
630      */
getCLDRDataDirectories()631     public static Iterable<String> getCLDRDataDirectories() {
632         return Arrays.asList(CLDR_DATA_DIRECTORIES);
633     }
634 
635     /**
636      * Given comma separated list "common" or "common,main" return a list of actual files. Adds
637      * subdirectories in STANDARD_SUBDIRS as necessary.
638      */
getCLDRDataDirectories(String list)639     public File[] getCLDRDataDirectories(String list) {
640         final File dir = getCldrBaseDirectory();
641         String stubs[] = list.split(",");
642         File[] ret = new File[stubs.length];
643         for (int i = 0; i < stubs.length; i++) {
644             ret[i] = new File(dir, stubs[i]);
645         }
646         return ret;
647     }
648 
649     /**
650      * Add subdirectories to file list as needed, from STANDARD_SUBDIRS.
651      *
652      * <ul>
653      *   <li>map "common","seed" -> "common/main", "seed/main"
654      *   <li>but common/main -> common/main
655      * </ul>
656      */
addStandardSubdirectories(String... base)657     public File[] addStandardSubdirectories(String... base) {
658         return addStandardSubdirectories(fileArrayFromStringArray(getCldrBaseDirectory(), base));
659     }
660 
addStandardSubdirectories(File... base)661     public File[] addStandardSubdirectories(File... base) {
662         List<File> ret = new ArrayList<>();
663         // File[] ret = new File[base.length * 2];
664         for (int i = 0; i < base.length; i++) {
665             File baseFile = base[i];
666             String name = baseFile.getName();
667             if (STANDARD_SUBDIRS.contains(name)) {
668                 ret.add(baseFile);
669             } else {
670                 for (String sub : STANDARD_SUBDIRS) {
671                     addIfExists(ret, baseFile, sub);
672                 }
673             }
674         }
675         return ret.toArray(new File[ret.size()]);
676     }
677 
fileArrayFromStringArray(File dir, String... subdirNames)678     public static File[] fileArrayFromStringArray(File dir, String... subdirNames) {
679         File[] fileList = new File[subdirNames.length];
680         int i = 0;
681         for (String item : subdirNames) {
682             fileList[i++] = new File(dir, item);
683         }
684         return fileList;
685     }
686 
addIfExists(List<File> ret, File baseFile, String sub)687     private static void addIfExists(List<File> ret, File baseFile, String sub) {
688         File file = new File(baseFile, sub);
689         if (file.exists()) {
690             ret.add(file);
691         }
692     }
693 
694     /**
695      * Utility function. Recursively add to a list of files. Skips ".svn" and junk directories.
696      *
697      * @param directory base directory
698      * @param filter filter to restrict files added
699      * @param toAddTo set to add to
700      * @return returns toAddTo.
701      */
getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo)702     public Set<File> getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo) {
703         File files[] = directory.listFiles();
704         if (files != null) {
705             for (File subfile : files) {
706                 if (subfile.isDirectory()) {
707                     if (!isJunkFile(subfile.getName())) {
708                         getFilesRecursively(subfile, filter, toAddTo);
709                     }
710                 } else if (filter.accept(directory, subfile.getName())) {
711                     toAddTo.add(subfile);
712                 }
713             }
714         }
715         return toAddTo;
716     }
717 
718     /**
719      * Is the filename junk? (subversion, backup, etc)
720      *
721      * @param name
722      * @return
723      */
isJunkFile(String name)724     public static final boolean isJunkFile(String name) {
725         return name.startsWith(".")
726                 || (name.startsWith("#")); // Skip:  .svn, .BACKUP,  #backup# files.
727     }
728 
729     /**
730      * Get the value of the debug setting for the calling class; assuming that no debugging is
731      * wanted if the property value cannot be found
732      *
733      * @param callingClass
734      * @return
735      * @see {@link #getDebugSettingsFor(Class, boolean)}
736      */
getDebugSettingsFor(Class<?> callingClass)737     public boolean getDebugSettingsFor(Class<?> callingClass) {
738         return getDebugSettingsFor(callingClass, false);
739     }
740 
741     /**
742      * Get the debug settings (whether debugging is enabled for the calling class; This will look
743      * for a property corresponding to the canonical classname +".debug"; if that property cannot be
744      * found, the default value will be returned.
745      *
746      * @param callingClass
747      * @param defaultValue
748      * @return
749      */
getDebugSettingsFor(Class<?> callingClass, boolean defaultValue)750     public boolean getDebugSettingsFor(Class<?> callingClass, boolean defaultValue) {
751         // avoid NPE
752         if (callingClass == null) {
753             return defaultValue;
754         }
755         return getProperty(callingClass.getCanonicalName() + ".debug", defaultValue);
756     }
757 
758     /**
759      * Get the URL generator for "general purpose" (non chart) use.
760      *
761      * @return
762      */
urls()763     public CLDRURLS urls() {
764         if (urls == null) {
765             synchronized (this) {
766                 urls = internalGetUrls();
767             }
768         }
769         return urls;
770     }
771 
772     /**
773      * Get the URL generator for "absolute" (chart, email) use. By default, this is the same as
774      * urls.
775      */
absoluteUrls()776     public CLDRURLS absoluteUrls() {
777         if (absoluteUrls == null) {
778             synchronized (this) {
779                 absoluteUrls = internalGetAbsoluteUrls();
780             }
781         }
782         return absoluteUrls;
783     }
784 
785     /** Probably would not need to override this. */
internalGetAbsoluteUrls()786     protected CLDRURLS internalGetAbsoluteUrls() {
787         return new StaticCLDRURLS(
788                 this.getProperty(CLDRURLS.CLDR_SURVEY_BASE, CLDRURLS.DEFAULT_BASE));
789     }
790 
791     /** Override this to provide a different URL source for non-absolute URLs. */
internalGetUrls()792     protected CLDRURLS internalGetUrls() {
793         return absoluteUrls();
794     }
795 
796     private CLDRURLS urls = null;
797     private CLDRURLS absoluteUrls = null;
798 
isCldrVersionBefore(int... version)799     public boolean isCldrVersionBefore(int... version) {
800         return getEnglish().getDtdVersionInfo().compareTo(getVersion(version)) < 0;
801     }
802 
getVersion(int... versionInput)803     public static VersionInfo getVersion(int... versionInput) {
804         int[] version = new int[4];
805         for (int i = 0; i < versionInput.length; ++i) {
806             version[i] = versionInput[i];
807         }
808         return VersionInfo.getInstance(version[0], version[1], version[2], version[3]);
809     }
810 }
811