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