xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/CLDRConfig.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.util;
2 
3 import com.google.common.cache.CacheBuilder;
4 import com.google.common.cache.CacheLoader;
5 import com.google.common.cache.LoadingCache;
6 import com.google.common.collect.ImmutableSet;
7 import com.ibm.icu.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     @Override
getProperty(String key, String d)404     public String getProperty(String key, String d) {
405         String result = getProperty(key);
406         if (result == null) return d;
407         return result;
408     }
409 
410     private Set<String> shown = new HashSet<>();
411 
412     private Map<String, String> localSet = null;
413 
414     @Override
get(Object key)415     public String get(Object key) {
416         return getProperty(key.toString());
417     }
418 
419     @Override
getProperty(String key)420     public String getProperty(String key) {
421         String result = null;
422         if (localSet != null) {
423             result = localSet.get(key);
424         }
425         if (result == null) {
426             result = System.getProperty(key);
427         }
428         if (result == null) {
429             result = System.getProperty(key.toUpperCase(Locale.ENGLISH));
430         }
431         if (result == null) {
432             result = System.getProperty(key.toLowerCase(Locale.ENGLISH));
433         }
434         if (result == null) {
435             result = System.getenv(key);
436         }
437         if (DEBUG && !shown.contains(key)) {
438             logln("-D" + key + "=" + result);
439             shown.add(key);
440         }
441         return result;
442     }
443 
444     private Environment curEnvironment = null;
445 
getEnvironment()446     public Environment getEnvironment() {
447         if (curEnvironment == null) {
448             String envString = getProperty("CLDR_ENVIRONMENT");
449             if (envString != null) {
450                 curEnvironment = Environment.valueOf(envString.trim());
451             }
452             if (curEnvironment == null) {
453                 curEnvironment = getDefaultEnvironment();
454             }
455         }
456         return curEnvironment;
457     }
458 
459     /**
460      * If no environment is defined, what is the default?
461      *
462      * @return
463      */
getDefaultEnvironment()464     protected Environment getDefaultEnvironment() {
465         return Environment.LOCAL;
466     }
467 
setEnvironment(Environment environment)468     public void setEnvironment(Environment environment) {
469         curEnvironment = environment;
470     }
471 
472     /**
473      * For test use only. Will throw an exception in non test environments.
474      *
475      * @param k
476      * @param v
477      * @return
478      */
479     @Override
setProperty(String k, String v)480     public Object setProperty(String k, String v) {
481         if (getEnvironment() != Environment.UNITTEST) {
482             throw new InternalError("setProperty() only valid in UNITTEST Environment.");
483         }
484         if (localSet == null) {
485             localSet = new ConcurrentHashMap<>();
486         }
487         shown.remove(k); // show it again with -D
488         return localSet.put(k, v);
489     }
490 
491     @Override
put(Object k, Object v)492     public Object put(Object k, Object v) {
493         return setProperty(k.toString(), v.toString());
494     }
495 
496     /**
497      * Return true if the value indicates 'true'
498      *
499      * @param k key
500      * @param defVal default value
501      * @return
502      */
getProperty(String k, boolean defVal)503     public boolean getProperty(String k, boolean defVal) {
504         String val = getProperty(k, defVal ? "true" : null);
505         if (val == null) {
506             return false;
507         } else {
508             val = val.trim().toLowerCase();
509             return (val.equals("true") || val.equals("t") || val.equals("yes") || val.equals("y"));
510         }
511     }
512 
513     /**
514      * Return a numeric property
515      *
516      * @param k key
517      * @param defVal default value
518      * @return
519      */
getProperty(String k, int defVal)520     public int getProperty(String k, int defVal) {
521         String val = getProperty(k, Integer.toString(defVal));
522         if (val == null) {
523             return defVal;
524         } else {
525             try {
526                 return Integer.parseInt(val);
527             } catch (NumberFormatException nfe) {
528                 return defVal;
529             }
530         }
531     }
532 
533     private static class FileWrapper {
534         private File cldrDir = null;
535 
FileWrapper()536         private FileWrapper() {
537             String dir = getInstance().getProperty(CldrUtility.DIR_KEY, null);
538             if (dir != null) {
539                 cldrDir = new File(dir);
540             } else {
541                 cldrDir = null;
542             }
543         }
544 
getCldrDir()545         public File getCldrDir() {
546             return this.cldrDir;
547         }
548         // singleton
549         private static FileWrapper fileWrapperInstance = new FileWrapper();
550 
getFileWrapperInstance()551         public static FileWrapper getFileWrapperInstance() {
552             return fileWrapperInstance;
553         }
554     }
555 
getCldrBaseDirectory()556     public File getCldrBaseDirectory() {
557         return FileWrapper.getFileWrapperInstance().getCldrDir();
558     }
559 
560     /**
561      * Get all CLDR XML files in the CLDR base directory.
562      *
563      * @return
564      */
getAllCLDRFilesEndingWith(final String suffix)565     public Set<File> getAllCLDRFilesEndingWith(final String suffix) {
566         FilenameFilter filter =
567                 new FilenameFilter() {
568                     @Override
569                     public boolean accept(File dir, String name) {
570                         return name.endsWith(suffix)
571                                 && !isJunkFile(name); // skip junk and backup files
572                     }
573                 };
574         final File dir = getCldrBaseDirectory();
575         Set<File> list;
576         list = getCLDRFilesMatching(filter, dir);
577         return list;
578     }
579 
580     /**
581      * Return all CLDR data files matching this filter
582      *
583      * @param filter matching filter
584      * @param baseDir base directory, see {@link #getCldrBaseDirectory()}
585      * @return set of files
586      */
getCLDRFilesMatching(FilenameFilter filter, final File baseDir)587     public Set<File> getCLDRFilesMatching(FilenameFilter filter, final File baseDir) {
588         Set<File> list;
589         list = new LinkedHashSet<>();
590         for (String subdir : getCLDRDataDirectories()) {
591             getFilesRecursively(new File(baseDir, subdir), filter, list);
592         }
593         return list;
594     }
595 
596     /** TODO: better place for these constants? */
597     private static final String COMMON_DIR = "common";
598     /** TODO: better place for these constants? */
599     private static final String EXEMPLARS_DIR = "exemplars";
600     /** TODO: better place for these constants? */
601     private static final String SEED_DIR = "seed";
602     /** TODO: better place for these constants? */
603     private static final String KEYBOARDS_DIR = "keyboards";
604 
605     private static final String MAIN_DIR = "main";
606     private static final String ANNOTATIONS_DIR = "annotations";
607     private static final String SUBDIVISIONS_DIR = "subdivisions";
608 
609     /** TODO: better place for these constants? */
610     private static final String CLDR_DATA_DIRECTORIES[] = {
611         COMMON_DIR, SEED_DIR, KEYBOARDS_DIR, EXEMPLARS_DIR
612     };
613 
614     private static final ImmutableSet<String> STANDARD_SUBDIRS =
615             ImmutableSet.of(MAIN_DIR, ANNOTATIONS_DIR, SUBDIVISIONS_DIR);
616 
617     /**
618      * Get a list of CLDR directories containing actual data
619      *
620      * @return an iterable containing the names of all CLDR data subdirectories
621      */
getCLDRDataDirectories()622     public static Iterable<String> getCLDRDataDirectories() {
623         return Arrays.asList(CLDR_DATA_DIRECTORIES);
624     }
625 
626     /**
627      * Given comma separated list "common" or "common,main" return a list of actual files. Adds
628      * subdirectories in STANDARD_SUBDIRS as necessary.
629      */
getCLDRDataDirectories(String list)630     public File[] getCLDRDataDirectories(String list) {
631         final File dir = getCldrBaseDirectory();
632         String stubs[] = list.split(",");
633         File[] ret = new File[stubs.length];
634         for (int i = 0; i < stubs.length; i++) {
635             ret[i] = new File(dir, stubs[i]);
636         }
637         return ret;
638     }
639 
640     /**
641      * Add subdirectories to file list as needed, from STANDARD_SUBDIRS.
642      *
643      * <ul>
644      *   <li>map "common","seed" -> "common/main", "seed/main"
645      *   <li>but common/main -> common/main
646      * </ul>
647      */
addStandardSubdirectories(String... base)648     public File[] addStandardSubdirectories(String... base) {
649         return addStandardSubdirectories(fileArrayFromStringArray(getCldrBaseDirectory(), base));
650     }
651 
addStandardSubdirectories(File... base)652     public File[] addStandardSubdirectories(File... base) {
653         List<File> ret = new ArrayList<>();
654         // File[] ret = new File[base.length * 2];
655         for (int i = 0; i < base.length; i++) {
656             File baseFile = base[i];
657             String name = baseFile.getName();
658             if (STANDARD_SUBDIRS.contains(name)) {
659                 ret.add(baseFile);
660             } else {
661                 for (String sub : STANDARD_SUBDIRS) {
662                     addIfExists(ret, baseFile, sub);
663                 }
664             }
665         }
666         return ret.toArray(new File[ret.size()]);
667     }
668 
fileArrayFromStringArray(File dir, String... subdirNames)669     public static File[] fileArrayFromStringArray(File dir, String... subdirNames) {
670         File[] fileList = new File[subdirNames.length];
671         int i = 0;
672         for (String item : subdirNames) {
673             fileList[i++] = new File(dir, item);
674         }
675         return fileList;
676     }
677 
addIfExists(List<File> ret, File baseFile, String sub)678     private static void addIfExists(List<File> ret, File baseFile, String sub) {
679         File file = new File(baseFile, sub);
680         if (file.exists()) {
681             ret.add(file);
682         }
683     }
684 
685     /**
686      * Utility function. Recursively add to a list of files. Skips ".svn" and junk directories.
687      *
688      * @param directory base directory
689      * @param filter filter to restrict files added
690      * @param toAddTo set to add to
691      * @return returns toAddTo.
692      */
getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo)693     public Set<File> getFilesRecursively(File directory, FilenameFilter filter, Set<File> toAddTo) {
694         File files[] = directory.listFiles();
695         if (files != null) {
696             for (File subfile : files) {
697                 if (subfile.isDirectory()) {
698                     if (!isJunkFile(subfile.getName())) {
699                         getFilesRecursively(subfile, filter, toAddTo);
700                     }
701                 } else if (filter.accept(directory, subfile.getName())) {
702                     toAddTo.add(subfile);
703                 }
704             }
705         }
706         return toAddTo;
707     }
708 
709     /**
710      * Is the filename junk? (subversion, backup, etc)
711      *
712      * @param name
713      * @return
714      */
isJunkFile(String name)715     public static final boolean isJunkFile(String name) {
716         return name.startsWith(".")
717                 || (name.startsWith("#")); // Skip:  .svn, .BACKUP,  #backup# files.
718     }
719 
720     /**
721      * Get the value of the debug setting for the calling class; assuming that no debugging is
722      * wanted if the property value cannot be found
723      *
724      * @param callingClass
725      * @return
726      * @see {@link #getDebugSettingsFor(Class, boolean)}
727      */
getDebugSettingsFor(Class<?> callingClass)728     public boolean getDebugSettingsFor(Class<?> callingClass) {
729         return getDebugSettingsFor(callingClass, false);
730     }
731 
732     /**
733      * Get the debug settings (whether debugging is enabled for the calling class; This will look
734      * for a property corresponding to the canonical classname +".debug"; if that property cannot be
735      * found, the default value will be returned.
736      *
737      * @param callingClass
738      * @param defaultValue
739      * @return
740      */
getDebugSettingsFor(Class<?> callingClass, boolean defaultValue)741     public boolean getDebugSettingsFor(Class<?> callingClass, boolean defaultValue) {
742         // avoid NPE
743         if (callingClass == null) {
744             return defaultValue;
745         }
746         return getProperty(callingClass.getCanonicalName() + ".debug", defaultValue);
747     }
748 
749     /**
750      * Get the URL generator for "general purpose" (non chart) use.
751      *
752      * @return
753      */
urls()754     public CLDRURLS urls() {
755         if (urls == null) {
756             synchronized (this) {
757                 urls = internalGetUrls();
758             }
759         }
760         return urls;
761     }
762 
763     /**
764      * Get the URL generator for "absolute" (chart, email) use. By default, this is the same as
765      * urls.
766      */
absoluteUrls()767     public CLDRURLS absoluteUrls() {
768         if (absoluteUrls == null) {
769             synchronized (this) {
770                 absoluteUrls = internalGetAbsoluteUrls();
771             }
772         }
773         return absoluteUrls;
774     }
775 
776     /** Probably would not need to override this. */
internalGetAbsoluteUrls()777     protected CLDRURLS internalGetAbsoluteUrls() {
778         return new StaticCLDRURLS(
779                 this.getProperty(CLDRURLS.CLDR_SURVEY_BASE, CLDRURLS.DEFAULT_BASE));
780     }
781 
782     /** Override this to provide a different URL source for non-absolute URLs. */
internalGetUrls()783     protected CLDRURLS internalGetUrls() {
784         return absoluteUrls();
785     }
786 
787     private CLDRURLS urls = null;
788     private CLDRURLS absoluteUrls = null;
789 
isCldrVersionBefore(int... version)790     public boolean isCldrVersionBefore(int... version) {
791         return getEnglish().getDtdVersionInfo().compareTo(getVersion(version)) < 0;
792     }
793 
getVersion(int... versionInput)794     public static VersionInfo getVersion(int... versionInput) {
795         int[] version = new int[4];
796         for (int i = 0; i < versionInput.length; ++i) {
797             version[i] = versionInput[i];
798         }
799         return VersionInfo.getInstance(version[0], version[1], version[2], version[3]);
800     }
801 }
802