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